@@ -213,8 +213,10 @@ public class IndexShard extends AbstractIndexShardComponent implements IndicesCl
213
213
214
214
protected volatile ShardRouting shardRouting ;
215
215
protected volatile IndexShardState state ;
216
+ // ensure happens-before relation between addRefreshListener() and postRecovery()
217
+ private final Object postRecoveryMutex = new Object ();
216
218
private volatile long pendingPrimaryTerm ; // see JavaDocs for getPendingPrimaryTerm
217
- private final Object engineMutex = new Object ();
219
+ private final Object engineMutex = new Object (); // lock ordering: engineMutex -> mutex
218
220
private final AtomicReference <Engine > currentEngineReference = new AtomicReference <>();
219
221
final EngineFactory engineFactory ;
220
222
@@ -1338,23 +1340,24 @@ public void close(String reason, boolean flushEngine) throws IOException {
1338
1340
}
1339
1341
}
1340
1342
1341
- public IndexShard postRecovery (String reason )
1342
- throws IndexShardStartedException , IndexShardRelocatedException , IndexShardClosedException {
1343
- synchronized (mutex ) {
1344
- if (state == IndexShardState .CLOSED ) {
1345
- throw new IndexShardClosedException (shardId );
1346
- }
1347
- if (state == IndexShardState .STARTED ) {
1348
- throw new IndexShardStartedException (shardId );
1343
+ public void postRecovery (String reason ) throws IndexShardStartedException , IndexShardRelocatedException , IndexShardClosedException {
1344
+ synchronized (postRecoveryMutex ) {
1345
+ // we need to refresh again to expose all operations that were index until now. Otherwise
1346
+ // we may not expose operations that were indexed with a refresh listener that was immediately
1347
+ // responded to in addRefreshListener. The refresh must happen under the same mutex used in addRefreshListener
1348
+ // and before moving this shard to POST_RECOVERY state (i.e., allow to read from this shard).
1349
+ getEngine ().refresh ("post_recovery" );
1350
+ synchronized (mutex ) {
1351
+ if (state == IndexShardState .CLOSED ) {
1352
+ throw new IndexShardClosedException (shardId );
1353
+ }
1354
+ if (state == IndexShardState .STARTED ) {
1355
+ throw new IndexShardStartedException (shardId );
1356
+ }
1357
+ recoveryState .setStage (RecoveryState .Stage .DONE );
1358
+ changeState (IndexShardState .POST_RECOVERY , reason );
1349
1359
}
1350
- recoveryState .setStage (RecoveryState .Stage .DONE );
1351
- changeState (IndexShardState .POST_RECOVERY , reason );
1352
1360
}
1353
- // we need to refresh again to expose all operations that were index until now. Otherwise
1354
- // we may not expose operations that were indexed with a refresh listener that was immediately
1355
- // responded to in addRefreshListener.
1356
- getEngine ().refresh ("post_recovery" );
1357
- return this ;
1358
1361
}
1359
1362
1360
1363
/**
@@ -3257,10 +3260,10 @@ public void addRefreshListener(Translog.Location location, Consumer<Boolean> lis
3257
3260
if (isReadAllowed ()) {
3258
3261
readAllowed = true ;
3259
3262
} else {
3260
- // check again under mutex . this is important to create a happens before relationship
3263
+ // check again under postRecoveryMutex . this is important to create a happens before relationship
3261
3264
// between the switch to POST_RECOVERY + associated refresh. Otherwise we may respond
3262
3265
// to a listener before a refresh actually happened that contained that operation.
3263
- synchronized (mutex ) {
3266
+ synchronized (postRecoveryMutex ) {
3264
3267
readAllowed = isReadAllowed ();
3265
3268
}
3266
3269
}
0 commit comments