public async Task <DocumentHeaderResponse> Store(CouchConveyEntry <T> entry, string revision) { if (this.Client == null) { Logger.ErrorFormat("CouchDb client is not ready to process: {0}", entry); throw new InvalidOperationException("CouchDb client is not ready."); } var res = await TryPut(entry, revision); if (res != null) { return(res); } if (entry.Overwrite) { while (res == null) { var head = await this.Client.Documents.HeadAsync(entry.Id); res = await TryPut(entry, head.Rev); // We still have a chance to get conflict by timing } } return(res); }
public async Task <DocumentHeaderResponse> TryPut(CouchConveyEntry <T> entry, string revision) { try { DocumentHeaderResponse header = null; if (entry.Json == null && entry.Entity == null) { header = await this.Client.Documents.DeleteAsync(entry.Id, revision); } else if (revision != null) { header = await this.Client.Documents.PutAsync(entry.Id, revision, entry.Json); } else { header = await this.Client.Documents.PutAsync(entry.Id, entry.Json); } if (header.StatusCode == System.Net.HttpStatusCode.Conflict && entry.Overwrite) { return(null); // In this case, we will retry this outside so it's expected exception. } return(header); } catch (MyCouchResponseException ex) { Logger.DebugFormat("Error during put document {0}:{1} by {2}", entry.Id, revision, ex); if (ex.HttpStatus == System.Net.HttpStatusCode.Conflict && entry.Overwrite) { return(null); // In this case, we will retry this outside so it's expected exception. } throw ex; } }
public CouchTaskSerializeContext <T> GetContextFor(CouchConveyEntry <T> entry) { CouchTaskSerializeContext <T> context; if (_working_map.TryGetValue(entry.Id, out context)) { return(context); } return(_working_map.GetOrAdd(entry.Id, new CouchTaskSerializeContext <T>(entry.Id))); }
protected override async Task <bool> Process(CouchConveyEntry <T> entry) { try { var header = await Store(entry, null); if (header != null && header.IsSuccess) { entry.Handler.OnStored(entry.Id, header.Rev, entry.Entity); return(true); } throw new MyCouchResponseException(header); } catch (Exception ex) { entry.Handler.OnFailed(entry.Id, entry.Entity, ex); return(false); } }
internal void Processed(CouchConveyEntry <T> t) { ProcessedQueue.Enqueue(t); ProcessedContext.Enqueue(Tuple.Create(this._lock_seq, this._lock_id)); }
// FIXME: This weird method extraction is for resolve synchronization problem between `this.WorkingQueue.TryTake(out entry)` // and `context.WaitingQueue.Enqueue(entry);`. But this is too rough. Let me polish this later. protected override async Task <bool> TakeOneAndProcess() { CouchConveyEntry <T> entry = null; CouchTaskSerializeContext <T> context = null; lock (this._task_serializer) { if (this.WorkingQueue.TryTake(out entry)) { context = _task_serializer.GetContextFor(entry); context.WaitingQueue.Enqueue(entry); } else { return(false); } } if (entry == null || context == null) { Logger.Error("Invalid queue handling", new InvalidOperationException()); return(false); } int?lock_id = context.TryStartWork(); if (null == lock_id) { return(true); } try { CouchConveyEntry <T> t; while (context.WaitingQueue.TryDequeue(out t)) { context.Processed(t); try { var header = await Store(t, context.Rev); if (header != null && header.IsSuccess) { t.Handler.OnStored(t.Id, header.Rev, t.Entity); context.Rev = header.Rev; } else { throw new MyCouchResponseException(header); } } catch (Exception ex) { t.Handler.OnFailed(t.Id, t.Entity, ex); } } } catch (Exception ex) { Logger.Error(new object[] { "Dispatcher throws uncaught exception.", entry }, ex); } finally { context.EndWork(lock_id ?? 0); } return(true); }