public void BasicFunctionality() { using (var scheduler = new TestScheduler(threadCount: 4)) { TaskHelper taskHelper = scheduler.TaskHelper; var number = scheduler.Run(async() => { Assert.Equal(Dt(0), scheduler.Clock.GetCurrentDateTimeUtc()); await taskHelper.ConfigureAwait(scheduler.Delay(TimeSpan.FromSeconds(10), CancellationToken.None)); Assert.Equal(Dt(10), scheduler.Clock.GetCurrentDateTimeUtc()); await taskHelper.ConfigureAwait(scheduler.Delay(TimeSpan.FromSeconds(10), CancellationToken.None)); Assert.Equal(Dt(20), scheduler.Clock.GetCurrentDateTimeUtc()); int n1 = await taskHelper.ConfigureAwait(GetANumber1()); Assert.Equal(111, n1); int n2 = await taskHelper.ConfigureAwait(GetANumber2()); Assert.Equal(222, n2); Task <int>[] tasks = new Task <int> [1000]; for (int i = 0; i < tasks.Length; i++) { tasks[i] = WaitTest(scheduler, i); } await taskHelper.ConfigureAwait(taskHelper.WhenAll(tasks)); return(tasks.Sum(x => x.Result)); }); Assert.Equal(1000 * 999 / 2, number); } }
public override async Task AcknowledgeAsync(SubscriptionName subscription, IEnumerable <string> ackIds, CancellationToken cancellationToken) { if (_writeAsyncPreDelay != TimeSpan.Zero) { await _taskHelper.ConfigureAwait(_scheduler.Delay(_writeAsyncPreDelay, CancellationToken.None)); } lock (_lock) { _acks.AddRange(ackIds.Select(id => new TimedId(_clock.GetCurrentDateTimeUtc(), id))); } }
public void OrderingKeyManyMessages( [CombinatorialValues(1, 2, 5, 7)] int clientCount, [CombinatorialValues(1, 2, 6, 13)] int threadCount, [CombinatorialValues(101, 2000, 9999)] int messageCount, [CombinatorialValues(1, 2, 9, 51)] int orderingKeysCount, [CombinatorialValues(1, 5, 50)] int batchElementCountThreshold, [CombinatorialValues(0, 1, 59, 123, 1001)] int delayMs1, [CombinatorialValues(0, 2, 500)] int delayMs2) { var topicName = new TopicName("FakeProject", "FakeTopic"); var scheduler = new TestScheduler(threadCount); TaskHelper taskHelper = scheduler.TaskHelper; var clients = Enumerable.Range(0, clientCount) .Select(_ => new FakePublisherServiceApiClient(scheduler, taskHelper, new[] { TimeSpan.FromMilliseconds(delayMs1), TimeSpan.FromMilliseconds(delayMs2) })).ToArray(); var settings = MakeSettings(scheduler, batchElementCountThreshold: batchElementCountThreshold, batchRequestByteThreshold: 10000, enableMessageOrdering: true); int shutdownCount = 0; var pub = new PublisherClientImpl(topicName, clients, settings, () => { Interlocked.Increment(ref shutdownCount); return(Task.FromResult(0)); }, taskHelper); scheduler.Run(async() => { var tasks = Enumerable.Range(0, messageCount) .Select(i => pub.PublishAsync((i % orderingKeysCount).ToString(), $"{i % orderingKeysCount}:{i}")).ToArray(); var ids = new HashSet <string>(await taskHelper.ConfigureAwait(taskHelper.WhenAll(tasks))); await taskHelper.ConfigureAwait(pub.ShutdownAsync(new CancellationToken())); Assert.Equal(messageCount, ids.Count); // This doesn't check the global ordering between clients, but that's OK here. // The emulator-based integration test checks are more thorough. foreach (var client in clients) { var kv = client.HandledMessages.Select(x => x.Split(':')).Select(x => (key: x[0], value: x[1])); foreach (var values in kv.GroupBy(x => x.key, x => x.value)) { var errorMsg = $"Ordering-key '{values.Key}' out of order"; foreach (var pair in values.Zip(values.Skip(1), (a, b) => (a, b))) { Assert.True(int.Parse(pair.a) < int.Parse(pair.b), errorMsg); } } } Assert.Equal(ids, new HashSet <string>(clients.SelectMany(x => x.HandledMessages))); Assert.Equal(1, shutdownCount); }); }
public void OneMessage( [CombinatorialValues(false, true)] bool hardStop) { var topicName = new TopicName("FakeProject", "FakeTopic"); var scheduler = new TestScheduler(); TaskHelper taskHelper = scheduler.TaskHelper; var client = new FakePublisherServiceApiClient(scheduler, taskHelper); var settings = MakeSettings(scheduler); int shutdownCount = 0; var pub = new PublisherClientImpl(topicName, new[] { client }, settings, () => { Interlocked.Increment(ref shutdownCount); return(Task.FromResult(0)); }, taskHelper); scheduler.Run(async() => { var id = await taskHelper.ConfigureAwait(pub.PublishAsync("1")); bool isCancelled = await taskHelper.ConfigureAwaitHideCancellation( () => pub.ShutdownAsync(new CancellationToken(hardStop))); Assert.Equal(hardStop, isCancelled); Assert.Equal("1", id); Assert.Equal(1, shutdownCount); }); }
public void ManyMessages( [CombinatorialValues(false, true)] bool hardStop, [CombinatorialValues(1, 2, 5, 7)] int clientCount, [CombinatorialValues(1, 2, 6, 13)] int threadCount, [CombinatorialValues(101, 2000, 9999)] int messageCount, [CombinatorialValues(1, 5, 50)] int batchElementCountThreshold) { var topicName = new TopicName("FakeProject", "FakeTopic"); var scheduler = new TestScheduler(threadCount); TaskHelper taskHelper = scheduler.TaskHelper; var clients = Enumerable.Range(0, clientCount).Select(_ => new FakePublisherServiceApiClient(scheduler, taskHelper)).ToArray(); var settings = MakeSettings(scheduler, batchElementCountThreshold: batchElementCountThreshold, batchRequestByteThreshold: 10000); int shutdownCount = 0; var pub = new PublisherClientImpl(topicName, clients, settings, () => { Interlocked.Increment(ref shutdownCount); return(Task.FromResult(0)); }, taskHelper); scheduler.Run(async() => { var tasks = Enumerable.Range(0, messageCount).Select(i => pub.PublishAsync(i.ToString())).ToArray(); var ids = new HashSet <string>(await taskHelper.ConfigureAwait(taskHelper.WhenAll(tasks))); var isCancelled = await taskHelper.ConfigureAwaitHideCancellation( () => pub.ShutdownAsync(new CancellationToken(hardStop))); Assert.Equal(hardStop, isCancelled); var expected = new HashSet <string>(Enumerable.Range(0, messageCount).Select(x => x.ToString())); Assert.Equal(expected, ids); Assert.Equal(1, shutdownCount); }); }
public override async Task <PublishResponse> PublishAsync(PublishRequest request, CallSettings callSettings = null) { _delays.MoveNext(); await _taskHelper.ConfigureAwait(_schduler.Delay(_delays.Current, callSettings?.CancellationToken ?? CancellationToken.None)); var byOrderingKey = request.Messages.GroupBy(x => x.OrderingKey).ToList(); if (byOrderingKey.Count > 1) { throw new InvalidOperationException("Multiple ordering-keys should not be present within a single batch."); } var msgIds = request.Messages.Select(x => x.Data.ToStringUtf8()); lock (_lock) { if (byOrderingKey.Count > 0 && byOrderingKey[0].Key == _orderingKeyErrorUnrecoverable) { // Cause a one-off unrecoverable error. _orderingKeyErrorUnrecoverable = null; throw new RpcException(new Status(StatusCode.DataLoss, "Data loss")); } if (byOrderingKey.Count > 0 && byOrderingKey[0].Key == _orderingKeyErrorRecoverable) { // Cause a one-off recoverable error. _orderingKeyErrorRecoverable = null; throw new RpcException(new Status(StatusCode.Unavailable, "Unavailable")); } _handledIds.AddRange(msgIds); } return(new PublishResponse { MessageIds = { msgIds } }); }
public override async Task<PublishResponse> PublishAsync(PublishRequest request, CallSettings callSettings = null) { _delays.MoveNext(); await _taskHelper.ConfigureAwait(_schduler.Delay(_delays.Current, callSettings?.CancellationToken ?? CancellationToken.None)); var msgIds = request.Messages.Select(x => x.Data.ToStringUtf8()); lock (_lock) { _handledIds.AddRange(msgIds); } return new PublishResponse { MessageIds = { msgIds } }; }
public override async Task WriteAsync(StreamingPullRequest message) { await _taskHelper.ConfigureAwait(_scheduler.Delay(_writeAsyncPreDelay, CancellationToken.None)); var now = _clock.GetCurrentDateTimeUtc(); lock (_lock) { _extends.AddRange(message.ModifyDeadlineAckIds.Select(id => new TimedId(now, id)).ToList()); _acks.AddRange(message.AckIds.Select(id => new TimedId(now, id)).ToList()); } }
public void WhenAll([CombinatorialValues(1, 2, 3)] int threadCount) { using (var scheduler = new TestScheduler(threadCount: threadCount)) { TaskHelper th = scheduler.TaskHelper; scheduler.Run(async() => { var tcs1 = new TaskCompletionSource <int>(); var tcs2 = new TaskCompletionSource <int>(); tcs1.SetResult(0); Task delayTask = th.Run(async() => { await th.ConfigureAwait(scheduler.Delay(TimeSpan.FromSeconds(1), CancellationToken.None)); tcs2.SetResult(0); }); await th.ConfigureAwait(th.WhenAll(tcs1.Task, tcs2.Task)); await th.ConfigureAwait(delayTask); // Test passes if no exception is thrown. }); } }
public void FlowState() { var topicName = new TopicName("FakeProject", "FakeTopic"); var scheduler = new TestScheduler(); TaskHelper taskHelper = scheduler.TaskHelper; var client = new FakePublisher(scheduler, taskHelper, TimeSpan.FromSeconds(1)); var settings = MakeSettings(scheduler); var pub = new SimplePublisherImpl(topicName, new[] { client }, settings, null, taskHelper); var msgSize = new PubsubMessage { Data = ByteString.CopyFromUtf8("1") }.CalculateSize(); scheduler.Run(async () => { // Publish 2 msgs; 1st will be immediately sent, 2nd will stay in queue for 1 second. var pubTask = Task.WhenAll(pub.PublishAsync("1"), pub.PublishAsync("2")); Assert.Equal(1, pub.GetCurrentFlowState().ElementCount); Assert.Equal(msgSize, pub.GetCurrentFlowState().ByteCount); await taskHelper.ConfigureAwait(pubTask); Assert.Equal(0, pub.GetCurrentFlowState().ElementCount); Assert.Equal(0, pub.GetCurrentFlowState().ByteCount); }); }
public void PublishingMessageWithOrderingKeyRequiresOrderingEnabled() { var topicName = new TopicName("FakeProject", "FakeTopic"); var scheduler = new TestScheduler(); TaskHelper taskHelper = scheduler.TaskHelper; var client = new FakePublisherServiceApiClient(scheduler, taskHelper); var settings = MakeSettings(scheduler); int shutdownCount = 0; var pub = new PublisherClientImpl(topicName, new[] { client }, settings, () => { Interlocked.Increment(ref shutdownCount); return(Task.FromResult(0)); }, taskHelper); scheduler.Run(async() => { await taskHelper.ConfigureAwait( Assert.ThrowsAsync <InvalidOperationException>(() => pub.PublishAsync("an ordering key", "1"))); }); }
public void OrderingKeyResumePublish() { const string unrecoverableKey = "error-unrecoverable"; const string recoverableKey = "error-recoverable"; var topicName = new TopicName("FakeProject", "FakeTopic"); var scheduler = new TestScheduler(); TaskHelper taskHelper = scheduler.TaskHelper; var client = new FakePublisherServiceApiClient(scheduler, taskHelper, orderingKeyErrorUnrecoverable: unrecoverableKey, orderingKeyErrorRecoverable: recoverableKey); var settings = MakeSettings(scheduler, enableMessageOrdering: true); int shutdownCount = 0; var pub = new PublisherClientImpl(topicName, new[] { client }, settings, () => { Interlocked.Increment(ref shutdownCount); return(Task.FromResult(0)); }, taskHelper); scheduler.Run(async() => { // First call will trigger an unrecoverable error. var ex = await taskHelper.ConfigureAwait( Assert.ThrowsAsync <RpcException>(() => pub.PublishAsync(unrecoverableKey, "unrecoverable error"))); Assert.Equal(StatusCode.DataLoss, ex.StatusCode); // Sending again will reject the message. await taskHelper.ConfigureAwait( Assert.ThrowsAsync <OrderingKeyInErrorStateException>(() => pub.PublishAsync(unrecoverableKey, "key in error state"))); // Other ordering-keys publish OK. await taskHelper.ConfigureAwait(pub.PublishAsync("ok-key", "ok")); // Including a recoverable error. await taskHelper.ConfigureAwait(pub.PublishAsync(recoverableKey, "recoverable error")); // Including a message without an ordering key. await taskHelper.ConfigureAwait(pub.PublishAsync("key not ordered")); // Resume publishing on the ordering key. pub.ResumePublish(unrecoverableKey); await taskHelper.ConfigureAwait(pub.PublishAsync(unrecoverableKey, "unrecoverable key resumed")); var expected = new HashSet <string>(new[] { "ok", "key not ordered", "recoverable error", "unrecoverable key resumed" }); Assert.Equal(expected, new HashSet <string>(client.HandledMessages)); }); }
public void Run(Func <Task> taskProvider) => Run(async() => { await TaskHelper.ConfigureAwait(taskProvider()); return(0); });