public void ReleaseUnheldLock() { var redisTaskFunnel = CreateRedisTaskFunnel(); var msgValue = Guid.NewGuid().ToByteArray(); var pipeInfo = PipeInfo.Create("unheld", "a"); redisTaskFunnel.DestroyChildPipe(pipeInfo); var redisPipeValue = new RedisPipeValue(pipeInfo, msgValue, "asdf", true); //indicating a success does nothing if the hash doesn't exist redisTaskFunnel.AfterExecute(redisPipeValue, true); var readMessage = redisTaskFunnel.TryReadMessageBatch(false, pipeInfo, TimeSpan.FromMinutes(1), 1); readMessage.RedisValues.Should().BeEmpty(); //indicating a failure resubmits the message redisTaskFunnel.AfterExecute(redisPipeValue, false); readMessage = redisTaskFunnel.TryReadMessageBatch(false, pipeInfo, TimeSpan.FromMinutes(1), 1); readMessage.Should().NotBeNull(); var mesg = readMessage.RedisPipeValues.FirstOrDefault(); mesg?.Value.Should().BeEquivalentTo(msgValue); redisTaskFunnel.DestroyChildPipe(pipeInfo); redisTaskFunnel.AfterExecuteBatch(readMessage); }
private static void SendReadAndRelease(IRedisTaskFunnel redisTaskFunnel, string parentPipeName, string childPipeName) { var sent = redisTaskFunnel.TrySendMessage(parentPipeName, childPipeName, new byte[1] { (byte)'a' }, Int32.MaxValue, TimeSpan.FromMinutes(1)); sent.sent.Should().BeTrue(); sent.clients.Should().BeFalse(); var read = redisTaskFunnel.TryReadMessage <byte[]>(true, parentPipeName, childPipeName, TimeSpan.FromSeconds(1)); read.Should().NotBeNull(); read.LockValue.Should().NotBeNull(); read.Value.Should().NotBeNull(); //try to release the lock without the correct key var redisPipeValue = new RedisPipeValue <byte[]>(PipeInfo.Create(parentPipeName, childPipeName), "", Guid.NewGuid().ToString(), true); var badExtend = redisTaskFunnel.LockExtend(redisPipeValue, TimeSpan.FromSeconds(1)); badExtend.Should().BeFalse(); Action badRelease = () => redisTaskFunnel.LockRelease(redisPipeValue, true); badRelease.Should().Throw <ApplicationException>(); var extended = redisTaskFunnel.LockExtend(read, TimeSpan.FromSeconds(1)); extended.Should().BeTrue(); var released = redisTaskFunnel.LockRelease(read, true); released.Should().BeTrue(); }
private void ExecuteExisting(TimeSpan lockExpiry) { var sourcePipes = GetSourcePipes(); var tasks = new List <Task>(); foreach (var sourcePipe in sourcePipes) { foreach (var sourcePipeChild in sourcePipe.Value) { var pipeInfo = PipeInfo.Create(sourcePipe.Key, sourcePipeChild); //capture the number of reads write now to ignore: // - new events that arrive // - events that fail and are re-added. var maxReads = _taskFunnel.GetListLength(pipeInfo); var batchSize = 2; var messageBatch = _taskFunnel.TryReadMessageBatch(true, pipeInfo, lockExpiry, batchSize); //this is still an early implementation. The objectives are: // - speed (obviously) // - consistent number of active tasks. // - batches released as soon as the last one completes while (messageBatch?.HoldingList != null) { var taskExecutor = _taskExecutors[sourcePipe.Key]; ExecuteBatch(taskExecutor, pipeInfo, messageBatch); messageBatch = _taskFunnel.TryReadMessageBatch(true, pipeInfo, lockExpiry, batchSize); } } } }
public void ReadFromNonExistantOrEmptyQueue() { var redisTaskFunnel = CreateRedisTaskFunnel(); var read = redisTaskFunnel.TryReadMessageBatch(true, PipeInfo.Create("emptyqueue", "emptypipe"), TimeSpan.FromSeconds(10), 1); read.HoldingList.Should().BeNull(); read.RedisValues.Should().BeEmpty(); }
public void ReleaseLockExtend() { var redisTaskFunnel = CreateRedisTaskFunnel(); var lockKey = Guid.NewGuid().ToString(); var redisPipeValue = new RedisPipeValue <byte[]>(PipeInfo.Create("lock", "a"), lockKey, "asdf", true); var extended = redisTaskFunnel.LockExtend(redisPipeValue, TimeSpan.FromMinutes(1)); extended.Should().BeFalse(); }
public void ReleaseUnheldLock() { var redisTaskFunnel = CreateRedisTaskFunnel(); var lockKey = Guid.NewGuid().ToString(); var redisPipeValue = new RedisPipeValue <byte[]>(PipeInfo.Create("unheld", "a"), lockKey, "asdf", true); Action act = () => redisTaskFunnel.LockRelease(redisPipeValue, true); act.Should().Throw <ApplicationException>(); }
public void ReleaseLockExtend() { var redisTaskFunnel = CreateRedisTaskFunnel(); var lockKey = Guid.NewGuid().ToString(); var pipeInfo = PipeInfo.Create("lock", "a"); var redisPipeValue = new RedisPipeValue(pipeInfo, lockKey, "asdf", true); var extended = redisTaskFunnel.RetainHoldingList(redisPipeValue, TimeSpan.FromMinutes(1)); extended.Should().BeFalse(); redisTaskFunnel.DestroyChildPipe(pipeInfo); }
public void ListenForPipeEvents(/*IEnumerable<string> parentPipeNames,*/ BlockingCollection <PipeInfo> pipeInfos) { var sub = _redis.GetSubscriber(); //foreach (var parentPipeName in parentPipeNames){} sub.Subscribe(RedisTaskMultiplexorConstants.RedisTaskMultiplexorBroadcastPrefix, (channel, value) => { var eventObject = JObject.Parse(value); //$"{{\"type\":\"new\",\"parent\":\"{parentPipeName}\",\"child\":\"{childPipeName}\"}}"); var parent = eventObject["parent"].ToString(); var child = eventObject["child"].ToString(); var eventPipeInfo = PipeInfo.Create(parent, child); pipeInfos.Add(eventPipeInfo); }); }
public void TestSendAndRecover() { var redisTaskFunnel = CreateRedisTaskFunnel(); var parentPipeName = "SendAndRecover"; var childPipeName = Guid.NewGuid().ToString(); //send 2 messages var messageBody1 = "body1"; var sent = redisTaskFunnel.TrySendMessage(parentPipeName, childPipeName, messageBody1, Int32.MaxValue, TimeSpan.FromMinutes(1)); sent.sent.Should().BeTrue(); var messageBody2 = "body2"; sent = redisTaskFunnel.TrySendMessage(parentPipeName, childPipeName, messageBody2, Int32.MaxValue, TimeSpan.FromMinutes(1)); sent.sent.Should().BeTrue(); //sent.clients.Should().BeFalse(); //read the batch var pipeInfo = PipeInfo.Create(parentPipeName, childPipeName); var read = redisTaskFunnel.TryReadMessageBatch(true, pipeInfo, TimeSpan.FromSeconds(1), 2); read.Should().NotBeNull(); read.HoldingList.Should().NotBeNull(); read.RedisValues.Count.Should().Be(2); read.RedisValues.First().HasValue.Should().BeTrue(); read.RedisValues.Skip(1).First().HasValue.Should().BeTrue(); read.RedisPipeValues.Any(a => a.ValueString == messageBody1).Should().BeTrue(); read.RedisPipeValues.Any(a => a.ValueString == messageBody2).Should().BeTrue(); //recover batch (redeliver its messages) redisTaskFunnel.RecoverBatch(read.HoldingList).Should().BeTrue(); read = redisTaskFunnel.TryReadMessageBatch(true, pipeInfo, TimeSpan.FromSeconds(1), 2); read.Should().NotBeNull(); read.HoldingList.Should().NotBeNull(); read.RedisValues.Count.Should().Be(2); read.RedisValues.First().HasValue.Should().BeTrue(); read.RedisValues.Skip(1).First().HasValue.Should().BeTrue(); var actualRedisPipeValue = read.RedisPipeValues.First(); read.RedisPipeValues.Any(a => a.ValueString == messageBody1).Should().BeTrue(); read.RedisPipeValues.Any(a => a.ValueString == messageBody2).Should().BeTrue(); }
private static void SendReadAndRelease(IRedisTaskFunnel redisTaskFunnel, string parentPipeName, string childPipeName) { //send a message var messageBody = "body"; var sent = redisTaskFunnel.TrySendMessage(parentPipeName, childPipeName, messageBody, Int32.MaxValue, TimeSpan.FromMinutes(1)); sent.sent.Should().BeTrue(); //sent.clients.Should().BeFalse(); //read the batch var read = redisTaskFunnel.TryReadMessageBatch(true, PipeInfo.Create(parentPipeName, childPipeName), TimeSpan.FromSeconds(1), 1); read.Should().NotBeNull(); read.HoldingList.Should().NotBeNull(); read.RedisValues.Should().NotBeEmpty(); read.RedisValues.First().HasValue.Should().BeTrue(); var actualRedisPipeValue = read.RedisPipeValues.First(); actualRedisPipeValue.ValueString.Should().Be(messageBody); //try to release the lock without the wrong holdingListName var redisPipeValue = new RedisPipeValue(PipeInfo.Create(parentPipeName, childPipeName), "body", Guid.NewGuid().ToString(), true); var badExtend = redisTaskFunnel.RetainHoldingList(redisPipeValue, TimeSpan.FromSeconds(1)); badExtend.Should().BeFalse(); redisTaskFunnel.AfterExecute(redisPipeValue, true); //retain with the correct name var extended = redisTaskFunnel.RetainHoldingList(read, TimeSpan.FromSeconds(1)); extended.Should().BeTrue(); //complete the message and the batch redisTaskFunnel.AfterExecute(actualRedisPipeValue, true); redisTaskFunnel.AfterExecuteBatch(read); //now check the holding list doesn't exist any more. extended = redisTaskFunnel.RetainHoldingList(read, TimeSpan.FromSeconds(1)); extended.Should().BeFalse(); }
public (bool sent, bool clients) TrySendMessage(string parentPipeName, string childPipeName, object body, int maxListLength = int.MaxValue, TimeSpan?expiry = null) { if (body == null) { throw new ArgumentNullException(nameof(body)); } //will throw ArgumentException is body is not a supported type var redisValue = RedisValue.Unbox(body); var db = _redis.GetDatabase(); var parentInfoPath = CreateParentChildSetPath(parentPipeName); var childPipePath = PipeInfo.Create(parentPipeName, childPipeName); var trans = db.CreateTransaction(); { if (maxListLength < int.MaxValue) { trans.AddCondition(Condition.ListLengthLessThan(childPipePath.PipePath, maxListLength)); } //ensure the name of the new pipe exists for the pipe monitor (before checking list length) db.SetAdd(RedisTaskMultiplexorConstants.PipeNameSetKey, parentPipeName); //add the child to the parents hash set (and update the expiry time on it) db.HashSet(parentInfoPath, childPipeName, DateConverters.ExpiryToTimeString(expiry ?? TimeSpan.FromDays(7))); //add the message to the left of the list, and is later popped from the right. db.ListLeftPush(childPipePath.PipePath, redisValue); } var executed = trans.Execute(); if (!executed) { return(false, false); } var sub = _redis.GetSubscriber(); var listeners = sub.Publish(childPipePath.BroadcastPath, $"{{\"type\":\"new\",\"parent\":\"{parentPipeName}\",\"child\":\"{childPipeName}\"}}"); return(true, listeners > 0); }