@@ -110,6 +110,7 @@ public class InternalEngine extends Engine {
110
110
111
111
private final SearcherFactory searcherFactory ;
112
112
private final SearcherManager searcherManager ;
113
+ private final SearcherManager internalSearcherManager ;
113
114
114
115
private final Lock flushLock = new ReentrantLock ();
115
116
private final ReentrantLock optimizeLock = new ReentrantLock ();
@@ -164,6 +165,7 @@ public InternalEngine(EngineConfig engineConfig) throws EngineException {
164
165
IndexWriter writer = null ;
165
166
Translog translog = null ;
166
167
SearcherManager manager = null ;
168
+ SearcherManager internalSearcherManager = null ;
167
169
EngineMergeScheduler scheduler = null ;
168
170
boolean success = false ;
169
171
try {
@@ -215,9 +217,11 @@ public InternalEngine(EngineConfig engineConfig) throws EngineException {
215
217
throw e ;
216
218
}
217
219
}
218
- manager = createSearcherManager ();
220
+ manager = createSearcherManager (searcherFactory );
221
+ internalSearcherManager = createSearcherManager (new SearcherFactory ());
222
+ this .internalSearcherManager = internalSearcherManager ;
219
223
this .searcherManager = manager ;
220
- this .versionMap .setManager (searcherManager );
224
+ this .versionMap .setManager (internalSearcherManager );
221
225
assert pendingTranslogRecovery .get () == false : "translog recovery can't be pending before we set it" ;
222
226
// don't allow commits until we are done with recovering
223
227
pendingTranslogRecovery .set (openMode == EngineConfig .OpenMode .OPEN_INDEX_AND_TRANSLOG );
@@ -227,7 +231,7 @@ public InternalEngine(EngineConfig engineConfig) throws EngineException {
227
231
success = true ;
228
232
} finally {
229
233
if (success == false ) {
230
- IOUtils .closeWhileHandlingException (writer , translog , manager , scheduler );
234
+ IOUtils .closeWhileHandlingException (writer , translog , manager , internalSearcherManager , scheduler );
231
235
versionMap .clear ();
232
236
if (isClosed .get () == false ) {
233
237
// failure we need to dec the store reference
@@ -441,7 +445,7 @@ private String loadOrGenerateHistoryUUID(final IndexWriter writer, boolean force
441
445
return uuid ;
442
446
}
443
447
444
- private SearcherManager createSearcherManager () throws EngineException {
448
+ private SearcherManager createSearcherManager (SearcherFactory searcherFactory ) throws EngineException {
445
449
boolean success = false ;
446
450
SearcherManager searcherManager = null ;
447
451
try {
@@ -482,7 +486,7 @@ public GetResult get(Get get, Function<String, Searcher> searcherFactory) throws
482
486
throw new VersionConflictEngineException (shardId , get .type (), get .id (),
483
487
get .versionType ().explainConflictForReads (versionValue .version , get .version ()));
484
488
}
485
- refresh ("realtime_get" );
489
+ refresh ("realtime_get" , false );
486
490
}
487
491
}
488
492
@@ -1187,17 +1191,26 @@ private NoOpResult innerNoOp(final NoOp noOp) throws IOException {
1187
1191
1188
1192
@ Override
1189
1193
public void refresh (String source ) throws EngineException {
1194
+ refresh (source , true );
1195
+ }
1196
+
1197
+ final void refresh (String source , boolean refreshExternal ) throws EngineException {
1190
1198
// we obtain a read lock here, since we don't want a flush to happen while we are refreshing
1191
1199
// since it flushes the index as well (though, in terms of concurrency, we are allowed to do it)
1192
1200
try (ReleasableLock lock = readLock .acquire ()) {
1193
1201
ensureOpen ();
1194
- searcherManager .maybeRefreshBlocking ();
1202
+ internalSearcherManager .maybeRefreshBlocking ();
1203
+ if (refreshExternal ) {
1204
+ // even though we maintain 2 managers we really do the heavy-lifting only once.
1205
+ // the second refresh will only do the extra work we have to do for warming caches etc.
1206
+ searcherManager .maybeRefreshBlocking ();
1207
+ }
1195
1208
} catch (AlreadyClosedException e ) {
1196
1209
failOnTragicEvent (e );
1197
1210
throw e ;
1198
1211
} catch (Exception e ) {
1199
1212
try {
1200
- failEngine ("refresh failed" , e );
1213
+ failEngine ("refresh failed source[" + source + "] " , e );
1201
1214
} catch (Exception inner ) {
1202
1215
e .addSuppressed (inner );
1203
1216
}
@@ -1219,10 +1232,6 @@ public void writeIndexingBuffer() throws EngineException {
1219
1232
// since it flushes the index as well (though, in terms of concurrency, we are allowed to do it)
1220
1233
try (ReleasableLock lock = readLock .acquire ()) {
1221
1234
ensureOpen ();
1222
-
1223
- // TODO: it's not great that we secretly tie searcher visibility to "freeing up heap" here... really we should keep two
1224
- // searcher managers, one for searching which is only refreshed by the schedule the user requested (refresh_interval, or invoking
1225
- // refresh API), and another for version map interactions. See #15768.
1226
1235
final long versionMapBytes = versionMap .ramBytesUsedForRefresh ();
1227
1236
final long indexingBufferBytes = indexWriter .ramBytesUsed ();
1228
1237
@@ -1231,7 +1240,7 @@ public void writeIndexingBuffer() throws EngineException {
1231
1240
// The version map is using > 25% of the indexing buffer, so we do a refresh so the version map also clears
1232
1241
logger .debug ("use refresh to write indexing buffer (heap size=[{}]), to also clear version map (heap size=[{}])" ,
1233
1242
new ByteSizeValue (indexingBufferBytes ), new ByteSizeValue (versionMapBytes ));
1234
- refresh ("write indexing buffer" );
1243
+ refresh ("write indexing buffer" , false );
1235
1244
} else {
1236
1245
// Most of our heap is used by the indexing buffer, so we do a cheaper (just writes segments, doesn't open a new searcher) IW.flush:
1237
1246
logger .debug ("use IndexWriter.flush to write indexing buffer (heap size=[{}]) since version map is small (heap size=[{}])" ,
@@ -1303,9 +1312,8 @@ final boolean tryRenewSyncCommit() {
1303
1312
throw new EngineException (shardId , "failed to renew sync commit" , ex );
1304
1313
}
1305
1314
if (renewed ) { // refresh outside of the write lock
1306
- refresh ("renew sync commit" );
1315
+ refresh ("renew sync commit" ); // we have to refresh both searchers here to ensure we release unreferenced segments.
1307
1316
}
1308
-
1309
1317
return renewed ;
1310
1318
}
1311
1319
@@ -1347,7 +1355,7 @@ public CommitId flush(boolean force, boolean waitIfOngoing) throws EngineExcepti
1347
1355
commitIndexWriter (indexWriter , translog , null );
1348
1356
logger .trace ("finished commit for flush" );
1349
1357
// we need to refresh in order to clear older version values
1350
- refresh ("version_table_flush" );
1358
+ refresh ("version_table_flush" ); // TODO technically we could also only refresh the internal searcher
1351
1359
translog .trimUnreferencedReaders ();
1352
1360
} catch (Exception e ) {
1353
1361
throw new FlushFailedEngineException (shardId , e );
@@ -1500,6 +1508,8 @@ public void forceMerge(final boolean flush, int maxNumSegments, boolean onlyExpu
1500
1508
if (flush ) {
1501
1509
if (tryRenewSyncCommit () == false ) {
1502
1510
flush (false , true );
1511
+ } else {
1512
+ refresh ("renew sync commit" ); // we have to refresh both searchers here to ensure we release unreferenced segments.
1503
1513
}
1504
1514
}
1505
1515
if (upgrade ) {
@@ -1652,7 +1662,7 @@ protected final void closeNoLock(String reason, CountDownLatch closedLatch) {
1652
1662
try {
1653
1663
this .versionMap .clear ();
1654
1664
try {
1655
- IOUtils .close (searcherManager );
1665
+ IOUtils .close (searcherManager , internalSearcherManager );
1656
1666
} catch (Exception e ) {
1657
1667
logger .warn ("Failed to close SearcherManager" , e );
1658
1668
}
@@ -1684,8 +1694,15 @@ protected final void closeNoLock(String reason, CountDownLatch closedLatch) {
1684
1694
}
1685
1695
1686
1696
@ Override
1687
- protected SearcherManager getSearcherManager () {
1688
- return searcherManager ;
1697
+ protected SearcherManager getSearcherManager (String source , SearcherScope scope ) {
1698
+ switch (scope ) {
1699
+ case GET :
1700
+ return internalSearcherManager ;
1701
+ case SEARCH :
1702
+ return searcherManager ;
1703
+ default :
1704
+ throw new IllegalStateException ("unknonw scope: " + scope );
1705
+ }
1689
1706
}
1690
1707
1691
1708
private Releasable acquireLock (BytesRef uid ) {
@@ -1867,6 +1884,10 @@ protected void doRun() throws Exception {
1867
1884
// free up transient disk usage of the (presumably biggish) segments that were just merged
1868
1885
if (tryRenewSyncCommit () == false ) {
1869
1886
flush ();
1887
+ } else {
1888
+ // we only refresh the rather cheap internal searcher manager in order to not trigger new datastructures
1889
+ // by accident ie. warm big segments in parent child case etc.
1890
+ refresh ("renew sync commit" , false );
1870
1891
}
1871
1892
}
1872
1893
});
0 commit comments