/// <summary>Create an allocator operating under a specific location</summary>
		/// <param name="subspace"></param>
		public FdbHighContentionAllocator(FdbSubspace subspace)
		{
			if (subspace == null) throw new ArgumentException("subspace");

			this.Subspace = subspace;
			this.Counters = subspace.Partition(COUNTERS);
			this.Recent = subspace.Partition(RECENT);
		}
Beispiel #2
0
		/// <summary>Create a new High Contention counter, using a specific value encoder.</summary>
		/// <param name="db">Database used by this layer</param>
		/// <param name="subspace">Subspace to be used for storing the counter</param>
		/// <param name="encoder">Encoder for the counter values</param>
		public FdbHighContentionCounter(IFdbDatabase db, FdbSubspace subspace, IValueEncoder<long> encoder)
		{
			if (db == null) throw new ArgumentNullException("db");
			if (subspace == null) throw new ArgumentNullException("subspace");
			if (encoder == null) throw new ArgumentNullException("encoder");

			this.Database = db;
			this.Subspace = subspace;
			this.Encoder = encoder;
		}
		/// <summary>
		/// Setup the initial state of the database
		/// </summary>
		public async Task Init(IFdbDatabase db, CancellationToken ct)
		{
			// open the folder where we will store everything
			this.Subspace = await db.Directory.CreateOrOpenAsync(new [] { "Benchmarks", "LeakTest" }, cancellationToken: ct);

			// clear all previous values
			await db.ClearRangeAsync(this.Subspace, ct);

			// insert all the classes
			await db.WriteAsync((tr) =>
			{
				tr.Set(this.Subspace.Concat(FdbKey.MinValue), Slice.FromString("BEGIN"));
				tr.Set(this.Subspace.Concat(FdbKey.MaxValue), Slice.FromString("END"));
			}, ct);
		}
		/// <summary>
		/// Setup the initial state of the database
		/// </summary>
		public async Task Init(IFdbDatabase db, CancellationToken ct)
		{
			// open the folder where we will store everything
			this.Subspace = await db.Directory.CreateOrOpenAsync(new [] { "Tutorials", "ClassScheduling" }, cancellationToken: ct);

			// clear all previous values
			await db.ClearRangeAsync(this.Subspace, ct);
			// insert all the classes
			await db.WriteAsync((tr) =>
			{
				foreach (var c in this.ClassNames)
				{
					tr.Set(ClassKey(c), Slice.FromAscii("100"));
				}
			}, ct);


		}
		public void Test_Subspace_With_Binary_Prefix()
		{
			var subspace = new FdbSubspace(Slice.Create(new byte[] { 42, 255, 0, 127 }));

			Assert.That(subspace.Key.ToString(), Is.EqualTo("*<FF><00><7F>"));
			Assert.That(subspace.Copy(), Is.Not.SameAs(subspace));
			Assert.That(subspace.Copy().Key, Is.EqualTo(subspace.Key));

			// concat(Slice) should append the slice to the binary prefix directly
			Assert.That(subspace.Concat(Slice.FromInt32(0x01020304)).ToString(), Is.EqualTo("*<FF><00><7F><04><03><02><01>"));
			Assert.That(subspace.Concat(Slice.FromAscii("hello")).ToString(), Is.EqualTo("*<FF><00><7F>hello"));

			// pack(...) should use tuple serialization
			Assert.That(subspace.Pack(123).ToString(), Is.EqualTo("*<FF><00><7F><15>{"));
			Assert.That(subspace.Pack("hello").ToString(), Is.EqualTo("*<FF><00><7F><02>hello<00>"));
			Assert.That(subspace.Pack(Slice.FromAscii("world")).ToString(), Is.EqualTo("*<FF><00><7F><01>world<00>"));
			Assert.That(subspace.Pack(FdbTuple.Create("hello", 123)).ToString(), Is.EqualTo("*<FF><00><7F><02>hello<00><15>{"));

			// if we derive a tuple from this subspace, it should keep the binary prefix when converted to a key
			var t = subspace.Append("world", 123, false);
			Assert.That(t, Is.Not.Null);
			Assert.That(t.Count, Is.EqualTo(3));
			Assert.That(t.Get<string>(0), Is.EqualTo("world"));
			Assert.That(t.Get<int>(1), Is.EqualTo(123));
			Assert.That(t.Get<bool>(2), Is.False);
			var k = t.ToSlice();
			Assert.That(k.ToString(), Is.EqualTo("*<FF><00><7F><02>world<00><15>{<14>"));

			// if we unpack the key with the binary prefix, we should get a valid tuple
			var t2 = subspace.Unpack(k);
			Assert.That(t2, Is.Not.Null);
			Assert.That(t2.Count, Is.EqualTo(3));
			Assert.That(t2.Get<string>(0), Is.EqualTo("world"));
			Assert.That(t2.Get<int>(1), Is.EqualTo(123));
			Assert.That(t2.Get<bool>(2), Is.False);
		}
		public static MemoryDatabase CreateNew(string name, FdbSubspace globalSpace, bool readOnly)
		{
			globalSpace = globalSpace ?? FdbSubspace.Empty;
			var uid = Guid.NewGuid();

			MemoryClusterHandler cluster = null;
			MemoryDatabaseHandler db = null;
            try
			{
				cluster = new MemoryClusterHandler();
				db = cluster.OpenDatabase(uid);

				// initialize the system keys for this new db
				db.PopulateSystemKeys();

				return new MemoryDatabase(new FdbCluster(cluster, ":memory:"), db, name, globalSpace, null, readOnly, true);
			}
			catch
			{
				if (db != null) db.Dispose();
				if (cluster != null) cluster.Dispose();
				throw;
			}
		}
