@@ -102,6 +102,7 @@ public abstract class IndexingBase {
102
102
private final long startingTimeMillis ;
103
103
private long lastTypeStampCheckMillis ;
104
104
private Map <String , IndexingMerger > indexingMergerMap = null ;
105
+ @ Nullable
105
106
private IndexingHeartbeat heartbeat = null ; // this will stay null for index scrubbing
106
107
107
108
IndexingBase (@ Nonnull IndexingCommon common ,
@@ -157,15 +158,13 @@ public CompletableFuture<Void> buildIndexAsync(boolean markReadable) {
157
158
long startNanos = System .nanoTime ();
158
159
FDBDatabaseRunner runner = getRunner ();
159
160
final FDBStoreTimer timer = runner .getTimer ();
160
- if ( timer != null ) {
161
+ if (timer != null ) {
161
162
lastProgressSnapshot = StoreTimerSnapshot .from (timer );
162
163
}
163
- AtomicReference <Throwable > indexingException = new AtomicReference <>(null );
164
- return handleStateAndDoBuildIndexAsync (markReadable , message )
165
- .handle ((ret , ex ) -> {
166
- if (ex != null ) {
167
- indexingException .set (ex );
168
- }
164
+ return MoreAsyncUtil .composeWhenComplete (
165
+ handleStateAndDoBuildIndexAsync (markReadable , message ),
166
+ (result , ex ) -> {
167
+ // proper log
169
168
message .addKeysAndValues (indexingLogMessageKeyValues ()) // add these here to pick up state accumulated during build
170
169
.addKeysAndValues (common .indexLogMessageKeyValues ())
171
170
.addKeyAndValue (LogMessageKeys .TOTAL_MICROS , TimeUnit .NANOSECONDS .toMicros (System .nanoTime () - startNanos ));
@@ -177,20 +176,13 @@ public CompletableFuture<Void> buildIndexAsync(boolean markReadable) {
177
176
message .addKeyAndValue (LogMessageKeys .RESULT , "success" );
178
177
LOGGER .info (message .toString ());
179
178
}
180
- return ret ;
181
- })
182
- // Here: if the heartbeat was *not* cleared while marking the index readable, it would be cleared in
183
- // these dedicated transaction. Heartbeat clearing is not a blocker but a "best effort" operation.
184
- .thenCompose (ignore -> clearHeartbeats ())
185
- .handle ((ignore , exIgnore ) -> {
186
- Throwable ex = indexingException .get ();
187
- if (ex instanceof RuntimeException ) {
188
- throw (RuntimeException ) ex ;
189
- } else if (ex != null ) {
190
- throw new RuntimeException (ex );
191
- }
192
- return null ;
193
- });
179
+ // Here: if the heartbeat was *not* cleared while marking the index readable, it would be cleared in
180
+ // these dedicated transaction. Heartbeat clearing is not a blocker but a "best effort" operation.
181
+ return clearHeartbeats ()
182
+ .handle ((ignoreRet , ignoreEx ) -> null );
183
+ },
184
+ getRunner ().getDatabase ()::mapAsyncToSyncException
185
+ );
194
186
}
195
187
196
188
abstract List <Object > indexingLogMessageKeyValues ();
@@ -271,7 +263,8 @@ private CompletableFuture<Void> handleStateAndDoBuildIndexAsync(boolean markRead
271
263
doIndex ?
272
264
buildIndexInternalAsync ().thenApply (ignore -> markReadable ) :
273
265
AsyncUtil .READY_FALSE
274
- ).thenCompose (this ::markIndexReadable ).thenApply (ignore -> null );
266
+ ).thenCompose (this ::markIndexReadable
267
+ ).thenApply (ignore -> null );
275
268
}
276
269
277
270
private CompletableFuture <Void > markIndexesWriteOnly (boolean continueBuild , FDBRecordStore store ) {
@@ -317,7 +310,7 @@ public CompletableFuture<Boolean> markIndexReadable(boolean markReadablePlease)
317
310
// Mark each index readable in its own (retriable, parallel) transaction. If one target fails to become
318
311
// readable, it should not affect the others.
319
312
return forEachTargetIndex (index ->
320
- markIndexReadableSingleTarget (index , anythingChanged , runtimeExceptionAtomicReference )
313
+ markIndexReadableForIndex (index , anythingChanged , runtimeExceptionAtomicReference )
321
314
).thenApply (ignore -> {
322
315
RuntimeException ex = runtimeExceptionAtomicReference .get ();
323
316
if (ex != null ) {
@@ -328,13 +321,13 @@ public CompletableFuture<Boolean> markIndexReadable(boolean markReadablePlease)
328
321
});
329
322
}
330
323
331
- private CompletableFuture <Boolean > markIndexReadableSingleTarget (Index index , AtomicBoolean anythingChanged ,
332
- AtomicReference <RuntimeException > runtimeExceptionAtomicReference ) {
324
+ private CompletableFuture <Boolean > markIndexReadableForIndex (Index index , AtomicBoolean anythingChanged ,
325
+ AtomicReference <RuntimeException > runtimeExceptionAtomicReference ) {
333
326
// An extension function to reduce markIndexReadable's complexity
334
327
return getRunner ().runAsync (context ->
335
328
common .getRecordStoreBuilder ().copyBuilder ().setContext (context ).openAsync ()
336
329
.thenCompose (store -> {
337
- clearHeartbeatSingleTarget (store , index );
330
+ clearHeartbeatForIndex (store , index );
338
331
return policy .shouldAllowUniquePendingState (store ) ?
339
332
store .markIndexReadableOrUniquePending (index ) :
340
333
store .markIndexReadable (index );
@@ -376,7 +369,7 @@ private CompletableFuture<Void> setIndexingTypeOrThrow(FDBRecordStore store, boo
376
369
if (forceStampOverwrite && !continuedBuild ) {
377
370
// Fresh session + overwrite = no questions asked
378
371
store .saveIndexingTypeStamp (index , newStamp );
379
- return AsyncUtil .DONE ;
372
+ return AsyncUtil .DONE ;
380
373
}
381
374
return store .loadIndexingTypeStampAsync (index )
382
375
.thenCompose (savedStamp -> {
@@ -858,30 +851,27 @@ private CompletableFuture<Void> updateHeartbeat(boolean validate, FDBRecordStore
858
851
859
852
private CompletableFuture <Void > clearHeartbeats () {
860
853
if (heartbeat == null ) {
854
+ // Here: either silent heartbeats or heartbeats had been cleared during markReadable phase
861
855
return AsyncUtil .DONE ;
862
856
}
863
- return forEachTargetIndex (this ::clearHeartbeatSingleTarget )
864
- .thenAccept (ignore -> heartbeat = null );
857
+ // Here: for each index we clear (only) the heartbeat generated by this indexer. This is a quick operation that can be done in a single transaction.
858
+ return getRunner ().runAsync (context ->
859
+ common .getRecordStoreBuilder ().copyBuilder ().setContext (context ).openAsync ()
860
+ .thenApply (store -> {
861
+ clearHeartbeats (store );
862
+ return null ;
863
+ }));
865
864
}
866
865
867
866
private void clearHeartbeats (FDBRecordStore store ) {
868
867
if (heartbeat != null ) {
869
868
for (Index index : common .getTargetIndexes ()) {
870
- clearHeartbeatSingleTarget (store , index );
869
+ heartbeat . clearHeartbeat (store , index );
871
870
}
872
871
}
873
872
}
874
873
875
- private CompletableFuture <Void > clearHeartbeatSingleTarget (Index index ) {
876
- return getRunner ().runAsync (context ->
877
- common .getRecordStoreBuilder ().copyBuilder ().setContext (context ).openAsync ()
878
- .thenApply (store -> {
879
- clearHeartbeatSingleTarget (store , index );
880
- return null ;
881
- }));
882
- }
883
-
884
- private void clearHeartbeatSingleTarget (FDBRecordStore store , Index index ) {
874
+ private void clearHeartbeatForIndex (FDBRecordStore store , Index index ) {
885
875
if (heartbeat != null ) {
886
876
heartbeat .clearHeartbeat (store , index );
887
877
}
@@ -1115,9 +1105,9 @@ public CompletableFuture<Map<UUID, IndexBuildProto.IndexBuildHeartbeat>> getInde
1115
1105
.thenCompose (store -> IndexingHeartbeat .getIndexingHeartbeats (store , common .getPrimaryIndex (), maxCount )));
1116
1106
}
1117
1107
1118
- public CompletableFuture <Integer > clearIndexingHeartbeats (long minAgenMilliseconds , int maxIteration ) {
1108
+ public CompletableFuture <Integer > clearIndexingHeartbeats (long minAgeMilliseconds , int maxIteration ) {
1119
1109
return getRunner ().runAsync (context -> openRecordStore (context )
1120
- .thenCompose (store -> IndexingHeartbeat .clearIndexingHeartbeats (store , common .getPrimaryIndex (), minAgenMilliseconds , maxIteration )));
1110
+ .thenCompose (store -> IndexingHeartbeat .clearIndexingHeartbeats (store , common .getPrimaryIndex (), minAgeMilliseconds , maxIteration )));
1121
1111
}
1122
1112
1123
1113
/**
0 commit comments