public async Task RetryAfterTotalExpiration() { var settings = new BigtableServiceApiSettings(); // Don't allow for any time to retry. settings.MutateRowsSettings = CallSettings.FromExpiration(Expiration.FromTimeout(TimeSpan.Zero)); var request = new MutateRowsRequest { Entries = { Mutations.CreateEntry("a", Mutations.DeleteFromRow()), Mutations.CreateEntry("b", Mutations.DeleteFromRow()), Mutations.CreateEntry("c", Mutations.DeleteFromRow()) } }; var client = Utilities.CreateMutateRowsMockClient( request, entriesForInitialStream: new[] { Utilities.CreateMutateRowsResponseEntry(0, Code.Ok), Utilities.CreateMutateRowsResponseEntry(1, Code.DeadlineExceeded), Utilities.CreateMutateRowsResponseEntry(2, Code.Ok) }, entriesForRetryStreams: new[] { // 1st retry response entries new[] { Utilities.CreateMutateRowsResponseEntry(0, Code.Ok) } }, settings: settings); var exception = await Assert.ThrowsAsync <RpcException>(() => client.MutateRowsAsync(request)); Assert.Equal(StatusCode.DeadlineExceeded, exception.StatusCode); }
public static BigtableClient CreateMutateRowsMockClient( MutateRowsRequest initialRequest, MutateRowsResponse.Types.Entry[] entriesForInitialStream, MutateRowsResponse.Types.Entry[][] entriesForRetryStreams = null, BigtableServiceApiSettings settings = null) => CreateMockClientForStreamingRpc( initialRequest, c => c.MutateRows( It.Is <MutateRowsRequest>(r => ReferenceEquals(r, initialRequest)), It.IsAny <CallSettings>()), c => c.MutateRows( It.IsAny <MutateRowsRequest>(), It.IsAny <CallSettings>()), entriesForInitialStream, entriesForRetryStreams, itemsToStream: entries => new MockMutateRowsStream(new MutateRowsResponse { Entries = { entries } }), validator: (request, response) => { // Make sure the request is properly formulated for the mock stream being returned. if (request.Entries.Count != response.Responses.SelectMany(r => r.Entries).Count()) { throw new InvalidOperationException("The specified request is invalid for the mock stream about to be returned."); } }, settings);
private static BigtableClient CreateMockClientForStreamingRpc <TRequest, TStream, TMockStream, TStreamItems>( TRequest initialRequest, Expression <Func <BigtableServiceApiClient, TStream> > initialSetup, Expression <Func <BigtableServiceApiClient, TStream> > retrySetup, TStreamItems[] entriesForInitialStream, TStreamItems[][] entriesForRetryStreams, Func <TStreamItems[], TMockStream> itemsToStream, Action <TRequest, TMockStream> validator, BigtableServiceApiSettings settings) where TMockStream : class, TStream { var mock = new Mock <BigtableServiceApiClient>(); mock.SetupGet(x => x.DefaultSettings).Returns(settings); // Even though we want to setup the initial call last, we should call the stream conversion for it // first so `itemsToStream` is called in the order the streams will be returned, just in case the // order matters to the caller. var initialResponse = itemsToStream(entriesForInitialStream); var retryStreams = entriesForRetryStreams != null ? new Queue <TMockStream>(entriesForRetryStreams.Select(items => items == null ? null : itemsToStream(items))) : new Queue <TMockStream>(); mock.Setup(retrySetup).Returns <TRequest, CallSettings>((request, callSettings) => { Assert.NotEmpty(retryStreams); var respose = retryStreams.Dequeue(); if (respose == null) { throw new Grpc.Core.RpcException( new Grpc.Core.Status(Grpc.Core.StatusCode.Unavailable, "Unavailable")); } validator?.Invoke(request, respose); return(respose); }); // Setup the initial response last so the catch-all setup doesn't overwrite it. // Check for reference equality to retry requests that happen to be a duplicate of the original don't match here. mock.Setup(initialSetup).Returns <TRequest, CallSettings>((request, callSettings) => { validator?.Invoke(request, initialResponse); return(initialResponse); }); return(new BigtableClientImpl(mock.Object)); }
public async Task RetryingAfterTotalExpiration() { var settings = new BigtableServiceApiSettings(); // Don't allow for any time to retry. settings.ReadRowsRetrySettings = settings.ReadRowsRetrySettings.WithTotalExpiration( Expiration.FromTimeout(TimeSpan.Zero)); var request = new ReadRowsRequest { Rows = RowSet.FromRowKeys("a", "b", "c") }; var client = Utilities.CreateReadRowsMockClient( request, initialStreamResponse: new[] { new ReadRowsResponse { Chunks = { CreateChunk("a", "cf1", "column1", "value1", commitRow: true) } } }, responsesForRetryStreams: new[] { null, // A null entry will throw an Unavailable RpcException new [] { new ReadRowsResponse { Chunks = { CreateChunk("b", "cf1", "column2", "value2", commitRow: true) } } } }, settings: settings); var exception = await Assert.ThrowsAsync <RpcException>(() => client.ReadRows(request).ToList()); Assert.Equal(StatusCode.Unavailable, exception.StatusCode); }
public BigtableFixtureBase() { GrpcInfo.EnableSubchannelCounting(); string emulatorHost = Environment.GetEnvironmentVariable(EmulatorEnvironmentVariable); string projectId; string instanceId; if (!string.IsNullOrEmpty(emulatorHost)) { projectId = "emulator-test-project"; EmulatorCallInvoker = new GcpCallInvoker( emulatorHost, ChannelCredentials.Insecure, GrpcCoreAdapter.Instance.ConvertOptions(BigtableServiceApiSettings.GetDefault().CreateChannelOptions())); instanceId = "doesnt-matter"; } else { projectId = Environment.GetEnvironmentVariable(TestProjectEnvironmentVariable); if (string.IsNullOrEmpty(projectId)) { throw new InvalidOperationException( $"Please set either the {EmulatorEnvironmentVariable} or {TestProjectEnvironmentVariable} environment variable before running tests"); } instanceId = Environment.GetEnvironmentVariable(TestInstanceEnvironmentVariable); if (string.IsNullOrEmpty(instanceId)) { throw new InvalidOperationException( $"Please set the {TestInstanceEnvironmentVariable} environment variable before running non-emulator tests."); } } ProjectName = new ProjectName(projectId); InstanceName = new InstanceName(projectId, instanceId); Task.Run(InitBigtableInstanceAndTable).Wait(); }
public static BigtableClient CreateReadRowsMockClient( ReadRowsRequest initialRequest, ReadRowsResponse[] initialStreamResponse, ReadRowsResponse[][] responsesForRetryStreams = null, bool errorAtEndOfLastStream = false, BigtableServiceApiSettings settings = null) { MockReadRowsStream lastStream = null; var result = CreateMockClientForStreamingRpc( initialRequest, c => c.ReadRows( It.Is <ReadRowsRequest>(r => ReferenceEquals(r, initialRequest)), It.IsAny <CallSettings>()), c => c.ReadRows( It.IsAny <ReadRowsRequest>(), It.IsAny <CallSettings>()), initialStreamResponse, responsesForRetryStreams, itemsToStream: entries => lastStream = new MockReadRowsStream(entries) { ShouldErrorAtEnd = true }, validator: (request, response) => { // Make sure the request is properly formulated for the mock stream being returned. // Each response chunk should be a continuation of a previous chunk or from a row // that was requested. if (!response.Responses.SelectMany(r => r.Chunks).All( c => c.RowKey.IsEmpty || IsRequested(request, c.RowKey))) { throw new InvalidOperationException("The specified request is invalid for the mock stream about to be returned."); } }, settings); // All but the last stream should end with an RpcException which permits retrying with the // default RetrySettings so the higher level ReadRowsStream keeps retrying // (see `ShouldErrorAtEnd = true` above). The last stream should end normally, unless // errorAtEndOfLastStream is true, in which case it should end with an error as well. lastStream.ShouldErrorAtEnd = errorAtEndOfLastStream; return(result); bool IsRequested(ReadRowsRequest request, BigtableByteString rowKey) { return (request.Rows.RowRanges.Any(IsInRange) || request.Rows.RowKeys.Contains((ByteString)rowKey)); bool IsInRange(RowRange range) { switch (range.StartKeyCase) { case RowRange.StartKeyOneofCase.StartKeyClosed: if (rowKey < range.StartKeyClosed) { return(false); } break; case RowRange.StartKeyOneofCase.StartKeyOpen: if (rowKey <= range.StartKeyOpen) { return(false); } break; } if (!range.EndKeyClosed.IsEmpty) { switch (range.EndKeyCase) { case RowRange.EndKeyOneofCase.EndKeyClosed: if (range.EndKeyClosed < rowKey) { return(false); } break; case RowRange.EndKeyOneofCase.EndKeyOpen: if (range.EndKeyOpen <= rowKey) { return(false); } break; } } return(true); } } }