Beispiel #7
0
        public const int DefaultChunkSize = 1 << 20;         // 1 MB

        public FdbDocumentCollection(FdbSubspace subspace, Func <TDocument, TId> selector, IValueEncoder <TDocument> valueEncoder)
            : this(subspace, selector, KeyValueEncoders.Tuples.CompositeKey <TId, int>(), valueEncoder)
        {
        }
		private async Task<KeyValuePair<Slice, Slice>> FindRandomItem(IFdbTransaction tr, FdbSubspace ring)
		{
			var range = ring.ToRange();

			// start from a random position around the ring
			Slice key = ring.Pack(GetRandomId());

			// We want to find the next item in the clockwise direction. If we reach the end of the ring, we "wrap around" by starting again from the start
			// => So we do find_next(key <= x < MAX) and if that does not produce any result, we do a find_next(MIN <= x < key)

			// When the ring only contains a few items (or is empty), there is more than 50% change that we wont find anything in the first read.
			// To reduce the latency for this case, we will issue both range reads at the same time, and discard the second one if the first returned something.
			// This should reduce the latency in half when the ring is empty, or when it contains only items before the random key.

			var candidate = await tr.GetRange(key, range.End).FirstOrDefaultAsync();

			if (!candidate.Key.IsPresent)
			{
				candidate = await tr.GetRange(range.Begin, key).FirstOrDefaultAsync();
			}

			return candidate;
		}
		//TODO: move these methods to FdbTest ?

		/// <summary>Connect to the local test database</summary>
		public static Task<IFdbDatabase> OpenTestDatabaseAsync(CancellationToken ct)
		{
			var subspace = new FdbSubspace(TestGlobalPrefix.Memoize());
			return Fdb.OpenAsync(TestClusterFile, TestDbName, subspace, false, ct);
		}
		/// <summary>Create a new database handler instance using the specificied cluster file, database name, global subspace and read only settings</summary>
		internal static async Task<FdbDatabase> OpenInternalAsync(string clusterFile, string dbName, FdbSubspace globalSpace, bool readOnly, CancellationToken cancellationToken)
		{
			cancellationToken.ThrowIfCancellationRequested();

			dbName = dbName ?? "DB";
			globalSpace = globalSpace ?? FdbSubspace.Empty;

			if (Logging.On) Logging.Info(typeof(Fdb), "OpenAsync", String.Format("Connecting to database '{0}' using cluster file '{1}' and subspace '{2}' ...", dbName, clusterFile, globalSpace));

			FdbCluster cluster = null;
			FdbDatabase db = null;
			bool success = false;
			try
			{
				cluster = await CreateClusterInternalAsync(clusterFile, cancellationToken).ConfigureAwait(false);
				//note: since the cluster is not provided by the caller, link it with the database's Dispose()
				db = await cluster.OpenDatabaseInternalAsync(dbName, globalSpace, readOnly: readOnly, ownsCluster: true, cancellationToken: cancellationToken).ConfigureAwait(false);
				success = true;
				return db;
			}
			finally
			{
				if (!success)
				{
					// cleanup the cluter if something went wrong
					if (db != null) db.Dispose();
					if (cluster != null) cluster.Dispose();
				}
			}
		}
		/// <summary>Create a new connection with the "DB" database on the cluster specified by the default cluster file, and with the specified global subspace</summary>
		/// <param name="globalSpace">Global subspace used as a prefix for all keys and layers</param>
		/// <param name="cancellationToken">Token used to abort the operation</param>
		/// <returns>Task that will return an FdbDatabase, or an exception</returns>
		/// <exception cref="OperationCanceledException">If the token <paramref name="cancellationToken"/> is cancelled</exception>
		/// <remarks>Since connections are not pooled, so this method can be costly and should NOT be called every time you need to read or write from the database. Instead, you should open a database instance at the start of your process, and use it a singleton.</remarks>
		public static Task<IFdbDatabase> OpenAsync(FdbSubspace globalSpace, CancellationToken cancellationToken = default(CancellationToken))
		{
			return OpenAsync(clusterFile: null, dbName: null, globalSpace: globalSpace, cancellationToken: cancellationToken);
		}
