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); } }