5858import org .opensearch .index .mapper .NumberFieldMapper ;
5959import org .opensearch .index .mapper .TextFieldMapper ;
6060import org .opensearch .indices .IndicesService ;
61+ import org .opensearch .indices .cluster .IndicesClusterStateService ;
6162import org .opensearch .indices .fielddata .cache .IndicesFieldDataCache ;
6263import org .opensearch .plugins .Plugin ;
6364import org .opensearch .search .lookup .SearchLookup ;
6768import org .opensearch .threadpool .TestThreadPool ;
6869import org .opensearch .threadpool .ThreadPool ;
6970
71+ import java .io .IOException ;
7072import java .util .Arrays ;
7173import java .util .Collection ;
7274import java .util .Collections ;
@@ -93,7 +95,8 @@ public void testGetForFieldDefaults() {
9395 indexService .getIndexSettings (),
9496 indicesService .getIndicesFieldDataCache (),
9597 indicesService .getCircuitBreakerService (),
96- indexService .mapperService ()
98+ indexService .mapperService (),
99+ indexService .getThreadPool ()
97100 );
98101 final BuilderContext ctx = new BuilderContext (indexService .getIndexSettings ().getSettings (), new ContentPath (1 ));
99102 final MappedFieldType stringMapper = new KeywordFieldMapper .Builder ("string" ).build (ctx ).fieldType ();
@@ -127,14 +130,72 @@ public void testGetForFieldDefaults() {
127130 assertTrue (fd instanceof SortedNumericIndexFieldData );
128131 }
129132
133+ public void testIndexFieldDataCacheIsCleredAfterIndexRemoval () throws IOException , InterruptedException {
134+ final IndexService indexService = createIndex ("test" );
135+ final IndicesService indicesService = getInstanceFromNode (IndicesService .class );
136+ // copy the ifdService since we can set the listener only once.
137+ final IndexFieldDataService ifdService = new IndexFieldDataService (
138+ indexService .getIndexSettings (),
139+ indicesService .getIndicesFieldDataCache (),
140+ indicesService .getCircuitBreakerService (),
141+ indexService .mapperService (),
142+ indexService .getThreadPool ()
143+ );
144+
145+ final BuilderContext ctx = new BuilderContext (indexService .getIndexSettings ().getSettings (), new ContentPath (1 ));
146+ final MappedFieldType mapper1 = new TextFieldMapper .Builder ("field_1" , createDefaultIndexAnalyzers ()).fielddata (true )
147+ .build (ctx )
148+ .fieldType ();
149+ final MappedFieldType mapper2 = new TextFieldMapper .Builder ("field_2" , createDefaultIndexAnalyzers ()).fielddata (true )
150+ .build (ctx )
151+ .fieldType ();
152+ final IndexWriter writer = new IndexWriter (new ByteBuffersDirectory (), new IndexWriterConfig (new KeywordAnalyzer ()));
153+ Document doc = new Document ();
154+ doc .add (new StringField ("field_1" , "thisisastring" , Store .NO ));
155+ doc .add (new StringField ("field_2" , "thisisanotherstring" , Store .NO ));
156+ writer .addDocument (doc );
157+ final IndexReader reader = DirectoryReader .open (writer );
158+ final AtomicInteger onCacheCalled = new AtomicInteger ();
159+ final AtomicInteger onRemovalCalled = new AtomicInteger ();
160+ ifdService .setListener (new IndexFieldDataCache .Listener () {
161+ @ Override
162+ public void onCache (ShardId shardId , String fieldName , Accountable ramUsage ) {}
163+
164+ @ Override
165+ public void onRemoval (ShardId shardId , String fieldName , boolean wasEvicted , long sizeInBytes ) {}
166+ });
167+ IndexFieldData <?> ifd1 = ifdService .getForField (mapper1 , "test" , () -> { throw new UnsupportedOperationException (); });
168+ IndexFieldData <?> ifd2 = ifdService .getForField (mapper2 , "test" , () -> { throw new UnsupportedOperationException (); });
169+ LeafReaderContext leafReaderContext = reader .getContext ().leaves ().get (0 );
170+ LeafFieldData loadField1 = ifd1 .load (leafReaderContext );
171+ LeafFieldData loadField2 = ifd2 .load (leafReaderContext );
172+
173+ assertEquals (2 , indicesService .getIndicesFieldDataCache ().getCache ().count ());
174+
175+ // Remove index
176+ indicesService .removeIndex (
177+ indexService .index (),
178+ IndicesClusterStateService .AllocatedIndices .IndexRemovalReason .DELETED ,
179+ "Please delete!"
180+ );
181+
182+ waitUntil (() -> indicesService .getIndicesFieldDataCache ().getCache ().count () == 0 );
183+
184+ reader .close ();
185+ loadField1 .close ();
186+ loadField2 .close ();
187+ writer .close ();
188+ }
189+
130190 public void testGetForFieldRuntimeField () {
131191 final IndexService indexService = createIndex ("test" );
132192 final IndicesService indicesService = getInstanceFromNode (IndicesService .class );
133193 final IndexFieldDataService ifdService = new IndexFieldDataService (
134194 indexService .getIndexSettings (),
135195 indicesService .getIndicesFieldDataCache (),
136196 indicesService .getCircuitBreakerService (),
137- indexService .mapperService ()
197+ indexService .mapperService (),
198+ indexService .getThreadPool ()
138199 );
139200 final SetOnce <Supplier <SearchLookup >> searchLookupSetOnce = new SetOnce <>();
140201 MappedFieldType ft = mock (MappedFieldType .class );
@@ -159,7 +220,8 @@ public void testClearField() throws Exception {
159220 indexService .getIndexSettings (),
160221 indicesService .getIndicesFieldDataCache (),
161222 indicesService .getCircuitBreakerService (),
162- indexService .mapperService ()
223+ indexService .mapperService (),
224+ indexService .getThreadPool ()
163225 );
164226
165227 final BuilderContext ctx = new BuilderContext (indexService .getIndexSettings ().getSettings (), new ContentPath (1 ));
@@ -227,7 +289,8 @@ public void testFieldDataCacheListener() throws Exception {
227289 indexService .getIndexSettings (),
228290 indicesService .getIndicesFieldDataCache (),
229291 indicesService .getCircuitBreakerService (),
230- indexService .mapperService ()
292+ indexService .mapperService (),
293+ indexService .getThreadPool ()
231294 );
232295
233296 final BuilderContext ctx = new BuilderContext (indexService .getIndexSettings ().getSettings (), new ContentPath (1 ));
@@ -284,7 +347,8 @@ public void testSetCacheListenerTwice() {
284347 indexService .getIndexSettings (),
285348 indicesService .getIndicesFieldDataCache (),
286349 indicesService .getCircuitBreakerService (),
287- indexService .mapperService ()
350+ indexService .mapperService (),
351+ indexService .getThreadPool ()
288352 );
289353 // set it the first time...
290354 shardPrivateService .setListener (new IndexFieldDataCache .Listener () {
@@ -325,7 +389,8 @@ private void doTestRequireDocValues(MappedFieldType ft) {
325389 IndexSettingsModule .newIndexSettings ("test" , Settings .EMPTY ),
326390 cache ,
327391 null ,
328- null
392+ null ,
393+ threadPool
329394 );
330395 if (ft .hasDocValues ()) {
331396 ifds .getForField (ft , "test" , () -> { throw new UnsupportedOperationException (); }); // no exception
0 commit comments