99
1010package org .elasticsearch .index .engine ;
1111
12+ import org .apache .lucene .codecs .StoredFieldsReader ;
1213import org .apache .lucene .index .BaseTermsEnum ;
1314import org .apache .lucene .index .BinaryDocValues ;
1415import org .apache .lucene .index .ByteVectorValues ;
4647import org .apache .lucene .util .Bits ;
4748import org .apache .lucene .util .BytesRef ;
4849import org .elasticsearch .common .bytes .BytesReference ;
50+ import org .elasticsearch .common .lucene .Lucene ;
51+ import org .elasticsearch .common .lucene .index .ElasticsearchDirectoryReader ;
52+ import org .elasticsearch .common .lucene .index .SequentialStoredFieldsLeafReader ;
4953import org .elasticsearch .common .xcontent .XContentHelper ;
5054import org .elasticsearch .core .IOUtils ;
5155import org .elasticsearch .index .fieldvisitor .FieldNamesProvidingStoredFieldsVisitor ;
7680 * into an in-memory Lucene segment that is created on-demand.
7781 */
7882final class TranslogDirectoryReader extends DirectoryReader {
79- private final TranslogLeafReader leafReader ;
83+ private final LeafReader leafReader ;
8084
81- TranslogDirectoryReader (
85+ static DirectoryReader create (
8286 ShardId shardId ,
8387 Translog .Index operation ,
8488 MappingLookup mappingLookup ,
8589 DocumentParser documentParser ,
8690 EngineConfig engineConfig ,
8791 Runnable onSegmentCreated
8892 ) throws IOException {
89- this (new TranslogLeafReader (shardId , operation , mappingLookup , documentParser , engineConfig , onSegmentCreated ));
93+ final Directory directory = new ByteBuffersDirectory ();
94+ boolean success = false ;
95+ try {
96+ final LeafReader leafReader ;
97+ // When using synthetic source, the translog operation must always be reindexed into an in-memory Lucene to ensure consistent
98+ // output for realtime-get operations. However, this can degrade the performance of realtime-get and update operations.
99+ // If slight inconsistencies in realtime-get operations are acceptable, the translog operation can be reindexed lazily.
100+ if (mappingLookup .isSourceSynthetic ()) {
101+ onSegmentCreated .run ();
102+ leafReader = createInMemoryReader (shardId , engineConfig , directory , documentParser , mappingLookup , false , operation );
103+ } else {
104+ leafReader = new TranslogLeafReader (
105+ shardId ,
106+ operation ,
107+ mappingLookup ,
108+ documentParser ,
109+ engineConfig ,
110+ directory ,
111+ onSegmentCreated
112+ );
113+ }
114+ var directoryReader = ElasticsearchDirectoryReader .wrap (new TranslogDirectoryReader (directory , leafReader ), shardId );
115+ success = true ;
116+ return directoryReader ;
117+ } finally {
118+ if (success == false ) {
119+ IOUtils .closeWhileHandlingException (directory );
120+ }
121+ }
90122 }
91123
92- private TranslogDirectoryReader (TranslogLeafReader leafReader ) throws IOException {
93- super (leafReader . directory , new LeafReader [] { leafReader }, null );
124+ private TranslogDirectoryReader (Directory directory , LeafReader leafReader ) throws IOException {
125+ super (directory , new LeafReader [] { leafReader }, null );
94126 this .leafReader = leafReader ;
95127 }
96128
@@ -139,12 +171,13 @@ public CacheHelper getReaderCacheHelper() {
139171 return leafReader .getReaderCacheHelper ();
140172 }
141173
142- static DirectoryReader createInMemoryReader (
174+ private static LeafReader createInMemoryReader (
143175 ShardId shardId ,
144176 EngineConfig engineConfig ,
145177 Directory directory ,
146178 DocumentParser documentParser ,
147179 MappingLookup mappingLookup ,
180+ boolean rootDocOnly ,
148181 Translog .Index operation
149182 ) {
150183 final ParsedDocument parsedDocs = documentParser .parseDocument (
@@ -159,13 +192,55 @@ static DirectoryReader createInMemoryReader(
159192 IndexWriterConfig .OpenMode .CREATE
160193 ).setCodec (engineConfig .getCodec ());
161194 try (IndexWriter writer = new IndexWriter (directory , writeConfig )) {
162- writer .addDocuments (parsedDocs .docs ());
195+ final int numDocs ;
196+ if (rootDocOnly ) {
197+ numDocs = 1 ;
198+ writer .addDocument (parsedDocs .rootDoc ());
199+ } else {
200+ numDocs = parsedDocs .docs ().size ();
201+ writer .addDocuments (parsedDocs .docs ());
202+ }
163203 final DirectoryReader reader = open (writer );
164- if (reader .leaves ().size () != 1 ) {
204+ if (reader .leaves ().size () != 1 || reader . leaves (). get ( 0 ). reader (). numDocs () != numDocs ) {
165205 reader .close ();
166- throw new IllegalStateException ("Expected a single segment; " + "but got[" + reader .leaves ().size () + "] segments" );
206+ throw new IllegalStateException (
207+ "Expected a single segment with "
208+ + numDocs
209+ + " documents, "
210+ + "but ["
211+ + reader .leaves ().size ()
212+ + " segments with "
213+ + reader .leaves ().get (0 ).reader ().numDocs ()
214+ + " documents"
215+ );
167216 }
168- return reader ;
217+ LeafReader leafReader = reader .leaves ().get (0 ).reader ();
218+ return new SequentialStoredFieldsLeafReader (leafReader ) {
219+ @ Override
220+ protected void doClose () throws IOException {
221+ IOUtils .close (super ::doClose , directory );
222+ }
223+
224+ @ Override
225+ public CacheHelper getCoreCacheHelper () {
226+ return leafReader .getCoreCacheHelper ();
227+ }
228+
229+ @ Override
230+ public CacheHelper getReaderCacheHelper () {
231+ return leafReader .getReaderCacheHelper ();
232+ }
233+
234+ @ Override
235+ public StoredFieldsReader getSequentialStoredFieldsReader () {
236+ return Lucene .segmentReader (leafReader ).getFieldsReader ().getMergeInstance ();
237+ }
238+
239+ @ Override
240+ protected StoredFieldsReader doGetSequentialStoredFieldsReader (StoredFieldsReader reader ) {
241+ return reader ;
242+ }
243+ };
169244 } catch (IOException e ) {
170245 throw new EngineException (shardId , "failed to create an in-memory segment for get [" + operation .id () + "]" , e );
171246 }
@@ -252,6 +327,7 @@ private static class TranslogLeafReader extends LeafReader {
252327 MappingLookup mappingLookup ,
253328 DocumentParser documentParser ,
254329 EngineConfig engineConfig ,
330+ Directory directory ,
255331 Runnable onSegmentCreated
256332 ) {
257333 this .shardId = shardId ;
@@ -260,7 +336,7 @@ private static class TranslogLeafReader extends LeafReader {
260336 this .documentParser = documentParser ;
261337 this .engineConfig = engineConfig ;
262338 this .onSegmentCreated = onSegmentCreated ;
263- this .directory = new ByteBuffersDirectory () ;
339+ this .directory = directory ;
264340 this .uid = Uid .encodeId (operation .id ());
265341 }
266342
@@ -272,7 +348,15 @@ private LeafReader getDelegate() {
272348 ensureOpen ();
273349 reader = delegate .get ();
274350 if (reader == null ) {
275- var indexReader = createInMemoryReader (shardId , engineConfig , directory , documentParser , mappingLookup , operation );
351+ var indexReader = createInMemoryReader (
352+ shardId ,
353+ engineConfig ,
354+ directory ,
355+ documentParser ,
356+ mappingLookup ,
357+ true ,
358+ operation
359+ );
276360 reader = indexReader .leaves ().get (0 ).reader ();
277361 final LeafReader existing = delegate .getAndSet (reader );
278362 assert existing == null ;
@@ -457,7 +541,12 @@ private void readStoredFieldsDirectly(StoredFieldVisitor visitor) throws IOExcep
457541
458542 @ Override
459543 protected synchronized void doClose () throws IOException {
460- IOUtils .close (delegate .get (), directory );
544+ final LeafReader leaf = delegate .get ();
545+ if (leaf != null ) {
546+ leaf .close ();
547+ } else {
548+ directory .close ();
549+ }
461550 }
462551 }
463552
0 commit comments