private void CheckExpire(TaskTimer timer) { // get the next scheduled item UpdateRecord data = null; lock (_data) { if (_queue.Count == 0) { _queueTimer.Change(_delay, TaskEnv.Current); return; } Tuplet <string, DateTime> key = _queue.Peek(); if (key.Item2 > DateTime.UtcNow) { _queueTimer.Change(key.Item2, TaskEnv.Current); return; } data = _data[key.Item1]; _queue.Dequeue(); _data.Remove(key.Item1); } _dispatcher.Dispatch(data); // check for optimal sleep interval lock (_data) { if (_queue.Count == 0) { _queueTimer.Change(_delay, TaskEnv.Current); return; } Tuplet <string, DateTime> key = _queue.Peek(); _queueTimer.Change(key.Item2, TaskEnv.Current); } }
//--- Methods --- public void Enqueue(XDoc meta) { XUri channel = meta["channel"].AsUri; RecordType type; int id; string path = string.Empty; string wikiid = meta["@wikiid"].AsText; if(channel.Segments[1] == "pages") { type = RecordType.Page; id = meta["pageid"].AsInt ?? 0; path = meta["path"].AsText; } else { type = RecordType.File; id = meta["fileid"].AsInt ?? 0; } string key = string.Format("{0}:{1}:{2}", wikiid, type, id); lock(_data) { UpdateRecord data; if(!_data.TryGetValue(key, out data)) { _log.DebugFormat("queueing '{0}'", key); _queue.Enqueue(new Tuplet<string, DateTime>(key, DateTime.UtcNow.Add(_delay))); data = new UpdateRecord(id, type, wikiid); _data.Add(key,data); } if(!string.IsNullOrEmpty(path)) { data.Path = path; } } }
//--- Methods --- public void Dispatch(UpdateRecord updateRecord) { _log.DebugFormat("moving update record '{0}' from delay to dispatch queue", updateRecord.Id); Interlocked.Increment(ref _dispatchCount); if(!_dispatchQueue.TryEnqueue(updateRecord)) { Interlocked.Decrement(ref _dispatchCount); throw new InvalidOperationException(string.Format("Enqueue of '{0}' failed.", updateRecord.Id)); } }
//--- Methods --- public void Dispatch(UpdateRecord updateRecord) { _log.DebugFormat("moving update record '{0}' from delay to dispatch queue", updateRecord.Id); Interlocked.Increment(ref _dispatchCount); if (!_dispatchQueue.TryEnqueue(updateRecord)) { Interlocked.Decrement(ref _dispatchCount); throw new InvalidOperationException(string.Format("Enqueue of '{0}' failed.", updateRecord.Id)); } }
private void DispatchQueued(UpdateRecord updateRecord, Action completionCallback) { _log.DebugFormat("dispatching update record '{0}'", updateRecord.Id); Coroutine.Invoke(_callback, updateRecord, new Result()).WhenDone( r => { completionCallback(); Interlocked.Decrement(ref _dispatchCount); if(r.HasException) { _log.ErrorExceptionMethodCall(r.Exception, "DispatchFromQueue", string.Format("dispatch of '{0}' encountered an error", updateRecord.Id)); } else { _log.DebugFormat("finished dispatch of update record '{0}'", updateRecord.Id); } }); }
private void DispatchQueued(UpdateRecord updateRecord, Action completionCallback) { _log.DebugFormat("dispatching update record '{0}'", updateRecord.Id); Coroutine.Invoke(_callback, updateRecord, new Result()).WhenDone( r => { completionCallback(); Interlocked.Decrement(ref _dispatchCount); if (r.HasException) { _log.ErrorExceptionMethodCall(r.Exception, "DispatchFromQueue", string.Format("dispatch of '{0}' encountered an error", updateRecord.Id)); } else { _log.DebugFormat("finished dispatch of update record '{0}'", updateRecord.Id); } }); }
//--- Methods --- public void Enqueue(XDoc meta) { XUri channel = meta["channel"].AsUri; RecordType type; int id; string path = string.Empty; string wikiid = meta["@wikiid"].AsText; if (channel.Segments[1] == "pages") { type = RecordType.Page; id = meta["pageid"].AsInt ?? 0; path = meta["path"].AsText; } else { type = RecordType.File; id = meta["fileid"].AsInt ?? 0; } string key = string.Format("{0}:{1}:{2}", wikiid, type, id); lock (_data) { UpdateRecord data; if (!_data.TryGetValue(key, out data)) { _log.DebugFormat("queueing '{0}'", key); _queue.Enqueue(new Tuplet <string, DateTime>(key, DateTime.UtcNow.Add(_delay))); data = new UpdateRecord(id, type, wikiid); _data.Add(key, data); } if (!string.IsNullOrEmpty(path)) { data.Path = path; } } }
public void Dispatch(UpdateRecord updateRecord) { Dispatches.Add(new Tuplet<DateTime, UpdateRecord>(DateTime.Now, updateRecord)); if(Dispatches.Count >= _expectedDispatches) { ResetEvent.Set(); } }
public Yield Invoke(UpdateRecord data, Result result) { Callbacks.Add(data); ResetEvent.Set(); yield break; }
private Yield OnQueueExpire(UpdateRecord data, Result result) { string regex; switch(data.Type) { case RecordType.Page: { if(string.IsNullOrEmpty(data.Path)) { Result<DreamMessage> pageResult; yield return pageResult = _deki.At("pages", data.Id.ToString()).GetAsync(); DreamMessage pageInfo = pageResult.Value; if(!pageInfo.IsSuccessful) { throw new DreamBadRequestException(string.Format("unable to fetch page for '{0}' from '{1}'", data.Id, data.WikiId)); } data.Path = pageInfo.ToDocument()["path"].AsText; } string pathIndexPhp = Title.FromUriPath(data.Path).AsUiUriPath(true); // need to purge url's like: // 1) index.php?title=Some/Page // 2) /Some/Page // 3) /@api/deki/pages/{id}/.* regex = string.Format(@"^/(({0}|{1})[\?&]?|@api/deki/pages/{2}/?).*$", Regex.Escape(data.Path), Regex.Escape(pathIndexPhp), data.Id); break; } case RecordType.File: { // need to purge url's like: // 1) /@api/deki/files/1234 // 2) /@api/deki/files/1234/=test.png // 3) /@api/deki/files/1234/=test.png?size=webview regex = string.Format(@"^/@api/deki/files/{0}/?.*$", data.Id); break; } default: result.Return(); yield break; } DreamMessage msg = new DreamMessage(DreamStatus.Ok, null, MimeType.TEXT, "dummy"); // mono requires some data, hence the "dummy" msg.Headers.Add("X-Purge-Url", regex); Result<DreamMessage> response; yield return response = _varnish.InvokeAsync("PURGE", msg); if(!response.Value.IsSuccessful) { _log.DebugFormat("failure purging: {0}", regex); } else { _log.DebugFormat("purged: {0}", regex); } result.Return(); yield break; }