@@ -313,40 +313,71 @@ Future<Void> remoteRecovered(Reference<AsyncVar<ServerDBInfo> const> db) {
313313// On value=2, clears backupPartitionListKey.
314314// In both cases the request key is cleared in the same commit.
315315Future<Void> monitorBackupPartitionRequired (Database cx, KeyRangeMap<ShardTrackedData>* shards, UID ddId) {
316+ // The partition computation can wait arbitrarily long on shard-metrics tracking, so it runs OUTSIDE any
317+ // transaction to avoid transaction_too_old. A short re-read in the write transaction protects against
318+ // the race where a value=2 (cleanup) arrives while we are computing for a value=1.
316319 while (true ) {
317- ReadYourWritesTransaction tr (cx);
318- while (true ) {
320+ // Phase 1: peek the request key in a loop. If nothing pending, park on watch and wait, then re-read.
321+ int8_t requestType = 0 ;
322+ while (requestType == 0 ) {
323+ ReadYourWritesTransaction tr (cx);
319324 Error err;
320325 try {
321326 tr.setOption (FDBTransactionOptions::ACCESS_SYSTEM_KEYS );
322327 tr.setOption (FDBTransactionOptions::LOCK_AWARE );
323328 Optional<Value> value = co_await tr.get (backupPartitionRequiredKey);
324- int8_t requestType = value.present () ? decodeBackupPartitionRequiredValue (value.get ()) : 0 ;
325-
326- if (requestType == 1 ) {
327- std::vector<KeyRange> partitions = co_await calculateBackupPartitionKeyRanges (shards);
328- tr.set (backupPartitionListKey, encodeBackupPartitionListValue (partitions));
329- tr.clear (backupPartitionRequiredKey);
330- co_await tr.commit ();
331- TraceEvent (" DDBackupPartitionsComputed" , ddId).detail (" NumPartitions" , partitions.size ());
332- break ;
333- } else if (requestType == 2 ) {
334- tr.clear (backupPartitionListKey);
335- tr.clear (backupPartitionRequiredKey);
329+ requestType = value.present () ? decodeBackupPartitionRequiredValue (value.get ()) : 0 ;
330+ if (requestType == 0 ) {
331+ Future<Void> watchFuture = tr.watch (backupPartitionRequiredKey);
336332 co_await tr.commit ();
337- TraceEvent (" DDBackupPartitionsCleared" , ddId);
338- break ;
333+ co_await watchFuture;
339334 }
340-
341- Future<Void> watchFuture = tr.watch (backupPartitionRequiredKey);
342- co_await tr.commit ();
343- co_await watchFuture;
344- break ;
335+ continue ;
345336 } catch (Error& e) {
346337 err = e;
347338 }
348339 co_await tr.onError (err);
349340 }
341+
342+ // Phase 2: compute outside any transaction (may wait long on shard metrics).
343+ std::vector<KeyRange> partitions;
344+ if (requestType == 1 ) {
345+ partitions = co_await calculateBackupPartitionKeyRanges (shards);
346+ }
347+
348+ // Phase 3: short txn to re-check the request value and write the result.
349+ {
350+ ReadYourWritesTransaction tr (cx);
351+ while (true ) {
352+ Error err;
353+ try {
354+ tr.setOption (FDBTransactionOptions::ACCESS_SYSTEM_KEYS );
355+ tr.setOption (FDBTransactionOptions::LOCK_AWARE );
356+ Optional<Value> value = co_await tr.get (backupPartitionRequiredKey);
357+ int8_t currentType = value.present () ? decodeBackupPartitionRequiredValue (value.get ()) : 0 ;
358+ if (currentType != requestType) {
359+ // Someone wrote a new request while we were computing partitions; restart the outer loop
360+ // so the next iteration acts on the new value.
361+ break ;
362+ }
363+ if (requestType == 1 ) {
364+ tr.set (backupPartitionListKey, encodeBackupPartitionListValue (partitions));
365+ tr.clear (backupPartitionRequiredKey);
366+ co_await tr.commit ();
367+ TraceEvent (" DDBackupPartitionsComputed" , ddId).detail (" NumPartitions" , partitions.size ());
368+ } else {
369+ tr.clear (backupPartitionListKey);
370+ tr.clear (backupPartitionRequiredKey);
371+ co_await tr.commit ();
372+ TraceEvent (" DDBackupPartitionsCleared" , ddId);
373+ }
374+ break ;
375+ } catch (Error& e) {
376+ err = e;
377+ }
378+ co_await tr.onError (err);
379+ }
380+ }
350381 }
351382}
352383
0 commit comments