Beispiel #12
0
		protected Task DumpSubspace(IFdbDatabase db, FdbSubspace subspace)
		{
			return TestHelpers.DumpSubspace(db, subspace, this.Cancellation);
		}
		/// <summary>Returns the list of names and nodes of all children of the specified node</summary>
		private IFdbAsyncEnumerable<KeyValuePair<string, FdbSubspace>> SubdirNamesAndNodes(IFdbReadOnlyTransaction tr, FdbSubspace node)
		{
			Contract.Requires(tr != null && node != null);

			var sd = node.Partition(SUBDIRS);
			return tr
				.GetRange(sd.ToRange())
				.Select(kvp => new KeyValuePair<string, FdbSubspace>(
					sd.UnpackSingle<string>(kvp.Key),
					NodeWithPrefix(kvp.Value)
				));
		}
			public Node(FdbSubspace subspace, IFdbTuple path, IFdbTuple targetPath, Slice layer)
			{
				this.Subspace = subspace;
				this.Path = path;
				this.TargetPath = targetPath;
				this.Layer = layer;
			}
		public static FdbDirectoryLayer Create(FdbSubspace subspace, IEnumerable<string> path = null)
		{
			if (subspace == null) throw new ArgumentNullException("subspace");

			var location = path != null ? ParsePath(path) : FdbTuple.Empty;
			return new FdbDirectoryLayer(subspace[FdbKey.Directory], subspace, location);
		}
		public void Test_Subspace_Partitioning_With_Tuple_Suffix()
		{
			// start from a parent subspace
			var parent = new FdbSubspace(Slice.Create(new byte[] { 254 }));
			Assert.That(parent.Key.ToString(), Is.EqualTo("<FE>"));

			// create a child subspace using a tuple
			var child = parent.Partition(FdbTuple.Create("hca"));
			Assert.That(child, Is.Not.Null);
			Assert.That(child.Key.ToString(), Is.EqualTo("<FE><02>hca<00>"));

			// create a tuple from this child subspace
			var tuple = child.Append(123);
			Assert.That(tuple, Is.Not.Null);
			Assert.That(tuple.ToSlice().ToString(), Is.EqualTo("<FE><02>hca<00><15>{"));

			// derive another tuple from this one
			var t1 = tuple.Append(false);
			Assert.That(t1.ToSlice().ToString(), Is.EqualTo("<FE><02>hca<00><15>{<14>"));

			// check that we could also create the same tuple starting from the parent subspace
			var t2 = parent.Append("hca", 123, false);
			Assert.That(t2.ToSlice(), Is.EqualTo(t1.ToSlice()));

			// cornercase
			Assert.That(child[FdbTuple.Empty].Key, Is.EqualTo(child.Key));

		}
