/// <summary>Remove an existing node from its parents</summary>
		/// <returns>True if the parent node was found, otherwise false</returns>
		private async Task<bool> RemoveFromParent(IFdbTransaction tr, IFdbTuple path)
		{
			Contract.Requires(tr != null && path != null);

			var parent = await FindAsync(tr, path.Substring(0, path.Count - 1)).ConfigureAwait(false);
			if (parent.Exists)
			{
				if (FdbDirectoryLayer.AnnotateTransactions) tr.Annotate("Removing path {0} from its parent folder at {1}", path, parent.Subspace.Key);
				tr.Clear(GetSubDirKey(parent.Subspace, path.Get<string>(-1)));
				return true;
			}
			return false;
		}
		/// <summary>Finds a node subspace, given its path, by walking the tree from the root.</summary>
		/// <returns>Node if it was found, or null</returns>
		private async Task<Node> FindAsync(IFdbReadOnlyTransaction tr, IFdbTuple path)
		{
			Contract.Requires(tr != null && path != null);

			// look for the node by traversing from the root down. Stop when crossing a partition...

			var n = this.RootNode;
			int i = 0;
			Slice layer = Slice.Nil;
			while (i < path.Count)
			{
				if (FdbDirectoryLayer.AnnotateTransactions) tr.Annotate("Looking for child {0} under node {1}...", path.Get<string>(i), n.Key);
				n = NodeWithPrefix(await tr.GetAsync(GetSubDirKey(n, path.Get<string>(i))).ConfigureAwait(false));
				if (n == null)
				{
					return new Node(null, path.Substring(0, i + 1), path, Slice.Empty);
				}

				if (FdbDirectoryLayer.AnnotateTransactions) tr.Annotate("Reading Layer value for subfolder {0} found at {1}", path, n.Key);
				layer = await tr.GetAsync(n.Pack(LayerSuffix)).ConfigureAwait(false);
				if (layer == FdbDirectoryPartition.LayerId)
				{ // stop when reaching a partition
					return new Node(n, path.Substring(0, i + 1), path, FdbDirectoryPartition.LayerId);
				}

				++i;
			}
			return new Node(n, path, path, layer);
		}
		/// <summary>Remove an existing node from its parents</summary>
		/// <returns>True if the parent node was found, otherwise false</returns>
		private async Task<bool> RemoveFromParent(IFdbTransaction tr, IFdbTuple path)
		{
			Contract.Requires(tr != null && path != null);

			var parent = await FindAsync(tr, path.Substring(0, path.Count - 1)).ConfigureAwait(false);
			if (parent.Exists)
			{
				tr.Clear(GetSubDirKey(parent.Subspace, path.Get<string>(-1)));
				return true;
			}
			return false;
		}
		internal async Task<FdbDirectorySubspace> MoveInternalAsync(IFdbTransaction trans, IFdbTuple oldPath, IFdbTuple newPath, bool throwOnError)
		{
			Contract.Requires(trans != null && oldPath != null && newPath != null);

			if (oldPath.Count == 0)
			{
				throw new InvalidOperationException("The root directory may not be moved.");
			}
			if (newPath.Count == 0)
			{
				throw new InvalidOperationException("The root directory cannot be overwritten.");
			}
			if (newPath.StartsWith(oldPath))
			{
				throw new InvalidOperationException(string.Format("The destination directory({0}) cannot be a subdirectory of the source directory({1}).", newPath, oldPath));
			}

			await CheckWriteVersionAsync(trans).ConfigureAwait(false);

			var oldNode = await FindAsync(trans, oldPath).ConfigureAwait(false);
			if (!oldNode.Exists)
			{
				if (throwOnError) throw new InvalidOperationException(string.Format("The source directory '{0}' does not exist.", oldPath));
				return null;
			}

			var newNode = await FindAsync(trans, newPath).ConfigureAwait(false);

			// we have already checked that old and new are under this partition path, but one of them (or both?) could be under a sub-partition..
			if (oldNode.IsInPartition(false) || newNode.IsInPartition(false))
			{
				if (!oldNode.IsInPartition(false) || !newNode.IsInPartition(false) || !FdbTuple.Equals(oldNode.Path, newNode.Path))
				{
					throw new InvalidOperationException("Cannot move between partitions.");
				}
				// both nodes are in the same sub-partition, delegate to it
				return await GetPartitionForNode(newNode).DirectoryLayer.MoveInternalAsync(trans, oldNode.PartitionSubPath, newNode.PartitionSubPath, throwOnError).ConfigureAwait(false);
			}

			if (newNode.Exists)
			{
				if (throwOnError) throw new InvalidOperationException(string.Format("The destination directory '{0}' already exists. Remove it first.", newPath));
				return null;
			}

			var parentNode = await FindAsync(trans, newPath.Substring(0, newPath.Count - 1)).ConfigureAwait(false);
			if (!parentNode.Exists)
			{
				if (throwOnError) throw new InvalidOperationException(string.Format("The parent of the destination directory '{0}' does not exist. Create it first.", newPath));
				return null;
			}

			trans.Set(GetSubDirKey(parentNode.Subspace, newPath.Get<string>(-1)), this.NodeSubspace.UnpackSingle<Slice>(oldNode.Subspace.Key));
			await RemoveFromParent(trans, oldPath).ConfigureAwait(false);

			return ContentsOfNode(oldNode.Subspace, newPath, oldNode.Layer);
		}
		internal async Task<FdbDirectorySubspace> CreateOrOpenInternalAsync(IFdbReadOnlyTransaction readTrans, IFdbTransaction trans, IFdbTuple path, Slice layer, Slice prefix, bool allowCreate, bool allowOpen, bool throwOnError)
		{
			Contract.Requires(readTrans != null || trans != null, "Need at least one transaction");
			Contract.Requires(path != null, "Path must be specified");
			Contract.Requires(readTrans == null || trans == null || object.ReferenceEquals(readTrans, trans), "The write transaction should be the same as the read transaction");

			if (path.Count == 0)
			{ // Root directory contains node metadata and so may not be opened.
				throw new InvalidOperationException("The root directory may not be opened.");
			}

			// to open an existing directory, we only need the read transaction
			// if none was specified, we can use the writeable transaction
			if (readTrans == null) readTrans = trans;

			await CheckReadVersionAsync(readTrans).ConfigureAwait(false);

			if (prefix.HasValue && this.Path.Count > 0)
				throw new InvalidOperationException("Cannot specify a prefix in a partition.");

			var existingNode = await FindAsync(readTrans, path).ConfigureAwait(false);

			if (existingNode.Exists)
			{
				if (existingNode.IsInPartition(false))
				{
					var subpath = existingNode.PartitionSubPath;
					var dl = GetPartitionForNode(existingNode).DirectoryLayer;
					return await dl.CreateOrOpenInternalAsync(readTrans, trans, subpath, layer, prefix, allowCreate, allowOpen, throwOnError).ConfigureAwait(false);
				}

				if (!allowOpen)
				{
					if (throwOnError) throw new InvalidOperationException(string.Format("The directory {0} already exists.", path));
					return null;
				}

				if (layer.IsPresent && layer != existingNode.Layer)
				{
					throw new InvalidOperationException(String.Format("The directory {0} was created with incompatible layer {1} instead of expected {2}.", path, layer.ToAsciiOrHexaString(), existingNode.Layer.ToAsciiOrHexaString()));
				}
				return ContentsOfNode(existingNode.Subspace, path, existingNode.Layer);
			}

			if (!allowCreate)
			{
				if (throwOnError) throw new InvalidOperationException(string.Format("The directory {0} does not exist.", path));
				return null;
			}

			// from there, we actually do need a wrtieable transaction
			if (trans == null) throw new InvalidOperationException("A writeable transaction is needed to create a new directory");

			await CheckWriteVersionAsync(trans).ConfigureAwait(false);

			if (prefix == null)
			{ // automatically allocate a new prefix inside the ContentSubspace
				long id = await this.Allocator.AllocateAsync(trans).ConfigureAwait(false);
				prefix = this.ContentSubspace.Pack(id);

				// ensure that there is no data already present under this prefix
				if (await trans.GetRange(FdbKeyRange.StartsWith(prefix)).AnyAsync().ConfigureAwait(false))
				{
					throw new InvalidOperationException(String.Format("The database has keys stored at the prefix chosen by the automatic prefix allocator: {0}", prefix.ToAsciiOrHexaString()));
				}

				// ensure that the prefix has not already been allocated
				if (!(await IsPrefixFree(trans.Snapshot, prefix).ConfigureAwait(false)))
				{
					throw new InvalidOperationException("The directory layer has manually allocated prefixes that conflict with the automatic prefix allocator.");
				}
			}
			else
			{
				// ensure that the prefix has not already been allocated
				if (!(await IsPrefixFree(trans, prefix).ConfigureAwait(false)))
				{
					throw new InvalidOperationException("The given prefix is already in use.");
				}
			}

			// we need to recursively create any missing parents
			FdbSubspace parentNode;
			if (path.Count > 1)
			{
				var parentSubspace = await CreateOrOpenInternalAsync(readTrans, trans, path.Substring(0, path.Count - 1), Slice.Nil, Slice.Nil, true, true, true).ConfigureAwait(false);
				parentNode = NodeWithPrefix(parentSubspace.Key);
			}
			else
			{
				parentNode = this.RootNode;
			}
			if (parentNode == null) throw new InvalidOperationException(string.Format("The parent directory of {0} doesn't exist.", path));

			// initialize the metadata for this new directory
			var node = NodeWithPrefix(prefix);
			trans.Set(GetSubDirKey(parentNode, path.Get<string>(-1)), prefix);
			SetLayer(trans, node, layer);

			return ContentsOfNode(node, path, layer);
		}
		/// <summary>Maps an absolute path to a relative path within this directory layer</summary>
		internal IFdbTuple ToRelativePath(IFdbTuple path)
		{
			if (path == null) throw new ArgumentNullException("path");

			if (!path.StartsWith(this.Location)) throw new InvalidOperationException("The path cannot be outside of this partition");
			return path.Substring(this.Location.Count);
		}