/// <summary> /// Get a non running message from queue /// </summary> /// <param name="query">query where top level fields do not contain operators. Lower level fields can however. eg: valid {a: {$gt: 1}, "b.c": 3}, invalid {$and: [{...}, {...}]}</param> /// <param name="resetRunning">duration before this message is considered abandoned and will be given with another call to Get()</param> /// <param name="wait">duration to keep polling before returning null</param> /// <param name="poll">duration between poll attempts</param> /// <returns>message or null</returns> /// <exception cref="ArgumentNullException">query is null</exception> public BsonDocument Get(QueryDocument query, TimeSpan resetRunning, TimeSpan wait, TimeSpan poll) { if (query == null) throw new ArgumentNullException("query"); //reset stuck messages collection.Update( new QueryDocument { { "running", true }, { "resetTimestamp", new BsonDocument("$lte", DateTime.UtcNow) } }, new UpdateDocument("$set", new BsonDocument("running", false)), UpdateFlags.Multi ); var builtQuery = new QueryDocument("running", false); foreach (var field in query) builtQuery.Add("payload." + field.Name, field.Value); builtQuery.Add("earliestGet", new BsonDocument("$lte", DateTime.UtcNow)); var resetTimestamp = DateTime.UtcNow; try { resetTimestamp += resetRunning; } catch (ArgumentOutOfRangeException) { resetTimestamp = resetRunning > TimeSpan.Zero ? DateTime.MaxValue : DateTime.MinValue; } var sort = new SortByDocument { { "priority", 1 }, { "created", 1 } }; var update = new UpdateDocument("$set", new BsonDocument { { "running", true }, { "resetTimestamp", resetTimestamp } }); var fields = new FieldsDocument("payload", 1); var end = DateTime.UtcNow; try { end += wait; } catch (ArgumentOutOfRangeException) { end = wait > TimeSpan.Zero ? DateTime.MaxValue : DateTime.MinValue; } while (true) { var message = collection.FindAndModify(builtQuery, sort, update, fields, false, false).ModifiedDocument; if (message != null) //using merge without overwriting so a possible id in payload doesnt wipe it out the generated one return new BsonDocument("id", message["_id"]).Merge(message["payload"].AsBsonDocument); if (DateTime.UtcNow >= end) return null; try { Thread.Sleep(poll); } catch (ArgumentOutOfRangeException) { poll = poll < TimeSpan.Zero ? TimeSpan.Zero : TimeSpan.FromMilliseconds(int.MaxValue); Thread.Sleep(poll); } } }
public void TestFieldsDocumentConstructor() { var document1 = new FieldsDocument(dictionary); var document2 = new FieldsDocument(hashtable); var document3 = new FieldsDocument(idictionaryNonGeneric); var document4 = new FieldsDocument(idictionary); Assert.AreEqual("Dictionary<string, object>", document1["type"].AsString); Assert.AreEqual("Hashtable", document2["type"].AsString); Assert.AreEqual("IDictionary", document3["type"].AsString); Assert.AreEqual("IDictionary<string, object>", document4["type"].AsString); }
/// <summary> /// Get a non running message from queue /// </summary> /// <param name="query">query where top level fields do not contain operators. Lower level fields can however. eg: valid {a: {$gt: 1}, "b.c": 3}, invalid {$and: [{...}, {...}]}</param> /// <param name="resetRunning">duration before this message is considered abandoned and will be given with another call to Get()</param> /// <param name="wait">duration to keep polling before returning null</param> /// <param name="poll">duration between poll attempts</param> /// <param name="approximateWait">whether to fluctuate the wait time randomly by +-10 percent. This ensures Get() calls seperate in time when multiple Queues are used in loops started at the same time</param> /// <returns>message or null</returns> /// <exception cref="ArgumentNullException">query is null</exception> public Message Get(QueryDocument query, TimeSpan resetRunning, TimeSpan wait, TimeSpan poll, bool approximateWait) { if (query == null) throw new ArgumentNullException ("query"); //reset stuck messages collection.Update( new QueryDocument { { "running", true }, { "resetTimestamp", new BsonDocument("$lte", DateTime.UtcNow) } }, new UpdateDocument("$set", new BsonDocument("running", false)), UpdateFlags.Multi ); var builtQuery = new QueryDocument("running", false); foreach (var field in query) builtQuery.Add("payload." + field.Name, field.Value); builtQuery.Add("earliestGet", new BsonDocument("$lte", DateTime.UtcNow)); var resetTimestamp = DateTime.UtcNow; try { resetTimestamp += resetRunning; } catch (ArgumentOutOfRangeException) { resetTimestamp = resetRunning > TimeSpan.Zero ? DateTime.MaxValue : DateTime.MinValue; } var sort = new SortByDocument { { "priority", 1 }, { "created", 1 } }; var update = new UpdateDocument("$set", new BsonDocument { { "running", true }, { "resetTimestamp", resetTimestamp } }); var fields = new FieldsDocument { { "payload", 1 }, { "streams", 1 } }; var end = DateTime.UtcNow; try { if (approximateWait) //fluctuate randomly by 10 percent wait += TimeSpan.FromMilliseconds(wait.TotalMilliseconds * GetRandomDouble(-0.1, 0.1)); end += wait; } catch (Exception e) { if (!(e is OverflowException) && !(e is ArgumentOutOfRangeException)) throw e;//cant cover end = wait > TimeSpan.Zero ? DateTime.MaxValue : DateTime.MinValue; } while (true) { var findModifyArgs = new FindAndModifyArgs { Query = builtQuery, SortBy = sort, Update = update, Fields = fields, Upsert = false }; var message = collection.FindAndModify(findModifyArgs).ModifiedDocument; if (message != null) { var handleStreams = new List<KeyValuePair<BsonValue, Stream>>(); var messageStreams = new Dictionary<string, Stream>(); foreach (var streamId in message["streams"].AsBsonArray) { var fileInfo = gridfs.FindOneById(streamId); var stream = fileInfo.OpenRead(); handleStreams.Add(new KeyValuePair<BsonValue, Stream>(streamId, stream)); messageStreams.Add(fileInfo.Name, stream); } var handle = new Handle(message["_id"].AsObjectId, handleStreams); return new Message(handle, message["payload"].AsBsonDocument, messageStreams); } if (DateTime.UtcNow >= end) return null; try { Thread.Sleep(poll); } catch (ArgumentOutOfRangeException) { if (poll < TimeSpan.Zero) poll = TimeSpan.Zero; else poll = TimeSpan.FromMilliseconds(int.MaxValue); Thread.Sleep(poll); } if (DateTime.UtcNow >= end) return null; } }