11// Copyright (c) Microsoft Corporation.
22// Licensed under the MIT License.
33
4- using System ;
54using System . Collections . Generic ;
65using System . Linq ;
7- using System . Runtime . CompilerServices ;
86using System . Threading . Tasks ;
97using BuildXL . Cache . ContentStore . Distributed . NuCache ;
108using BuildXL . Cache . ContentStore . Hashing ;
1513using BuildXL . Cache . ContentStore . Utils ;
1614using BuildXL . Cache . MemoizationStore . Interfaces . Results ;
1715using BuildXL . Cache . MemoizationStore . Interfaces . Sessions ;
18- using BuildXL . Utilities . Core . Tracing ;
19- using Grpc . Core ;
2016
2117#nullable enable
2218
@@ -25,104 +21,31 @@ namespace BuildXL.Cache.ContentStore.Distributed.MetadataService
2521 /// <summary>
2622 /// A global content metadata store client which routes requests to a remote machine.
2723 /// </summary>
28- public class ClientGlobalCacheStore : StartupShutdownComponentBase , IGlobalCacheStore
24+ public class ClientGlobalCacheStore : GrpcCodeFirstClient < IGlobalCacheService > , IGlobalCacheStore
2925 {
3026 /// <inheritdoc />
3127 public override bool AllowMultipleStartupAndShutdowns => true ;
3228
3329 /// <inheritdoc />
34- protected override Tracer Tracer { get ; } = new Tracer ( nameof ( ClientGlobalCacheStore ) ) ;
35-
36- private readonly IClientAccessor < IGlobalCacheService > _serviceClientFactory ;
37-
38- private readonly ClientContentMetadataStoreConfiguration _configuration ;
39-
40- private readonly IClock _clock ;
41-
42- private readonly IRetryPolicy _retryPolicy ;
30+ protected override Tracer Tracer { get ; } = new ( nameof ( ClientGlobalCacheStore ) ) ;
4331
4432 public ClientGlobalCacheStore (
4533 IClientAccessor < IGlobalCacheService > metadataServiceClientFactory ,
4634 ClientContentMetadataStoreConfiguration configuration )
35+ : base ( metadataServiceClientFactory , CreateRetryPolicy ( configuration ) , SystemClock . Instance , configuration . OperationTimeout )
4736 {
48- _serviceClientFactory = metadataServiceClientFactory ;
49- _configuration = configuration ;
50- _clock = SystemClock . Instance ;
37+ }
5138
52- _retryPolicy = RetryPolicyFactory . GetExponentialPolicy (
39+ private static IRetryPolicy CreateRetryPolicy ( ClientContentMetadataStoreConfiguration configuration )
40+ {
41+ return RetryPolicyFactory . GetExponentialPolicy (
5342 _ => true ,
5443 // We use an absurdly high retry count because the actual operation timeout is controlled through
5544 // PerformOperationAsync in ExecuteAsync.
5645 1_000_000 ,
57- _configuration . RetryMinimumWaitTime ,
58- _configuration . RetryMaximumWaitTime ,
59- _configuration . RetryDelta ) ;
60-
61- LinkLifetime ( _serviceClientFactory ) ;
62- }
63-
64- private async Task < TResult > ExecuteAsync < TResult > (
65- OperationContext originalContext ,
66- Func < OperationContext , CallOptions , IGlobalCacheService , Task < TResult > > executeAsync ,
67- Func < TResult , string ? > extraEndMessage ,
68- string ? extraStartMessage = null ,
69- [ CallerMemberName ] string caller = null ! )
70- where TResult : ResultBase
71- {
72- var attempt = - 1 ;
73- using var contextWithShutdown = TrackShutdown ( originalContext ) ;
74- var context = contextWithShutdown . Context ;
75- var callerAttempt = $ "{ caller } _Attempt";
76-
77- return await context . PerformOperationWithTimeoutAsync (
78- Tracer ,
79- context =>
80- {
81- var callOptions = new CallOptions (
82- headers : new Metadata ( )
83- {
84- MetadataServiceSerializer . CreateContextIdHeaderEntry ( context . TracingContext . TraceId )
85- } ,
86- deadline : _clock . UtcNow + _configuration . OperationTimeout ,
87- cancellationToken : context . Token ) ;
88-
89- return _retryPolicy . ExecuteAsync ( async ( ) =>
90- {
91- await Task . Yield ( ) ;
92-
93- attempt ++ ;
94-
95- var stopwatch = StopwatchSlim . Start ( ) ;
96- var clientCreationTime = TimeSpan . Zero ;
97-
98- var result = await context . PerformOperationAsync ( Tracer , ( ) =>
99- {
100- return _serviceClientFactory . UseAsync ( context , service =>
101- {
102- clientCreationTime = stopwatch . Elapsed ;
103-
104- return executeAsync ( context , callOptions , service ) ;
105- } ) ;
106- } ,
107- extraStartMessage : extraStartMessage ,
108- extraEndMessage : r => $ "Attempt=[{ attempt } ] ClientCreationTimeMs=[{ clientCreationTime . TotalMilliseconds } ] { extraEndMessage ( r ) } ",
109- caller : callerAttempt ,
110- traceErrorsOnly : true ) ;
111-
112- await Task . Yield ( ) ;
113-
114- // Because we capture exceptions inside the PerformOperation, we need to make sure that they
115- // get propagated for the retry policy to kick in.
116- result . RethrowIfFailure ( ) ;
117-
118- return result ;
119- } , context . Token ) ;
120- } ,
121- caller : caller ,
122- traceErrorsOnly : true ,
123- extraStartMessage : extraStartMessage ,
124- extraEndMessage : r => $ "Attempts=[{ attempt + 1 } ] { extraEndMessage ( r ) } ",
125- timeout : _configuration . OperationTimeout ) ;
46+ configuration . RetryMinimumWaitTime ,
47+ configuration . RetryMaximumWaitTime ,
48+ configuration . RetryDelta ) ;
12649 }
12750
12851 public Task < Result < IReadOnlyList < ContentLocationEntry > > > GetBulkAsync ( OperationContext context , IReadOnlyList < ShortHash > contentHashes )
@@ -243,15 +166,5 @@ public Task<Result<SerializedMetadataEntry>> GetContentHashListAsync(OperationCo
243166 // TODO: What to log here?
244167 extraEndMessage : r => r . GetValueOrDefault ( ) ? . ToString ( ) ) ;
245168 }
246-
247- public Task < Result < MachineMapping > > RegisterMachineAsync ( OperationContext context , MachineLocation machineLocation )
248- {
249- throw new NotImplementedException ( $ "Attempt to use { nameof ( ClientGlobalCacheStore ) } for machine registration is unsupported") ;
250- }
251-
252- public Task < BoolResult > ForceRegisterMachineAsync ( OperationContext context , MachineMapping mapping )
253- {
254- throw new NotImplementedException ( $ "Attempt to use { nameof ( ClientGlobalCacheStore ) } for machine registration is unsupported") ;
255- }
256169 }
257170}
0 commit comments