13
13
import org .apache .lucene .store .AlreadyClosedException ;
14
14
import org .apache .lucene .store .IndexInput ;
15
15
import org .opensearch .common .annotation .ExperimentalApi ;
16
+ import org .opensearch .common .util .concurrent .OpenSearchExecutors ;
16
17
17
18
import java .io .IOException ;
19
+ import java .lang .ref .Cleaner ;
18
20
import java .nio .file .Path ;
19
- import java .util .HashSet ;
20
- import java .util .Set ;
21
21
22
22
/**
23
23
* Extension of {@link FileCachedIndexInput} for full files for handling clones and slices
24
- * We maintain a clone map so that we can close them when the parent IndexInput is closed so that ref count is properly maintained in file cache
25
- * Closing of clones explicitly is needed as Lucene does not guarantee that it will close the clones
24
+ * Since Lucene does not guarantee that it will close the clones/slices, we have created a Cleaner which handles closing of the clones/slices when they become phantom reachable
26
25
* https://github.com/apache/lucene/blob/8340b01c3cc229f33584ce2178b07b8984daa6a9/lucene/core/src/java/org/apache/lucene/store/IndexInput.java#L32-L33
27
26
* @opensearch.experimental
28
27
*/
29
28
@ ExperimentalApi
30
29
public class FullFileCachedIndexInput extends FileCachedIndexInput {
31
30
private static final Logger logger = LogManager .getLogger (FullFileCachedIndexInput .class );
32
- private final Set <FullFileCachedIndexInput > clones ;
31
+ private final IndexInputHolder indexInputHolder ;
32
+ private static final Cleaner CLEANER = Cleaner .create (OpenSearchExecutors .daemonThreadFactory ("index-input-cleaner" ));
33
33
34
34
public FullFileCachedIndexInput (FileCache cache , Path filePath , IndexInput underlyingIndexInput ) {
35
35
this (cache , filePath , underlyingIndexInput , false );
36
36
}
37
37
38
38
public FullFileCachedIndexInput (FileCache cache , Path filePath , IndexInput underlyingIndexInput , boolean isClone ) {
39
39
super (cache , filePath , underlyingIndexInput , isClone );
40
- clones = new HashSet <>();
40
+ indexInputHolder = new IndexInputHolder (underlyingIndexInput , isClone , cache , filePath );
41
+ CLEANER .register (this , indexInputHolder );
41
42
}
42
43
43
44
/**
@@ -48,7 +49,6 @@ public FullFileCachedIndexInput(FileCache cache, Path filePath, IndexInput under
48
49
public FullFileCachedIndexInput clone () {
49
50
FullFileCachedIndexInput clonedIndexInput = new FullFileCachedIndexInput (cache , filePath , luceneIndexInput .clone (), true );
50
51
cache .incRef (filePath );
51
- clones .add (clonedIndexInput );
52
52
return clonedIndexInput ;
53
53
}
54
54
@@ -74,7 +74,6 @@ public IndexInput slice(String sliceDescription, long offset, long length) throw
74
74
}
75
75
IndexInput slicedLuceneIndexInput = luceneIndexInput .slice (sliceDescription , offset , length );
76
76
FullFileCachedIndexInput slicedIndexInput = new FullFileCachedIndexInput (cache , filePath , slicedLuceneIndexInput , true );
77
- clones .add (slicedIndexInput );
78
77
cache .incRef (filePath );
79
78
return slicedIndexInput ;
80
79
}
@@ -88,21 +87,37 @@ public void close() throws IOException {
88
87
if (isClone ) {
89
88
cache .decRef (filePath );
90
89
}
91
- clones .forEach (indexInput -> {
92
- try {
93
- indexInput .close ();
94
- } catch (Exception e ) {
95
- logger .trace ("Exception while closing clone - {}" , e .getMessage ());
96
- }
97
- });
98
90
try {
99
91
luceneIndexInput .close ();
100
92
} catch (AlreadyClosedException e ) {
101
93
logger .trace ("FullFileCachedIndexInput already closed" );
102
94
}
103
95
luceneIndexInput = null ;
104
- clones .clear ();
105
96
closed = true ;
106
97
}
107
98
}
99
+
100
+ private static class IndexInputHolder implements Runnable {
101
+ private final IndexInput indexInput ;
102
+ private final FileCache cache ;
103
+ private final boolean isClone ;
104
+ private final Path path ;
105
+
106
+ IndexInputHolder (IndexInput indexInput , boolean isClone , FileCache cache , Path path ) {
107
+ this .indexInput = indexInput ;
108
+ this .isClone = isClone ;
109
+ this .cache = cache ;
110
+ this .path = path ;
111
+ }
112
+
113
+ @ Override
114
+ public void run () {
115
+ try {
116
+ indexInput .close ();
117
+ if (isClone ) cache .decRef (path );
118
+ } catch (IOException e ) {
119
+ logger .error ("Failed to close IndexInput while clearing phantom reachable object" );
120
+ }
121
+ }
122
+ }
108
123
}
0 commit comments