Beispiel #17
0
 internal FdbDirectoryPartition(IFdbTuple location, IFdbTuple relativeLocation, Slice prefix, FdbDirectoryLayer directoryLayer)
     : base(location, relativeLocation, prefix, new FdbDirectoryLayer(FdbSubspace.CreateDynamic(prefix + FdbKey.Directory, TypeSystem.Tuples), FdbSubspace.CreateDynamic(prefix, TypeSystem.Tuples), location), LayerId, TypeSystem.Tuples.GetDynamicEncoder())
 {
     m_parentDirectoryLayer = directoryLayer;
 }
Beispiel #18
0
        //TODO: move these methods to FdbTest ?

        /// <summary>Connect to the local test database</summary>
        public static Task <IFdbDatabase> OpenTestDatabaseAsync(CancellationToken ct)
        {
            var subspace = new FdbSubspace(TestGlobalPrefix.Memoize());

            return(Fdb.OpenAsync(TestClusterFile, TestDbName, subspace, false, ct));
        }
		public static FdbDirectoryLayer Create(FdbSubspace nodeSubspace, FdbSubspace contentSubspace, IEnumerable<string> path = null)
		{
			if (nodeSubspace == null) throw new ArgumentNullException("nodeSubspace");
			if (contentSubspace == null) throw new ArgumentNullException("contentSubspace");

			var location = path != null ? ParsePath(path) : FdbTuple.Empty;
			//TODO: check that nodeSubspace != contentSubspace?
			return new FdbDirectoryLayer(nodeSubspace, contentSubspace, location);
		}
