2626import com .google .bigtable .v2 .MutateRowsResponse ;
2727import com .google .bigtable .v2 .RateLimitInfo ;
2828import com .google .cloud .bigtable .data .v2 .stub .metrics .BigtableTracer ;
29+ import com .google .cloud .bigtable .data .v2 .stub .metrics .Util ;
2930import com .google .common .annotations .VisibleForTesting ;
3031import com .google .common .base .Preconditions ;
3132import com .google .common .base .Stopwatch ;
3738import java .util .concurrent .atomic .AtomicReference ;
3839import java .util .logging .Logger ;
3940import javax .annotation .Nonnull ;
41+ import javax .annotation .Nullable ;
42+
43+ import static com .google .cloud .bigtable .data .v2 .stub .metrics .Util .extractStatus ;
4044
4145class RateLimitingServerStreamingCallable
4246 extends ServerStreamingCallable <MutateRowsRequest , MutateRowsResponse > {
@@ -69,6 +73,8 @@ class RateLimitingServerStreamingCallable
6973
7074 private final ServerStreamingCallable <MutateRowsRequest , MutateRowsResponse > innerCallable ;
7175
76+ private BigtableTracer bigtableTracer ;
77+
7278 RateLimitingServerStreamingCallable (
7379 @ Nonnull ServerStreamingCallable <MutateRowsRequest , MutateRowsResponse > innerCallable ) {
7480 this .limiter = new ConditionalRateLimiter (DEFAULT_QPS );
@@ -84,8 +90,8 @@ public void call(
8490 limiter .acquire ();
8591 stopwatch .stop ();
8692 if (context .getTracer () instanceof BigtableTracer ) {
87- (( BigtableTracer ) context .getTracer ())
88- .batchRequestThrottled (stopwatch .elapsed (TimeUnit .NANOSECONDS ));
93+ bigtableTracer = ( BigtableTracer ) context .getTracer ();
94+ bigtableTracer .batchRequestThrottled (stopwatch .elapsed (TimeUnit .NANOSECONDS ));
8995 }
9096 RateLimitingResponseObserver innerObserver = new RateLimitingResponseObserver (responseObserver );
9197 innerCallable .call (request , innerObserver , context );
@@ -104,7 +110,7 @@ static class ConditionalRateLimiter {
104110
105111 public ConditionalRateLimiter (long defaultQps ) {
106112 limiter = RateLimiter .create (defaultQps );
107- logger .info ("Rate limiting is initiated (but disabled) with rate of " + defaultQps + " QPS." );
113+ logger .info ("Batch write flow control: rate limiter is initiated (but disabled) with rate of " + defaultQps + " QPS." );
108114 }
109115
110116 /**
@@ -128,7 +134,7 @@ public void tryDisable() {
128134 if (now .isAfter (nextTime )) {
129135 boolean wasEnabled = this .enabled .getAndSet (false );
130136 if (wasEnabled ) {
131- logger .info ("Rate limiter is disabled." );
137+ logger .info ("Batch write flow control: rate limiter is disabled." );
132138 }
133139 // No need to update nextRateUpdateTime, any new RateLimitInfo can enable rate limiting and
134140 // update the rate again.
@@ -139,7 +145,7 @@ public void tryDisable() {
139145 public void enable () {
140146 boolean wasEnabled = this .enabled .getAndSet (true );
141147 if (!wasEnabled ) {
142- logger .info ("Rate limiter is enabled." );
148+ logger .info ("Batch write flow control: rate limiter is enabled." );
143149 }
144150 }
145151
@@ -158,31 +164,49 @@ public double getRate() {
158164 * @param rate The new rate of the rate limiter.
159165 * @param period The period during which rate should not be updated again and the rate limiter
160166 * should not be disabled.
167+ * @param bigtableTracer The tracer for exporting client-side metrics.
161168 */
162- public void trySetRate (double rate , Duration period ) {
169+ public void trySetRate (
170+ double rate ,
171+ Duration period ,
172+ BigtableTracer bigtableTracer ,
173+ double factor ,
174+ String statusString ) {
163175 Instant nextTime = nextRateUpdateTime .get ();
164176 Instant now = Instant .now ();
165177
166178 if (now .isBefore (nextTime )) {
179+ if (bigtableTracer != null ) {
180+ bigtableTracer .addBatchWriteFlowControlFactor (factor , statusString , false );
181+ }
167182 return ;
168183 }
169184
170185 Instant newNextTime = now .plusSeconds (period .getSeconds ());
171186
172187 if (!nextRateUpdateTime .compareAndSet (nextTime , newNextTime )) {
173188 // Someone else updated it already.
189+ if (bigtableTracer != null ) {
190+ bigtableTracer .addBatchWriteFlowControlFactor (factor , statusString , false );
191+ }
174192 return ;
175193 }
176194 final double oldRate = limiter .getRate ();
177195 limiter .setRate (rate );
178196 logger .info (
179- "Updated max rate from "
197+ "Batch write flow control: updated max rate from "
180198 + oldRate
181199 + " to "
182200 + rate
201+ + " applied factor "
202+ + factor
183203 + " with period "
184204 + period .getSeconds ()
185205 + " seconds." );
206+ if (bigtableTracer != null ) {
207+ bigtableTracer .setBatchWriteFlowControlTargetQps (rate );
208+ bigtableTracer .addBatchWriteFlowControlFactor (factor , statusString , true );
209+ }
186210 }
187211
188212 @ VisibleForTesting
@@ -215,17 +239,17 @@ private boolean hasValidRateLimitInfo(MutateRowsResponse response) {
215239 // have presence even thought it's marked as "optional". Check the factor and
216240 // period to make sure they're not 0.
217241 if (!response .hasRateLimitInfo ()) {
218- logger .finest ("Response carries no RateLimitInfo" );
242+ logger .finest ("Batch write flow control: response carries no RateLimitInfo" );
219243 return false ;
220244 }
221245
222246 if (response .getRateLimitInfo ().getFactor () <= 0
223247 || response .getRateLimitInfo ().getPeriod ().getSeconds () <= 0 ) {
224- logger .finest ("Response carries invalid RateLimitInfo=" + response .getRateLimitInfo ());
248+ logger .finest ("Batch write flow control: response carries invalid RateLimitInfo=" + response .getRateLimitInfo ());
225249 return false ;
226250 }
227251
228- logger .finest ("Response carries valid RateLimitInfo=" + response .getRateLimitInfo ());
252+ logger .finest ("Batch write flow control: response carries valid RateLimitInfo=" + response .getRateLimitInfo ());
229253 return true ;
230254 }
231255
@@ -236,7 +260,8 @@ protected void onResponseImpl(MutateRowsResponse response) {
236260 RateLimitInfo info = response .getRateLimitInfo ();
237261 updateQps (
238262 info .getFactor (),
239- Duration .ofSeconds (com .google .protobuf .util .Durations .toSeconds (info .getPeriod ())));
263+ Duration .ofSeconds (com .google .protobuf .util .Durations .toSeconds (info .getPeriod ())),
264+ extractStatus (null ));
240265 } else {
241266 limiter .tryDisable ();
242267 }
@@ -250,7 +275,11 @@ protected void onErrorImpl(Throwable t) {
250275 if (t instanceof DeadlineExceededException
251276 || t instanceof UnavailableException
252277 || t instanceof ResourceExhaustedException ) {
253- updateQps (MIN_FACTOR , DEFAULT_PERIOD );
278+ logger .info ("Batch write flow control: received error "
279+ + extractStatus (t ) + " applying min factor "
280+ + MIN_FACTOR + " with period "
281+ + DEFAULT_PERIOD + " seconds." );
282+ updateQps (MIN_FACTOR , DEFAULT_PERIOD , extractStatus (t ));
254283 }
255284 outerObserver .onError (t );
256285 }
@@ -260,11 +289,11 @@ protected void onCompleteImpl() {
260289 outerObserver .onComplete ();
261290 }
262291
263- private void updateQps (double factor , Duration period ) {
292+ private void updateQps (double factor , Duration period , String statusString ) {
264293 double cappedFactor = Math .min (Math .max (factor , MIN_FACTOR ), MAX_FACTOR );
265294 double currentRate = limiter .getRate ();
266295 double cappedRate = Math .min (Math .max (currentRate * cappedFactor , MIN_QPS ), MAX_QPS );
267- limiter .trySetRate (cappedRate , period );
296+ limiter .trySetRate (cappedRate , period , bigtableTracer , cappedFactor , statusString );
268297 }
269298 }
270299
0 commit comments