public async Task Add(CachedEvent e) { if (null == e) throw new KeenException("Cached events may not be null"); var keenFolder = await getKeenFolder() .ConfigureAwait(continueOnCapturedContext: false); IFile file; var attempts = 0; var done = false; string name = null; do { attempts++; // Avoid race conditions in parallel environment by locking on the events queue // and generating and inserting a unique name within the lock. CreateFileAsync has // a CreateCollisionOption.GenerateUniqueName, but it will return the same name // multiple times when called from parallel tasks. // If creating and writing the file fails, loop around and if (string.IsNullOrEmpty(name)) lock (events) { var i = 0; while (events.Contains(name = e.Collection + i++)) ; events.Enqueue(name); } Exception lastErr = null; try { file = await keenFolder.CreateFileAsync(name, CreationCollisionOption.FailIfExists) .ConfigureAwait(continueOnCapturedContext: false); var content = JObject.FromObject(e).ToString(); await file.WriteAllTextAsync(content) .ConfigureAwait(continueOnCapturedContext: false); done = true; } catch (Exception ex) { lastErr = ex; } // If the file was not created, not written, or partially written, // the events queue may be left with a file name that references a // file that is nonexistent, empty, or invalid. It's easier to handle // this when the queue is read than to try to dequeue the name. if (attempts > 100) throw new KeenException("Persistent failure while saving file, aborting", lastErr); } while (!done); }
public Task Add(CachedEvent e) { if (null == e) throw new KeenException("Cached events may not be null"); return Task.Run(() => { lock (events) events.Enqueue(e); }); }
public async Task<CachedEvent> TryTake() { var keenFolder = await getKeenFolder() .ConfigureAwait(continueOnCapturedContext: false); if (!events.Any()) return null; string fileName; lock(events) fileName = events.Dequeue(); var file = await keenFolder.GetFileAsync(fileName) .ConfigureAwait(continueOnCapturedContext: false); var content = await file.ReadAllTextAsync() .ConfigureAwait(continueOnCapturedContext: false); var ce = JObject.Parse(content); var item = new CachedEvent((string)ce.SelectToken("Collection"), (JObject)ce.SelectToken("Event"), ce.SelectToken("Error").ToObject<Exception>() ); await file.DeleteAsync() .ConfigureAwait(continueOnCapturedContext: false); return item; }