Beispiel #20
0
		/// <summary>Create a new High Contention counter.</summary>
		/// <param name="db">Database used by this layer</param>
		/// <param name="subspace">Subspace to be used for storing the counter</param>
		public FdbHighContentionCounter(IFdbDatabase db, FdbSubspace subspace)
			: this(db, subspace, KeyValueEncoders.Tuples.Value<long>())
		{ }
		/// <summary>Returns a new Directory Subspace given its node subspace, path and layer id</summary>
		private FdbDirectorySubspace ContentsOfNode(FdbSubspace node, IFdbTuple relativePath, Slice layer)
		{
			Contract.Requires(node != null);

			var path = this.Location.Concat(relativePath);
			var prefix = this.NodeSubspace.UnpackSingle<Slice>(node.Key);
			if (layer == FdbDirectoryPartition.LayerId)
			{
				return new FdbDirectoryPartition(path, relativePath, prefix, this);
			}
			else
			{
				return new FdbDirectorySubspace(path, relativePath, prefix, this, layer);
			}
		}
		private static async Task RunMultiClientTest(IFdbDatabase db, FdbSubspace location, bool highContention, string desc, int K, int NUM, CancellationToken ct)
		{
			Console.WriteLine("Starting {0} test with {1} threads and {2} iterations", desc, K, NUM);

			var queue = new FdbQueue<string>(location, highContention);
			await db.WriteAsync((tr) => queue.Clear(tr), ct);

			// use a CTS to ensure that everything will stop in case of problems...
			using (var go = new CancellationTokenSource(TimeSpan.FromSeconds(30)))
			{
				var tok = go.Token;

				var pushLock = new AsyncCancelableMutex(tok);
				var popLock = new AsyncCancelableMutex(tok);

				int pushCount = 0;
				int popCount = 0;
				int stalls = 0;

				var pushTreads = Enumerable.Range(0, K)
					.Select(async id =>
					{
						// wait for the signal
						await pushLock.Task.ConfigureAwait(false);

						var res = new List<string>(NUM);

						for (int i = 0; i < NUM; i++)
						{
							var item = id.ToString() + "." + i.ToString();
							await db.ReadWriteAsync((tr) => queue.PushAsync(tr, item), tok).ConfigureAwait(false);

							Interlocked.Increment(ref pushCount);
							res.Add(item);
						}

						return res;
					}).ToArray();

				var popThreads = Enumerable.Range(0, K)
					.Select(async id =>
					{
						// make everyone wait a bit, to ensure that they all start roughly at the same time
						await popLock.Task.ConfigureAwait(false);

						var res = new List<string>(NUM);

						int i = 0;
						while (i < NUM)
						{
							var item = await queue.PopAsync(db, tok).ConfigureAwait(false);
							if (item.HasValue)
							{
								Interlocked.Increment(ref popCount);
								res.Add(item.Value);
								++i;
							}
							else
							{
								Interlocked.Increment(ref stalls);
								await Task.Delay(10).ConfigureAwait(false);
							}
						}

						return res;
					}).ToArray();

				var sw = Stopwatch.StartNew();

				pushLock.Set(async: true);
				await Task.Delay(100);
				popLock.Set(async: true);

				//using (var timer = new Timer((_) =>
				//{
				//	var __ = TestHelpers.DumpSubspace(db, location);
				//}, null, 1000, 4000))
				{

					await Task.WhenAll(pushTreads);
					await Task.WhenAll(popThreads);
				}

				sw.Stop();
				Console.WriteLine("> Finished {0} test in {1} seconds", desc, sw.Elapsed.TotalSeconds);
				Console.WriteLine("> Pushed {0}, Popped {1} and Stalled {2}", pushCount, popCount, stalls);

				var pushedItems = pushTreads.SelectMany(t => t.Result).ToList();
				var poppedItems = popThreads.SelectMany(t => t.Result).ToList();

				Assert.That(pushCount, Is.EqualTo(K * NUM));
				Assert.That(popCount, Is.EqualTo(K * NUM));

				// all pushed items should have been popped (with no duplicates)
				Assert.That(poppedItems, Is.EquivalentTo(pushedItems));

				// the queue should be empty
				bool empty = await db.ReadAsync((tr) => queue.EmptyAsync(tr), ct);
				Assert.That(empty, Is.True);
			}
		}
		/// <summary>Resursively remove a node (including the content), all its children</summary>
		private async Task RemoveRecursive(IFdbTransaction tr, FdbSubspace node)
		{
			Contract.Requires(tr != null && node != null);

			//note: we could use Task.WhenAll to remove the children, but there is a risk of task explosion if the subtree is very large...
			await SubdirNamesAndNodes(tr, node).ForEachAsync((kvp) => RemoveRecursive(tr, kvp.Value)).ConfigureAwait(false);

			// remove ALL the contents
			if (FdbDirectoryLayer.AnnotateTransactions) tr.Annotate("Removing all content located under {0}", node.Key);
			tr.ClearRange(FdbKeyRange.StartsWith(ContentsOfNode(node, FdbTuple.Empty, Slice.Empty).Key));
			// and all the metadata for this folder
			if (FdbDirectoryLayer.AnnotateTransactions) tr.Annotate("Removing all metadata for folder under {0}", node.Key);
			tr.ClearRange(node.ToRange());
		}
		/// <summary>Opens a database on this cluster, configured to only access a specific subspace of keys</summary>
		/// <param name="databaseName">Name of the database. Must be 'DB' (as of Beta 2)</param>
		/// <param name="subspace">Subspace of keys that will be accessed.</param>
		/// <param name="readOnly">If true, the database will only allow read operations.</param>
		/// <param name="cancellationToken">Cancellation Token (optionnal) for the connect operation</param>
		/// <returns>Task that will return an FdbDatabase, or an exception</returns>
		/// <exception cref="System.InvalidOperationException">If <paramref name="databaseName"/> is anything other than 'DB'</exception>
		/// <exception cref="System.OperationCanceledException">If the token <paramref name="cancellationToken"/> is cancelled</exception>
		/// <remarks>Any attempt to use a key outside the specified subspace will throw an exception</remarks>
		public async Task<IFdbDatabase> OpenDatabaseAsync(string databaseName, FdbSubspace subspace, bool readOnly, CancellationToken cancellationToken)
		{
			if (subspace == null) throw new ArgumentNullException("subspace");
			return await OpenDatabaseInternalAsync(databaseName, subspace, readOnly: readOnly, ownsCluster: false, cancellationToken: cancellationToken).ConfigureAwait(false);
		}
