public async Task MultipleCallsEventualSuccess(bool async, bool serverStreaming) { var callDuration = TimeSpan.FromTicks(300); var failures = 4; // Fifth call will succeed var name = "name"; // Copied from request to response var scheduler = new FakeScheduler(); var time0 = scheduler.Clock.GetCurrentDateTimeUtc(); var server = new Server(failures, callDuration, scheduler); var retrySettings = new RetrySettings( retryBackoff: DoublingBackoff, timeoutBackoff: ConstantBackoff, totalExpiration: Expiration.FromTimeout(TimeSpan.FromSeconds(1)), retryFilter: null, delayJitter: RetrySettings.NoJitter); await scheduler.RunAsync(async() => { var callSettings = CallSettings.FromCallTiming(CallTiming.FromRetry(retrySettings)); var request = new SimpleRequest { Name = name }; var result = await Call(async, serverStreaming, scheduler, server, request, callSettings); Assert.Equal(name, result.Name); }); var firstCall = time0; var secondCall = firstCall + callDuration + TimeSpan.FromTicks(1000); // Delay for 1000 ticks var thirdCall = secondCall + callDuration + TimeSpan.FromTicks(2000); // Delay for 2000 ticks var fourthCall = thirdCall + callDuration + TimeSpan.FromTicks(4000); // Delay for 4000 ticks var fifthCall = fourthCall + callDuration + TimeSpan.FromTicks(5000); // Delay for 5000 ticks, as that's the max server.AssertCallTimes(firstCall, secondCall, thirdCall, fourthCall, fifthCall); // Time of last action was when the call returned Assert.Equal(fifthCall + callDuration, scheduler.Clock.GetCurrentDateTimeUtc()); }
public async Task RetryFilter_EventualSuccess(bool async, StatusCode failureCode, StatusCode[] filterCodes) { var callDuration = 100; var failures = 1; var scheduler = new FakeScheduler(); var server = new Server(failures, callDuration, scheduler, failureCode); // We're not really interested in the timing in this test. var retrySettings = new RetrySettings( retryBackoff: ConstantBackoff, timeoutBackoff: ConstantBackoff, delayJitter: RetrySettings.NoJitter, totalExpiration: Expiration.FromTimeout(TimeSpan.FromSeconds(1)), retryFilter: RetrySettings.FilterForStatusCodes(filterCodes)); await scheduler.RunAsync(async() => { var callSettings = CallSettings.FromCallTiming(CallTiming.FromRetry(retrySettings)); var retryingCallable = server.Callable.WithRetry(scheduler.Clock, scheduler); await Call(async, retryingCallable, new SimpleRequest { Name = "irrelevant" }, callSettings); }); Assert.True(server.CallTimes.Count() > 1); }
private async Task process() { InitializeQueue(); var response = await _sub.PullAsync(_subscriptionName, false, 1, CallSettings.FromCallTiming( CallTiming.FromExpiration( Expiration.FromTimeout( TimeSpan.FromSeconds(90))))); if (response.ReceivedMessages == null || response.ReceivedMessages.Count == 0) { return; } var message = response.ReceivedMessages[0]; var jsonBytes = message.Message.Data.ToByteArray(); var payload = Encoding.UTF8.GetString(jsonBytes); _appLogger.LogMessage($"Message Type: {typeof(T).FullName} dequeued with MessageId: {message.Message.MessageId}"); _messageHandler(payload); await _sub.AcknowledgeAsync(_subscriptionName, new string[] { message.AckId }); }
public async Task CallSettingsDeadlineIsObserved(bool async, bool serverStreaming) { var callDuration = TimeSpan.FromTicks(300); var failures = 4; // Fifth call would succeed, but we won't get that far. var scheduler = new FakeScheduler(); var time0 = scheduler.Clock.GetCurrentDateTimeUtc(); var server = new Server(failures, callDuration, scheduler); var callable = server.Callable; var retrySettings = new RetrySettings( retryBackoff: DoublingBackoff, timeoutBackoff: ConstantBackoff, totalExpiration: Expiration.FromTimeout(TimeSpan.FromTicks(2500)), retryFilter: null, delayJitter: RetrySettings.NoJitter); var task = scheduler.RunAsync(async() => { // Expiration makes it fail while waiting to make third call var callSettings = CallSettings.FromCallTiming(CallTiming.FromRetry(retrySettings)); var request = new SimpleRequest { Name = "irrelevant" }; await Call(async, serverStreaming, scheduler, server, request, callSettings); }); await Assert.ThrowsAsync <RpcException>(() => task); var firstCall = time0; var secondCall = firstCall + callDuration + TimeSpan.FromTicks(1000); server.AssertCallTimes(firstCall, secondCall); // We fail immediately when we work out that we would time out before we make the third // call - so this is before the actual total timeout. Assert.Equal((secondCall + callDuration).Ticks, scheduler.Clock.GetCurrentDateTimeUtc().Ticks); }
/// <summary> /// Creates a service context that binds the service, callsettings and the client. /// </summary> /// <param name="config">The configuration.</param> /// <returns>The service context.</returns> private GoogleAdsServiceContext CreateServiceContext(GoogleAdsConfig config) { GoogleAdsServiceContext serviceContext = new GoogleAdsServiceContext(); CallSettings callSettings = CallSettings.FromCallTiming( CallTiming.FromRetry(new RetrySettings( retryBackoff : backoffSettings, timeoutBackoff : backoffSettings, totalExpiration : Expiration.FromTimeout(TimeSpan.FromMilliseconds( config.Timeout)), retryFilter : retryFilter ))) .WithHeader(GoogleAdsConfig.DEVELOPER_TOKEN_KEYNAME, config.DeveloperToken) .WithResponseMetadataHandler(delegate(Metadata metadata) { GoogleAdsResponseMetadata responseMetadata = new GoogleAdsResponseMetadata(metadata); serviceContext.OnResponseMetadataReceived(responseMetadata); }); if (!string.IsNullOrEmpty(config.LoginCustomerId)) { callSettings.WithHeader("login-customer-id", config.LoginCustomerId); } serviceContext.CallSettings = callSettings; return(serviceContext); }
public async Task ExponentialTimeouts(bool async, bool serverStreaming) { var callDuration = TimeSpan.FromTicks(300); var failures = 2; var scheduler = new FakeScheduler(); var time0 = scheduler.Clock.GetCurrentDateTimeUtc(); var server = new Server(failures, callDuration, scheduler); var callable = server.Callable; var retrySettings = new RetrySettings( retryBackoff: ConstantBackoff, // 1500 ticks always timeoutBackoff: DoublingBackoff, // 1000, then 2000, then 4000 totalExpiration: Expiration.FromTimeout(TimeSpan.FromTicks(4500)), retryFilter: null, delayJitter: RetrySettings.NoJitter); await scheduler.RunAsync(async() => { // Expiration truncates the third timeout. We expect: // Call 1: t=0, deadline=1000, completes at 300 // Call 2: t=1800, deadline=3800 (2000+1800), completes at 2100 // Call 3, t=3600, deadline=4500 (would be 7600, but overall deadline truncates), completes at 3900 (with success) var callSettings = CallSettings.FromCallTiming(CallTiming.FromRetry(retrySettings)); var request = new SimpleRequest { Name = "irrelevant" }; await Call(async, serverStreaming, scheduler, server, request, callSettings); }); server.AssertCallTimes(time0, time0 + TimeSpan.FromTicks(1800), time0 + TimeSpan.FromTicks(3600)); server.AssertDeadlines(time0 + TimeSpan.FromTicks(1000), time0 + TimeSpan.FromTicks(3800), time0 + TimeSpan.FromTicks(4500)); Assert.Equal(3900L, scheduler.Clock.GetCurrentDateTimeUtc().Ticks); }
public void test() { LanguageServiceSettings settings = new LanguageServiceSettings(); if (TimeOut > 0) { int hour = TimeOut / 3600; int min = TimeOut / 60; if (TimeOut >= 60) { TimeOut = TimeOut % 60; } TimeSpan ts0 = new TimeSpan(hour, min, TimeOut); settings.AnalyzeSentimentSettings = CallSettings.FromCallTiming(CallTiming.FromTimeout(ts0)); } string json = "{\"type\": \"service_account\",\"project_id\": \"" + Config["project_id"] + "\",\"private_key_id\": \"" + Config["private_key_id"] + "\",\"private_key\": \"" + Config["private_key"] + "\",\"client_email\": \"" + Config["client_email"] + "\",\"client_id\": \"" + Config["client_id"] + "\",\"auth_uri\": \"" + Config["auth_uri"] + "\",\"token_uri\": \"" + Config["token_uri"] + "\",\"auth_provider_x509_cert_url\": \"" + Config["auth_provider_x509_cert_url"] + "\",\"client_x509_cert_url\": \"" + Config["client_x509_cert_url"] + "\"}"; var credential = GoogleCredential.FromJson(json).CreateScoped(LanguageServiceClient.DefaultScopes); var channel = new Grpc.Core.Channel( LanguageServiceClient.DefaultEndpoint.ToString(), credential.ToChannelCredentials()); LanguageServiceClient test = LanguageServiceClient.Create(channel, settings); var Sentimentresponse = test.AnalyzeSentiment(new Document() { Content = "hello", Type = Document.Types.Type.PlainText }); }
public async Task RetryFilter_EventualSuccess(bool async, bool serverStreaming) { StatusCode failureCode = StatusCode.NotFound; StatusCode[] filterCodes = new[] { StatusCode.NotFound, StatusCode.DeadlineExceeded }; var callDuration = TimeSpan.FromTicks(100); var failures = 1; var scheduler = new FakeScheduler(); var server = new Server(failures, callDuration, scheduler, failureCode); // We're not really interested in the timing in this test. var retrySettings = new RetrySettings( retryBackoff: ConstantBackoff, timeoutBackoff: ConstantBackoff, delayJitter: RetrySettings.NoJitter, totalExpiration: Expiration.FromTimeout(TimeSpan.FromSeconds(1)), retryFilter: RetrySettings.FilterForStatusCodes(filterCodes)); await scheduler.RunAsync(async() => { var callSettings = CallSettings.FromCallTiming(CallTiming.FromRetry(retrySettings)); var request = new SimpleRequest { Name = "irrelevant" }; await Call(async, serverStreaming, scheduler, server, request, callSettings); }); Assert.True(server.CallTimes.Count() > 1); }
public async Task RetryFilter_EventualFailure(bool async, bool serverStreaming, StatusCode failureCode, StatusCode[] filterCodes) { var callDuration = TimeSpan.FromTicks(100); var failures = 1; var scheduler = new FakeScheduler(); var server = new Server(failures, callDuration, scheduler, failureCode); // We're not really interested in the timing in this test. var retrySettings = new RetrySettings( retryBackoff: ConstantBackoff, timeoutBackoff: ConstantBackoff, delayJitter: RetrySettings.NoJitter, totalExpiration: Expiration.FromTimeout(TimeSpan.FromSeconds(1)), retryFilter: RetrySettings.FilterForStatusCodes(filterCodes)); var task = scheduler.RunAsync(async() => { var callSettings = CallSettings.FromCallTiming(CallTiming.FromRetry(retrySettings)); var request = new SimpleRequest { Name = "irrelevant" }; await Call(async, serverStreaming, scheduler, server, request, callSettings); }); await Assert.ThrowsAsync <RpcException>(() => task); Assert.Equal(1, server.CallTimes.Count()); }
public async Task RetryCancellation(bool serverStreaming, [CombinatorialValues(1500, 3500)] int delayMs) { // Note: Cannot test cancellation during wait for response header, due to FakeScheduler shortcomings. var async = true; var scheduler = new FakeScheduler(); var time0 = scheduler.Clock.GetCurrentDateTimeUtc(); var server = new Server(10, TimeSpan.FromSeconds(1), scheduler); var retrySettings = new RetrySettings( retryBackoff: new BackoffSettings(TimeSpan.FromSeconds(1), TimeSpan.FromSeconds(1), 1.0), timeoutBackoff: new BackoffSettings(TimeSpan.FromSeconds(1), TimeSpan.FromSeconds(1), 1.0), delayJitter: RetrySettings.NoJitter, totalExpiration: Expiration.FromTimeout(TimeSpan.FromSeconds(10)), retryFilter: RetrySettings.DefaultFilter); var delay = TimeSpan.FromMilliseconds(delayMs); Task task = scheduler.RunAsync(async() => { var cts = new CancellationTokenSource(); var unused = Task.Run(async() => { await scheduler.Delay(delay); cts.Cancel(); }); var callSettings = CallSettings.FromCallTiming(CallTiming.FromRetry(retrySettings)).WithCancellationToken(cts.Token); var request = new SimpleRequest { Name = "irrelevant" }; await Call(async, serverStreaming, scheduler, server, request, callSettings); }); await Assert.ThrowsAsync <TaskCanceledException>(() => task); Assert.Equal(time0 + delay, scheduler.Clock.GetCurrentDateTimeUtc()); }
public async Task FirstCallSucceeds(bool async, bool serverStreaming) { var name = "name"; // Copied from request to response var scheduler = new FakeScheduler(); var time0 = scheduler.Clock.GetCurrentDateTimeUtc(); var server = new Server(0, TimeSpan.FromTicks(300), scheduler); var retrySettings = new RetrySettings( retryBackoff: DoublingBackoff, timeoutBackoff: ConstantBackoff, totalExpiration: Expiration.FromTimeout(TimeSpan.FromSeconds(1)), retryFilter: null, delayJitter: RetrySettings.NoJitter); await scheduler.RunAsync(async() => { var callSettings = CallSettings.FromCallTiming(CallTiming.FromRetry(retrySettings)); var request = new SimpleRequest { Name = name }; var result = await Call(async, serverStreaming, scheduler, server, request, callSettings); Assert.Equal(name, result.Name); }); server.AssertCallTimes(time0); // Time of last action was when the call returned Assert.Equal(300, scheduler.Clock.GetCurrentDateTimeUtc().Ticks); }
// [START monitoring_uptime_check_create] public static object CreateUptimeCheck(string projectId, string hostName, string displayName) { // Define a new config. var config = new UptimeCheckConfig() { DisplayName = displayName, MonitoredResource = new MonitoredResource() { Type = "uptime_url", Labels = { { "host", hostName } } }, HttpCheck = new UptimeCheckConfig.Types.HttpCheck() { Path = "/", Port = 80, }, Timeout = TimeSpan.FromSeconds(10).ToDuration(), Period = TimeSpan.FromMinutes(5).ToDuration() }; // Create a client. var client = UptimeCheckServiceClient.Create(); string projectName = new ProjectName(projectId).ToString(); // Create the config. var newConfig = client.CreateUptimeCheckConfig(projectName, config, CallSettings.FromCallTiming(CallTiming.FromTimeout(TimeSpan.FromMinutes(2)))); Console.WriteLine(newConfig.Name); return(0); }
public void FromCallTiming() { Assert.Null(CallSettings.FromCallTiming(null)); var timing = CallTiming.FromExpiration(Expiration.None); var settings = CallSettings.FromCallTiming(timing); Assert.Same(timing, settings.Timing); }
public void SucceedWithExpiration() { var apiCall = ApiServerStreamingCall.Create <int, int>( (request, callOptions) => null, CallSettings.FromCallTiming(CallTiming.FromExpiration(Expiration.FromTimeout(TimeSpan.FromSeconds(100)))), new FakeClock()); Assert.Null(apiCall.Call(0, null)); }
public void ToCallOptions_CallTimingNull() { var mockClock = new Mock <IClock>(); CallSettings callSettings = CallSettings.FromCallTiming(null); var options = callSettings.ToCallOptions(mockClock.Object); Assert.Null(options.Deadline); mockClock.Verify(c => c.GetCurrentDateTimeUtc(), Times.Never); }
public void SucceedWithExpiration() { var apiCall = ApiBidirectionalStreamingCall.Create <int, int>( callOptions => null, CallSettings.FromCallTiming(CallTiming.FromExpiration(Expiration.FromTimeout(TimeSpan.FromSeconds(100)))), new BidirectionalStreamingSettings(100), new FakeClock()); Assert.Null(apiCall.Call(null)); }
public void ToCallOptions_CallTimingExpirationTimeout() { var clock = new FakeClock(); var timeout = TimeSpan.FromSeconds(1); CallSettings callSettings = CallSettings.FromCallTiming(CallTiming.FromExpiration(Expiration.FromTimeout(timeout))); var options = callSettings.ToCallOptions(clock); // Value should be exact, as we control time precisely. Assert.Equal(options.Deadline.Value, clock.GetCurrentDateTimeUtc() + timeout); }
public void ToCallOptions_CallTimingExpirationDeadline() { var deadline = new DateTime(2015, 6, 19, 5, 2, 3, DateTimeKind.Utc); var mockClock = new Mock <IClock>(); CallSettings callSettings = CallSettings.FromCallTiming(CallTiming.FromExpiration(Expiration.FromDeadline(deadline))); var options = callSettings.ToCallOptions(mockClock.Object); // Value should be exact, as we control time precisely. Assert.Equal(options.Deadline.Value, deadline); mockClock.Verify(c => c.GetCurrentDateTimeUtc(), Times.Never); }
public StackDriverLogger(string projectId) { _projectId = projectId; _retryAWhile = CallSettings.FromCallTiming(CallTiming.FromRetry(new RetrySettings( new BackoffSettings(TimeSpan.FromSeconds(3), TimeSpan.FromSeconds(12), 2.0), new BackoffSettings(TimeSpan.FromSeconds(30), TimeSpan.FromSeconds(120)), Google.Api.Gax.Expiration.FromTimeout(TimeSpan.FromSeconds(180)), (Grpc.Core.RpcException e) => new[] { Grpc.Core.StatusCode.Internal, Grpc.Core.StatusCode.DeadlineExceeded } .Contains(e.Status.StatusCode) ))); }
public void FailWithRetry() { var apiCall = ApiServerStreamingCall.Create <int, int>( (request, callOptions) => null, CallSettings.FromCallTiming(CallTiming.FromRetry(new RetrySettings( new BackoffSettings(TimeSpan.FromSeconds(10), TimeSpan.FromSeconds(100), 2.0), new BackoffSettings(TimeSpan.FromSeconds(10), TimeSpan.FromSeconds(100), 2.0), Expiration.FromTimeout(TimeSpan.FromSeconds(100))))), new FakeClock()); Assert.Throws <InvalidOperationException>(() => apiCall.Call(0, null)); }
/// <inheritdoc /> public async Task <SpannerClient> CreateClientAsync(ServiceEndpoint endpoint, ITokenAccess credential, IDictionary additionalOptions) { ChannelCredentials channelCredentials; var allowImmediateTimeout = false; if (additionalOptions.Contains(nameof(SpannerSettings.AllowImmediateTimeouts))) { allowImmediateTimeout = Convert.ToBoolean(additionalOptions[nameof(SpannerSettings.AllowImmediateTimeouts)]); } if (credential == null) { channelCredentials = await CreateDefaultChannelCredentialsAsync().ConfigureAwait(false); } else { channelCredentials = credential.ToChannelCredentials(); } var channel = new Channel( endpoint.Host, endpoint.Port, channelCredentials); Logger.LogPerformanceCounterFn("SpannerClient.RawCreateCount", x => x + 1); //Pull the timeout from spanner options. //The option must be set before OpenAsync is called. var idempotentCallSettings = CallSettings.FromCallTiming( CallTiming.FromRetry( new RetrySettings( SpannerSettings.GetDefaultRetryBackoff(), SpannerSettings.GetDefaultTimeoutBackoff(), SpannerSettings.ConvertTimeoutToExpiration(SpannerOptions.Instance.Timeout, allowImmediateTimeout), SpannerSettings.IdempotentRetryFilter ))); return(SpannerClient.Create( channel, new SpannerSettings { CreateSessionSettings = idempotentCallSettings, GetSessionSettings = idempotentCallSettings, DeleteSessionSettings = idempotentCallSettings, ExecuteSqlSettings = idempotentCallSettings, ReadSettings = idempotentCallSettings, BeginTransactionSettings = idempotentCallSettings, CommitSettings = idempotentCallSettings, RollbackSettings = idempotentCallSettings, AllowImmediateTimeouts = allowImmediateTimeout })); }
/// <summary> /// Makes one call to PubSub.Pull to pull some books from the subscription. /// </summary> // [START pullonce] private void PullOnce(Action <long> callback, CancellationToken cancellationToken) { _logger.LogVerbose($"Pulling messages from {_subscriptionName}..."); // Pull some messages from the subscription. var response = _sub.Pull(_subscriptionName, false, 3, CallSettings.FromCallTiming( CallTiming.FromExpiration( Expiration.FromTimeout( TimeSpan.FromSeconds(90))))); if (response.ReceivedMessages == null) { // HTTP Request expired because the queue was empty. Ok. _logger.LogVerbose("Pulled no messages."); return; } _logger.LogVerbose($"Pulled {response.ReceivedMessages.Count} messages."); if (response.ReceivedMessages.Count == 0) { return; } foreach (var message in response.ReceivedMessages) { try { // Unpack the message. byte[] json = message.Message.Data.ToByteArray(); var qmessage = JsonConvert.DeserializeObject <QueueMessage>( Encoding.UTF8.GetString(json)); // Invoke ProcessBook(). callback(qmessage.BookId); } catch (Exception e) { _logger.LogError("Error processing book.", e); } } // Acknowledge the message so we don't see it again. var ackIds = new string[response.ReceivedMessages.Count]; for (int i = 0; i < response.ReceivedMessages.Count; ++i) { ackIds[i] = response.ReceivedMessages[i].AckId; } _sub.Acknowledge(_subscriptionName, ackIds); }
private FirestoreDb(string projectId, string databaseId, FirestoreClient client, Action <string> warningLogger) { ProjectId = GaxPreconditions.CheckNotNull(projectId, nameof(projectId)); DatabaseId = GaxPreconditions.CheckNotNull(databaseId, nameof(databaseId)); Client = GaxPreconditions.CheckNotNull(client, nameof(client)); // TODO: Investigate using DatabaseName and DocumentPathName. RootPath = $"projects/{ProjectId}/databases/{DatabaseId}"; DocumentsPath = $"{RootPath}/documents"; WarningLogger = warningLogger; // TODO: Validate these settings, and potentially make them configurable _batchGetCallSettings = CallSettings.FromCallTiming(CallTiming.FromRetry(new RetrySettings( retryBackoff: new BackoffSettings(TimeSpan.FromMilliseconds(500), TimeSpan.FromSeconds(5), 2.0), timeoutBackoff: new BackoffSettings(TimeSpan.FromMinutes(1), TimeSpan.FromMinutes(3), 2.0), Expiration.FromTimeout(TimeSpan.FromMinutes(10)), RetrySettings.FilterForStatusCodes(StatusCode.Unavailable)))); }
public FirebaseDocumentListener(FirestoreDb db) { // Create client FirestoreDb = db; FirestoreClient = db.Client; //FirestoreClient.Create(); ProjectId = db.ProjectId; DatabaseId = db.DatabaseId; //Setup no expiration for the listen ListenSettings = CallSettings.FromCallTiming(CallTiming.FromExpiration(Expiration.None)); //Start our handler for writing requests to GCP RequestHanderTask = StartRequestHandlerTask(); //Initialize a cancelation source so we can cancel tasks we create CancellationTokenSource = new CancellationTokenSource(); CancellationToken = CancellationTokenSource.Token; }
public void Overrides() { string projectId = _fixture.ProjectId; string topicId = _fixture.CreateTopicId(); DateTime deadline = DateTime.MaxValue; CancellationToken cancellationToken = new CancellationTokenSource().Token; // Sample: Overrides // Create a default PublisherServiceApiSettings, with customizations for CreateTopic RPCs: // * A custom "ClientVersion" header. // * A custom 5-second timeout Timing. // * No cancellation token. PublisherServiceApiSettings publisherSettings = new PublisherServiceApiSettings(); publisherSettings.CreateTopicSettings = publisherSettings.CreateTopicSettings .WithCancellationToken(CancellationToken.None) .WithCallTiming(CallTiming.FromTimeout(TimeSpan.FromSeconds(5))) .WithHeader("ClientVersion", "1.0.0"); // Override the above Timing and CancellationToken in the client-wide CallSettings; // the Headers are not overridden. publisherSettings.CallSettings = CallSettings .FromCallTiming(CallTiming.FromDeadline(deadline)) .WithCancellationToken(CancellationToken.None); // Create the client with the configured publisherSettings PublisherServiceApiClient client = PublisherServiceApiClient.Create(settings: publisherSettings); // Create a topic name from the projectId and topicId. TopicName topicName = new TopicName(projectId, topicId); // Call CreateTopic(). Override only the CancellationToken, using a per-RPC-method CallSettings. // The CallSettings used during this RPC invocation is: // * A custom "ClientVersion" header. // * A Timing deadline of 'deadline' (*not* the overridden 5-second timeout). // * The CancellationToken 'cancellationToken' (*not* CancellationToken.None). Topic topic = client.CreateTopic(topicName, CallSettings.FromCancellationToken(cancellationToken)); // End sample }
private async Task RunBulkMessaging( int messageCount, int minMessageSize, int maxMessageSize, int maxMessagesInFlight, int initialNackCount, TimeSpan?timeouts = null, int?cancelAfterRecvCount = null) { // Force messages to be at least 4 bytes long, so an int ID can be used. minMessageSize = Math.Max(4, minMessageSize); var topicId = _fixture.CreateTopicId(); var subscriptionId = _fixture.CreateSubscriptionId(); Console.WriteLine("BulkMessaging test"); Console.WriteLine($"{messageCount} messages; of size [{minMessageSize}, {maxMessageSize}]; " + $"max in-flight: {maxMessagesInFlight}, initialNacks: {initialNackCount}, cancelAfterRecvCount: {cancelAfterRecvCount}"); // Create topic var topicName = new TopicName(_fixture.ProjectId, topicId); var publisher = await PublisherClient.CreateAsync().ConfigureAwait(false); await publisher.CreateTopicAsync(topicName).ConfigureAwait(false); // Subscribe to the topic var subscriber = await SubscriberClient.CreateAsync().ConfigureAwait(false); var subscriptionName = new SubscriptionName(_fixture.ProjectId, subscriptionId); await subscriber.CreateSubscriptionAsync(subscriptionName, topicName, null, 60).ConfigureAwait(false); // Create SimplePublisher and SimpleSubscriber var simplePublisher = await SimplePublisher.CreateAsync(topicName, clientCreationSettings : timeouts == null?null : new SimplePublisher.ClientCreationSettings( publisherSettings: new PublisherSettings { PublishSettings = CallSettings.FromCallTiming(CallTiming.FromRetry(new RetrySettings( retryBackoff: PublisherSettings.GetMessagingRetryBackoff(), timeoutBackoff: new BackoffSettings(timeouts.Value, timeouts.Value, 1.0), totalExpiration: Expiration.FromTimeout(timeouts.Value), retryFilter: PublisherSettings.NonIdempotentRetryFilter ))) } )).ConfigureAwait(false); var simpleSubscriber = await SimpleSubscriber.CreateAsync(subscriptionName, settings : new SimpleSubscriber.Settings { StreamAckDeadline = timeouts, FlowControlSettings = new FlowControlSettings(maxMessagesInFlight, null) }).ConfigureAwait(false); Console.WriteLine("Topic, Subscription, SimplePublisher and SimpleSubscriber all created"); // Subscribe object recvLock = new object(); int recvCount = 0; // Count of received messages int dupCount = 0; // Count of duplicate messages long recvSum = 0L; // Sum of bytes of received messages var recvedIds = new ConcurrentDictionary <int, bool>(); var nackedIds = new HashSet <int>(); Task subTask = simpleSubscriber.StartAsync((msg, ct) => { int id = BitConverter.ToInt32(msg.Data.ToArray(), 0); lock (nackedIds) { if (nackedIds.Count < initialNackCount) { if (nackedIds.Add(id)) { // This ID not already nacked Interlocked.Increment(ref recvCount); return(Task.FromResult(SimpleSubscriber.Reply.Nack)); } } } bool wasAdded = recvedIds.TryAdd(id, false); if (wasAdded) { var localRecvCount = Interlocked.Increment(ref recvCount); Interlocked.Add(ref recvSum, msg.Data.Sum(x => (long)x)); if (localRecvCount == cancelAfterRecvCount || localRecvCount >= messageCount + initialNackCount) { // Test finished, so stop subscriber Task unused = simpleSubscriber.StopAsync(TimeSpan.FromSeconds(15)); } } else { Interlocked.Add(ref dupCount, 1); } // ACK all messages return(Task.FromResult(SimpleSubscriber.Reply.Ack)); }); // Publish var rnd = new Random(1234); var activePubs = new HashSet <Task>(); int sentCount = 0; long sentSum = 0L; // Sum of bytes of sent messages // Watchdog to report progress and fail test on deadlock CancellationTokenSource watchdogCts = new CancellationTokenSource(); Task.Run(async() => { int prevSentCount = -1; int prevRecvCount = -1; int noProgressCount = 0; while (!watchdogCts.IsCancellationRequested) { await Task.Delay(TimeSpan.FromSeconds(1), watchdogCts.Token).ConfigureAwait(false); var localSentCount = Interlocked.Add(ref sentCount, 0); var localRecvCount = Interlocked.Add(ref recvCount, 0); var localDupCount = Interlocked.Add(ref dupCount, 0); if (prevSentCount == localSentCount && prevRecvCount == localRecvCount) { if (noProgressCount > 100) { // Deadlock, shutdown subscriber, and cancel Console.WriteLine("Deadlock detected. Cancelling test"); simpleSubscriber.StopAsync(new CancellationToken(true)); watchdogCts.Cancel(); break; } noProgressCount += 1; } else { noProgressCount = 0; } prevSentCount = localSentCount; prevRecvCount = localRecvCount; Console.WriteLine($"Sent: {localSentCount} (in-flight: {activePubs.Locked(() => activePubs.Count)}); Recv: {localRecvCount} (dups: {localDupCount})"); } }); for (int i = 0; i < messageCount; i++) { if (watchdogCts.IsCancellationRequested) { Assert.True(false, "Test cancelled by watchdog"); } if (subTask.IsCompleted) { break; } var msgSize = rnd.Next(minMessageSize, maxMessageSize + 1); var msg = new byte[msgSize]; rnd.NextBytes(msg); // Insert an int ID into message Array.Copy(BitConverter.GetBytes(i), msg, 4); sentSum += msg.Sum(x => (long)x); // Send message, and record Task var pubTask = simplePublisher.PublishAsync(msg); Interlocked.Increment(ref sentCount); activePubs.Locked(() => activePubs.Add(pubTask)); // Remove Task from active when the message has been sent to server pubTask.ContinueWith(t => activePubs.Locked(() => activePubs.Remove(pubTask))); // If too many messages are currently in flight, wait a bit while (activePubs.Locked(() => activePubs.Count) >= maxMessagesInFlight) { await Task.Delay(TimeSpan.FromMilliseconds(1)).ConfigureAwait(false); } } Console.WriteLine("Publishing complete"); // Wait for all messages to be sent to server await Task.WhenAll(activePubs.Locked(() => activePubs.ToArray())).ConfigureAwait(false); Console.WriteLine("Publishing completed sending to server"); // Wait for subscriber to finish shutdown await subTask.ConfigureAwait(false); watchdogCts.Cancel(); Console.WriteLine("Subscriber finished shutdown"); Console.WriteLine($"Sent: {sentCount}; Recv: {recvCount}"); if (cancelAfterRecvCount is int cancelAfter) { Assert.True(recvCount >= cancelAfter && recvCount <= cancelAfter + maxMessagesInFlight, $"Incorrect recvCount: {recvCount}"); } else { // Check that all messages are correctly received. Assert.Equal(messageCount + initialNackCount, recvCount); // This isn't foolproof (we can get to the right sum with wrong values) but it's a pretty strong indicator. Assert.Equal(sentSum, recvSum); } }