private async Task RunTask(IFdbDatabase db, FdbWorkerMessage msg, Func <FdbWorkerMessage, CancellationToken, Task> handler, CancellationToken ct)
        {
            var sw = Stopwatch.StartNew();

            try
            {
                Interlocked.Increment(ref m_workerTasksReceived);

                //TODO: custom TaskScheduler for task execution ?
                await handler(msg, ct).ConfigureAwait(false);

                Interlocked.Increment(ref m_workerTasksCompleted);
            }
            finally
            {
                sw.Stop();
                Interlocked.Add(ref m_workerBusyTime, sw.ElapsedTicks);
            }
        }
		private async Task RunTask(IFdbDatabase db, FdbWorkerMessage msg, Func<FdbWorkerMessage, CancellationToken, Task> handler, CancellationToken ct)
		{
			var sw = Stopwatch.StartNew();
			try
			{
				Interlocked.Increment(ref m_workerTasksReceived);

				//TODO: custom TaskScheduler for task execution ?
				await handler(msg, ct).ConfigureAwait(false);

				Interlocked.Increment(ref m_workerTasksCompleted);
			}
			finally
			{
				sw.Stop();
				Interlocked.Add(ref m_workerBusyTime, sw.ElapsedTicks);
			}
		}
		/// <summary>Run the worker loop</summary>
		public async Task RunWorkerAsync(IFdbDatabase db, Func<FdbWorkerMessage, CancellationToken, Task> handler, CancellationToken ct)
		{
			int num = Interlocked.Increment(ref counter);

			Slice workerId = Slice.Nil;
			Slice previousTaskId = Slice.Nil;
			FdbWatch watch = default(FdbWatch);
			FdbWorkerMessage msg = null;

			Interlocked.Increment(ref m_workers);
			try
			{
				while (true)
				{
					//TODO: how do we clear the previousTaskId from the db in case of cancellation ?
					ct.ThrowIfCancellationRequested();

					Slice myId = Slice.Nil;
					await db.ReadWriteAsync(
						async (tr) =>
						{
							tr.Annotate("I'm worker #{0} with id {1}", num, workerId.ToAsciiOrHexaString());

							myId = workerId;
							watch = default(FdbWatch);
							msg = new FdbWorkerMessage();

							if (previousTaskId != null)
							{ // we need to clean up the previous task
								ClearTask(tr, previousTaskId);
							}
							else if (myId.IsPresent)
							{ // look for an already assigned task
								tr.Annotate("Look for already assigned task");
								msg.Id = await tr.GetAsync(this.BusyRing.Pack(myId)).ConfigureAwait(false);
							}

							if (!msg.Id.IsPresent)
							{ // We aren't already assigned a task, so get an item from a random queue

								tr.Annotate("Look for next queued item");
								
								// Find the next task on the queue
								var item = await tr.GetRange(this.UnassignedTaskRing.ToRange()).FirstOrDefaultAsync().ConfigureAwait(false);

								if (item.Key != null)
								{ // pop the Task from the queue
									msg.Id = item.Value;
									tr.Clear(item.Key);
								}

								if (msg.Id.IsPresent)
								{ // mark this worker as busy
									// note: we need a random id so generate one if it is the first time...
									if (!myId.IsPresent) myId = GetRandomId();
									tr.Annotate("Found {0}, switch to busy with id {1}", msg.Id.ToAsciiOrHexaString(), myId.ToAsciiOrHexaString());
									tr.Set(this.BusyRing.Pack(myId), msg.Id);
									this.Counters.Increment(tr, COUNTER_BUSY);
								}
								else if (myId.IsPresent)
								{ // remove ourselves from the busy ring
									tr.Annotate("Found nothing, switch to idle with id {0}", myId.ToAsciiOrHexaString());
									//tr.Clear(this.BusyRing.Pack(myId));
								}
							}

							if (msg.Id.IsPresent)
							{ // get the task body

								tr.Annotate("Fetching body for task {0}", msg.Id.ToAsciiOrHexaString());
								var prefix = this.TaskStore.Partition(msg.Id);
								//TODO: replace this with a get_range ?
								var data = await tr.GetValuesAsync(new [] {
									prefix.Key,
									prefix.Pack(TASK_META_SCHEDULED)
								}).ConfigureAwait(false);

								msg.Body = data[0];
								msg.Scheduled = new DateTime(data[1].ToInt64(), DateTimeKind.Utc);
								msg.Received = DateTime.UtcNow;
							}
							else
							{ // There are no unassigned task, so enter the idle_worker_ring and wait for a task to be asssigned to us

								// remove us from the busy ring
								if (myId.IsPresent)
								{
									tr.Clear(this.BusyRing.Pack(myId));
									this.Counters.Decrement(tr, COUNTER_BUSY);
								}

								// choose a new random position on the idle ring
								myId = GetRandomId();

								// the idle key will also be used as the watch key to wake us up
								var watchKey = this.IdleRing.Pack(myId);
								tr.Annotate("Will start watching on key {0} with id {1}", watchKey.ToAsciiOrHexaString(), myId.ToAsciiOrHexaString());
								tr.Set(watchKey, Slice.Empty);
								this.Counters.Increment(tr, COUNTER_IDLE);

								watch = tr.Watch(watchKey, ct);
							}

						},
						onDone: (tr) =>
						{ // we have successfully acquired some work, or got a watch
							previousTaskId = Slice.Nil;
							workerId = myId;
						},
						cancellationToken: ct
					).ConfigureAwait(false);

					if (msg.Id.IsNullOrEmpty)
					{ // wait for someone to wake us up...
						Interlocked.Increment(ref m_idleWorkers);
						try
						{
							await watch.Task;
							//Console.WriteLine("Worker #" + num + " woken up!");
						}
						finally
						{
							Interlocked.Decrement(ref m_idleWorkers);
						}
					}
					else
					{
						//Console.WriteLine("Got task " + taskId);
						previousTaskId = msg.Id;

						if (msg.Body.IsNull)
						{ // the task has been dropped?
							// TODO: loggin?
							Console.WriteLine("[####] Task[" + msg.Id.ToAsciiOrHexaString() + "] has vanished?");
						}
						else
						{
							try
							{
								await RunTask(db, msg, handler, ct);
							}
							catch (Exception e)
							{
								//TODO: logging?
								Console.Error.WriteLine("Task[" + msg.Id.ToAsciiOrHexaString() + "] failed: " + e.ToString());
							}
						}
					}
				}
			}
			finally
			{
				//TODO: we should ensure that the last processed task is properly removed from the db before leaving !
				Interlocked.Decrement(ref m_workers);
			}
		}
        /// <summary>Run the worker loop</summary>
        public async Task RunWorkerAsync(IFdbDatabase db, Func <FdbWorkerMessage, CancellationToken, Task> handler, CancellationToken ct)
        {
            int num = Interlocked.Increment(ref s_counter);

            Slice            workerId       = Slice.Nil;
            Slice            previousTaskId = Slice.Nil;
            FdbWatch         watch          = null;
            FdbWorkerMessage msg            = null;

            Interlocked.Increment(ref m_workers);
            try
            {
                while (true)
                {
                    //TODO: how do we clear the previousTaskId from the db in case of cancellation ?
                    ct.ThrowIfCancellationRequested();

                    Slice myId = Slice.Nil;
                    await db.ReadWriteAsync(
                        async (tr) =>
                    {
                        tr.Annotate("I'm worker #{0} with id {1:P}", num, workerId);

                        myId  = workerId;
                        watch = null;
                        msg   = new FdbWorkerMessage();

                        if (previousTaskId != null)
                        {                                 // we need to clean up the previous task
                            ClearTask(tr, previousTaskId);
                        }
                        else if (myId.IsPresent)
                        {                                 // look for an already assigned task
                            tr.Annotate("Look for already assigned task");
                            msg.Id = await tr.GetAsync(this.BusyRing.Keys.Encode(myId)).ConfigureAwait(false);
                        }

                        if (!msg.Id.IsPresent)
                        {                                 // We aren't already assigned a task, so get an item from a random queue
                            tr.Annotate("Look for next queued item");

                            // Find the next task on the queue
                            var item = await tr.GetRange(this.UnassignedTaskRing.Keys.ToRange()).FirstOrDefaultAsync().ConfigureAwait(false);

                            if (item.Key != null)
                            {                                     // pop the Task from the queue
                                msg.Id = item.Value;
                                tr.Clear(item.Key);
                            }

                            if (msg.Id.IsPresent)
                            {                                     // mark this worker as busy
                                // note: we need a random id so generate one if it is the first time...
                                if (!myId.IsPresent)
                                {
                                    myId = GetRandomId();
                                }
                                tr.Annotate("Found {0:P}, switch to busy with id {1:P}", msg.Id, myId);
                                tr.Set(this.BusyRing.Keys.Encode(myId), msg.Id);
                                this.Counters.Increment(tr, COUNTER_BUSY);
                            }
                            else if (myId.IsPresent)
                            {                                     // remove ourselves from the busy ring
                                tr.Annotate("Found nothing, switch to idle with id {0:P}", myId);
                                //tr.Clear(this.BusyRing.Pack(myId));
                            }
                        }

                        if (msg.Id.IsPresent)
                        {                                 // get the task body
                            tr.Annotate("Fetching body for task {0:P}", msg.Id);
                            var prefix = this.TaskStore.Partition.ByKey(msg.Id);
                            //TODO: replace this with a get_range ?
                            var data = await tr.GetValuesAsync(new [] {
                                prefix.GetPrefix(),
                                prefix.Keys.Encode(TASK_META_SCHEDULED)
                            }).ConfigureAwait(false);

                            msg.Body      = data[0];
                            msg.Scheduled = new DateTime(data[1].ToInt64(), DateTimeKind.Utc);
                            msg.Received  = DateTime.UtcNow;
                        }
                        else
                        {                                 // There are no unassigned task, so enter the idle_worker_ring and wait for a task to be asssigned to us
                            // remove us from the busy ring
                            if (myId.IsPresent)
                            {
                                tr.Clear(this.BusyRing.Keys.Encode(myId));
                                this.Counters.Decrement(tr, COUNTER_BUSY);
                            }

                            // choose a new random position on the idle ring
                            myId = GetRandomId();

                            // the idle key will also be used as the watch key to wake us up
                            var watchKey = this.IdleRing.Keys.Encode(myId);
                            tr.Annotate("Will start watching on key {0:P} with id {1:P}", watchKey, myId);
                            tr.Set(watchKey, Slice.Empty);
                            this.Counters.Increment(tr, COUNTER_IDLE);

                            watch = tr.Watch(watchKey, ct);
                        }
                    },
                        success : (tr) =>
                    {                             // we have successfully acquired some work, or got a watch
                        previousTaskId = Slice.Nil;
                        workerId       = myId;
                    },
                        ct : ct
                        ).ConfigureAwait(false);

                    if (msg.Id.IsNullOrEmpty)
                    {                     // wait for someone to wake us up...
                        Interlocked.Increment(ref m_idleWorkers);
                        try
                        {
                            await watch.Task;
                            //Console.WriteLine("Worker #" + num + " woken up!");
                        }
                        finally
                        {
                            Interlocked.Decrement(ref m_idleWorkers);
                        }
                    }
                    else
                    {
                        //Console.WriteLine("Got task " + taskId);
                        previousTaskId = msg.Id;

                        if (msg.Body.IsNull)
                        {                          // the task has been dropped?
                                                   // TODO: loggin?
#if DEBUG
                            Console.WriteLine($"[####] Task[{msg.Id:P}] has vanished?");
#endif
                        }
                        else
                        {
                            try
                            {
                                await RunTask(db, msg, handler, ct);
                            }
                            catch (Exception e)
                            {
                                //TODO: logging?
#if DEBUG
                                Console.Error.WriteLine($"Task[{msg.Id:P}] failed: {e}");
#endif
                            }
                        }
                    }
                }
            }
            finally
            {
                //TODO: we should ensure that the last processed task is properly removed from the db before leaving !
                Interlocked.Decrement(ref m_workers);
            }
        }