Beispiel #25
0
		protected async Task DeleteSubspace(IFdbDatabase db, FdbSubspace subspace)
		{
			using (var tr = db.BeginTransaction(this.Cancellation))
			{
				tr.ClearRange(subspace);
				await tr.CommitAsync();
			}
		}
		/// <summary>Opens a database on this cluster</summary>
		/// <param name="databaseName">Name of the database. Must be 'DB'</param>
		/// <param name="subspace">Subspace of keys that will be accessed.</param>
		/// <param name="readOnly">If true, the database will only allow read operations.</param>
		/// <param name="ownsCluster">If true, the database will dispose this cluster when it is disposed.</param>
		/// <param name="cancellationToken">Cancellation Token</param>
		/// <returns>Task that will return an FdbDatabase, or an exception</returns>
		/// <exception cref="System.InvalidOperationException">If <paramref name="databaseName"/> is anything other than 'DB'</exception>
		/// <exception cref="System.OperationCanceledException">If the token <paramref name="cancellationToken"/> is cancelled</exception>
		/// <remarks>As of Beta2, the only supported database name is 'DB'</remarks>
		internal async Task<FdbDatabase> OpenDatabaseInternalAsync(string databaseName, FdbSubspace subspace, bool readOnly, bool ownsCluster, CancellationToken cancellationToken)
		{
			ThrowIfDisposed();
			if (string.IsNullOrEmpty(databaseName)) throw new ArgumentNullException("databaseName");
			if (subspace == null) throw new ArgumentNullException("subspace");

			if (Logging.On) Logging.Info(typeof(FdbCluster), "OpenDatabaseAsync", String.Format("Connecting to database '{0}' ...", databaseName));

			if (cancellationToken.IsCancellationRequested) cancellationToken.ThrowIfCancellationRequested();

			var handler = await m_handler.OpenDatabaseAsync(databaseName, cancellationToken).ConfigureAwait(false);

			if (Logging.On && Logging.IsVerbose) Logging.Verbose(typeof(FdbCluster), "OpenDatabaseAsync", String.Format("Connected to database '{0}'", databaseName));

			return FdbDatabase.Create(this, handler, databaseName, subspace, null, readOnly, ownsCluster);
		}
		/// <summary>Create a new connection with a database on the specified cluster</summary>
		/// <param name="clusterFile">Path to the 'fdb.cluster' file to use, or null for the default cluster file</param>
		/// <param name="dbName">Name of the database. Must be 'DB'</param>
		/// <param name="globalSpace">Global subspace used as a prefix for all keys and layers</param>
		/// <param name="readOnly">If true, the database instance will only allow read operations</param>
		/// <param name="cancellationToken">Token used to abort the operation</param>
		/// <returns>Task that will return an FdbDatabase, or an exception</returns>
		/// <remarks>As of 1.0, the only supported database name is 'DB'</remarks>
		/// <exception cref="InvalidOperationException">If <paramref name="dbName"/> is anything other than 'DB'</exception>
		/// <exception cref="OperationCanceledException">If the token <paramref name="cancellationToken"/> is cancelled</exception>
		/// <remarks>Since connections are not pooled, so this method can be costly and should NOT be called every time you need to read or write from the database. Instead, you should open a database instance at the start of your process, and use it a singleton.</remarks>
		public static async Task<IFdbDatabase> OpenAsync(string clusterFile, string dbName, FdbSubspace globalSpace, bool readOnly = false, CancellationToken cancellationToken = default(CancellationToken))
		{
			return await OpenInternalAsync(clusterFile, dbName, globalSpace, readOnly, cancellationToken);
		}
		/// <summary>
		/// Create a new object representing a binary large object (blob).
		/// Only keys within the subspace will be used by the object. 
		/// Other clients of the database should refrain from modifying the subspace.</summary>
		/// <param name="subspace">Subspace to be used for storing the blob data and metadata</param>
		public FdbBlob(FdbSubspace subspace)
		{
			if (subspace == null) throw new ArgumentNullException("subspace");

			this.Subspace = subspace;
		}
		public PrefixRewriterTransaction(FdbSubspace prefix, IFdbTransaction trans, bool ownsTransaction)
			: base(trans, false, ownsTransaction)
		{
			if (prefix == null) throw new ArgumentNullException("prefix");
			m_prefix = prefix;
		}
		private static Slice GetSubDirKey(FdbSubspace parent, string path)
		{
			Contract.Requires(parent != null && path != null);

			// for a path equal to ("foo","bar","baz") and index = -1, we need to generate (parent, SUBDIRS, "baz")
			// but since the last item of path can be of any type, we will use tuple splicing to copy the last item without changing its type
			return parent.Pack(SUBDIRS, path);
		}
		public FdbWorkerPool(FdbSubspace subspace)
		{
			if (subspace == null) throw new ArgumentNullException("subspace");

			this.Subspace = subspace;

			this.TaskStore = subspace.Partition(Slice.FromChar('T'));
			this.IdleRing = subspace.Partition(Slice.FromChar('I'));
			this.BusyRing = subspace.Partition(Slice.FromChar('B'));
			this.UnassignedTaskRing = subspace.Partition(Slice.FromChar('U'));

			this.Counters = new FdbCounterMap<int>(subspace.Partition(Slice.FromChar('C')));
		}
		/// <summary>
		/// Creates a new instance that will manages directories in FoudnationDB.
		/// </summary>
		/// <param name="nodeSubspace">Subspace where all the node metadata will be stored ('\xFE' by default)</param>
		/// <param name="contentSubspace">Subspace where all automatically allocated directories will be stored (empty by default)</param>
		/// <param name="location">Location of the root of all the directories managed by this Directory Layer. Ususally empty for the root partition of the database.</param>
		internal FdbDirectoryLayer(FdbSubspace nodeSubspace, FdbSubspace contentSubspace, IFdbTuple location)
		{
			Contract.Requires(nodeSubspace != null && contentSubspace != null);

			// If specified, new automatically allocated prefixes will all fall within content_subspace
			this.ContentSubspace = contentSubspace;
			this.NodeSubspace = nodeSubspace;

			// The root node is the one whose contents are the node subspace
			this.RootNode = nodeSubspace.Partition(nodeSubspace.Key);
			this.Allocator = new FdbHighContentionAllocator(this.RootNode.Partition(HcaKey));
			if (location == null || location.Count == 0)
			{
				this.Location = FdbTuple.Empty;
				this.Path = new string[0];
			}
			else
			{
				this.Location = location;
				this.Path = location.ToArray<string>();
			}
		}
		private async Task PushQueueAsync(IFdbTransaction tr, FdbSubspace queue, Slice taskId)
		{
			//TODO: use a high contention algo ?
			// - must support Push and Pop
			// - an empty queue must correspond to an empty subspace

			// get the current size of the queue
			var range = queue.ToRange();
			var lastKey = await tr.Snapshot.GetKeyAsync(FdbKeySelector.LastLessThan(range.End)).ConfigureAwait(false);
			int count = lastKey < range.Begin ? 0 : queue.Unpack(lastKey).Get<int>(0) + 1;

			// set the value
			tr.Set(queue.Pack(count, GetRandomId()), taskId);
		}
        private static async Task MainAsync(CancellationToken ct)
        {
            // change the path to the native lib if not default
            if (NATIVE_PATH != null)
            {
                Fdb.Options.SetNativeLibPath(NATIVE_PATH);
            }

            // uncomment this to enable network thread tracing
            // FdbCore.TracePath = Path.Combine(Path.GetTempPath(), "fdb");

            int apiVersion = Fdb.GetMaxApiVersion();

            Console.WriteLine("Max API Version: " + apiVersion);

            try
            {
                Console.WriteLine("Starting network thread...");
                Fdb.Start();                 // this will select API version 21
                Console.WriteLine("> Up and running");

                Console.WriteLine("Connecting to local cluster...");
                using (var cluster = await Fdb.CreateClusterAsync(CLUSTER_FILE, ct))
                {
                    Console.WriteLine("> Connected!");

                    Console.WriteLine("Opening database 'DB'...");
                    using (var db = await cluster.OpenDatabaseAsync(DB_NAME, FdbSubspace.Create(FdbTuple.Create(SUBSPACE)), false, ct))
                    {
                        Console.WriteLine("> Connected to db '{0}'", db.Name);

                        // get coordinators
                        var cf = await Fdb.System.GetCoordinatorsAsync(db, ct);

                        Console.WriteLine("Coordinators: " + cf.ToString());

                        // clear everything
                        using (var tr = db.BeginTransaction(ct))
                        {
                            Console.WriteLine("Clearing subspace " + db.GlobalSpace + " ...");
                            tr.ClearRange(db.GlobalSpace);
                            await tr.CommitAsync();

                            Console.WriteLine("> Database cleared");
                        }

                        Console.WriteLine("----------");

                        await TestSimpleTransactionAsync(db, ct);

                        Console.WriteLine("----------");

                        await BenchInsertSmallKeysAsync(db, N, 16, ct);                         // some guid
                        await BenchInsertSmallKeysAsync(db, N, 60 * 4, ct);                     // one Int32 per minutes, over an hour
                        await BenchInsertSmallKeysAsync(db, N, 512, ct);                        // small JSON payload

                        ////await BenchInsertSmallKeysAsync(db, N, 4096, ct); // typical small cunk size
                        ////await BenchInsertSmallKeysAsync(db, N / 10, 65536, ct); // typical medium chunk size
                        //await BenchInsertSmallKeysAsync(db, 1, 100000, ct); // Maximum value size (as of beta 1)

                        ////// insert keys in parrallel
                        await BenchConcurrentInsert(db, 1, 100, 512, ct);
                        await BenchConcurrentInsert(db, 1, 1000, 512, ct);
                        await BenchConcurrentInsert(db, 1, 10000, 512, ct);

                        await BenchConcurrentInsert(db, 1, N, 16, ct);
                        await BenchConcurrentInsert(db, 2, N, 16, ct);
                        await BenchConcurrentInsert(db, 4, N, 16, ct);
                        await BenchConcurrentInsert(db, 8, N, 16, ct);
                        await BenchConcurrentInsert(db, 16, N, 16, ct);

                        //await BenchSerialWriteAsync(db, N, ct);
                        //await BenchSerialReadAsync(db, N, ct);
                        //await BenchConcurrentReadAsync(db, N, ct);

                        //await BenchClearAsync(db, N, ct);

                        await BenchUpdateSameKeyLotsOfTimesAsync(db, 1000, ct);

                        await BenchUpdateLotsOfKeysAsync(db, 1000, ct);

                        await BenchBulkInsertThenBulkReadAsync(db, 100 * 1000, 50, 128, ct);
                        await BenchBulkInsertThenBulkReadAsync(db, 100 * 1000, 128, 50, ct);

                        ////await BenchBulkInsertThenBulkReadAsync(db, 1 * 1000 * 1000, 50, 128, ct);

                        await BenchMergeSortAsync(db, 100, 3, 20, ct);
                        await BenchMergeSortAsync(db, 1000, 10, 100, ct);
                        await BenchMergeSortAsync(db, 100, 100, 100, ct);
                        await BenchMergeSortAsync(db, 100, 1000, 100, ct);

                        Console.WriteLine("time to say goodbye...");
                    }
                }
            }
            finally
            {
                Console.WriteLine("### DONE ###");
                Fdb.Stop();
            }
#if DEBUG
            Console.ReadLine();
#endif
        }