9
9
10
10
package org .elasticsearch .index .engine ;
11
11
12
+ import org .apache .lucene .codecs .StoredFieldsReader ;
12
13
import org .apache .lucene .index .BaseTermsEnum ;
13
14
import org .apache .lucene .index .BinaryDocValues ;
14
15
import org .apache .lucene .index .ByteVectorValues ;
45
46
import org .apache .lucene .util .Bits ;
46
47
import org .apache .lucene .util .BytesRef ;
47
48
import org .elasticsearch .common .bytes .BytesReference ;
49
+ import org .elasticsearch .common .lucene .Lucene ;
50
+ import org .elasticsearch .common .lucene .index .ElasticsearchDirectoryReader ;
51
+ import org .elasticsearch .common .lucene .index .SequentialStoredFieldsLeafReader ;
48
52
import org .elasticsearch .common .xcontent .XContentHelper ;
49
53
import org .elasticsearch .core .IOUtils ;
50
54
import org .elasticsearch .index .fieldvisitor .FieldNamesProvidingStoredFieldsVisitor ;
75
79
* into an in-memory Lucene segment that is created on-demand.
76
80
*/
77
81
final class TranslogDirectoryReader extends DirectoryReader {
78
- private final TranslogLeafReader leafReader ;
82
+ private final LeafReader leafReader ;
79
83
80
- TranslogDirectoryReader (
84
+ static DirectoryReader create (
81
85
ShardId shardId ,
82
86
Translog .Index operation ,
83
87
MappingLookup mappingLookup ,
84
88
DocumentParser documentParser ,
85
89
EngineConfig engineConfig ,
86
90
Runnable onSegmentCreated
87
91
) throws IOException {
88
- this (new TranslogLeafReader (shardId , operation , mappingLookup , documentParser , engineConfig , onSegmentCreated ));
92
+ final Directory directory = new ByteBuffersDirectory ();
93
+ boolean success = false ;
94
+ try {
95
+ final LeafReader leafReader ;
96
+ // When using synthetic source, the translog operation must always be reindexed into an in-memory Lucene to ensure consistent
97
+ // output for realtime-get operations. However, this can degrade the performance of realtime-get and update operations.
98
+ // If slight inconsistencies in realtime-get operations are acceptable, the translog operation can be reindexed lazily.
99
+ if (mappingLookup .isSourceSynthetic ()) {
100
+ onSegmentCreated .run ();
101
+ leafReader = createInMemoryReader (shardId , engineConfig , directory , documentParser , mappingLookup , false , operation );
102
+ } else {
103
+ leafReader = new TranslogLeafReader (
104
+ shardId ,
105
+ operation ,
106
+ mappingLookup ,
107
+ documentParser ,
108
+ engineConfig ,
109
+ directory ,
110
+ onSegmentCreated
111
+ );
112
+ }
113
+ var directoryReader = ElasticsearchDirectoryReader .wrap (new TranslogDirectoryReader (directory , leafReader ), shardId );
114
+ success = true ;
115
+ return directoryReader ;
116
+ } finally {
117
+ if (success == false ) {
118
+ IOUtils .closeWhileHandlingException (directory );
119
+ }
120
+ }
89
121
}
90
122
91
- private TranslogDirectoryReader (TranslogLeafReader leafReader ) throws IOException {
92
- super (leafReader . directory , new LeafReader [] { leafReader }, null );
123
+ private TranslogDirectoryReader (Directory directory , LeafReader leafReader ) throws IOException {
124
+ super (directory , new LeafReader [] { leafReader }, null );
93
125
this .leafReader = leafReader ;
94
126
}
95
127
@@ -138,12 +170,13 @@ public CacheHelper getReaderCacheHelper() {
138
170
return leafReader .getReaderCacheHelper ();
139
171
}
140
172
141
- static DirectoryReader createInMemoryReader (
173
+ private static LeafReader createInMemoryReader (
142
174
ShardId shardId ,
143
175
EngineConfig engineConfig ,
144
176
Directory directory ,
145
177
DocumentParser documentParser ,
146
178
MappingLookup mappingLookup ,
179
+ boolean rootDocOnly ,
147
180
Translog .Index operation
148
181
) {
149
182
final ParsedDocument parsedDocs = documentParser .parseDocument (
@@ -158,13 +191,55 @@ static DirectoryReader createInMemoryReader(
158
191
IndexWriterConfig .OpenMode .CREATE
159
192
).setCodec (engineConfig .getCodec ());
160
193
try (IndexWriter writer = new IndexWriter (directory , writeConfig )) {
161
- writer .addDocuments (parsedDocs .docs ());
194
+ final int numDocs ;
195
+ if (rootDocOnly ) {
196
+ numDocs = 1 ;
197
+ writer .addDocument (parsedDocs .rootDoc ());
198
+ } else {
199
+ numDocs = parsedDocs .docs ().size ();
200
+ writer .addDocuments (parsedDocs .docs ());
201
+ }
162
202
final DirectoryReader reader = open (writer );
163
- if (reader .leaves ().size () != 1 ) {
203
+ if (reader .leaves ().size () != 1 || reader . leaves (). get ( 0 ). reader (). numDocs () != numDocs ) {
164
204
reader .close ();
165
- throw new IllegalStateException ("Expected a single segment; " + "but got[" + reader .leaves ().size () + "] segments" );
205
+ throw new IllegalStateException (
206
+ "Expected a single segment with "
207
+ + numDocs
208
+ + " documents, "
209
+ + "but ["
210
+ + reader .leaves ().size ()
211
+ + " segments with "
212
+ + reader .leaves ().get (0 ).reader ().numDocs ()
213
+ + " documents"
214
+ );
166
215
}
167
- return reader ;
216
+ LeafReader leafReader = reader .leaves ().get (0 ).reader ();
217
+ return new SequentialStoredFieldsLeafReader (leafReader ) {
218
+ @ Override
219
+ protected void doClose () throws IOException {
220
+ IOUtils .close (super ::doClose , directory );
221
+ }
222
+
223
+ @ Override
224
+ public CacheHelper getCoreCacheHelper () {
225
+ return leafReader .getCoreCacheHelper ();
226
+ }
227
+
228
+ @ Override
229
+ public CacheHelper getReaderCacheHelper () {
230
+ return leafReader .getReaderCacheHelper ();
231
+ }
232
+
233
+ @ Override
234
+ public StoredFieldsReader getSequentialStoredFieldsReader () {
235
+ return Lucene .segmentReader (leafReader ).getFieldsReader ().getMergeInstance ();
236
+ }
237
+
238
+ @ Override
239
+ protected StoredFieldsReader doGetSequentialStoredFieldsReader (StoredFieldsReader reader ) {
240
+ return reader ;
241
+ }
242
+ };
168
243
} catch (IOException e ) {
169
244
throw new EngineException (shardId , "failed to create an in-memory segment for get [" + operation .id () + "]" , e );
170
245
}
@@ -248,6 +323,7 @@ private static class TranslogLeafReader extends LeafReader {
248
323
MappingLookup mappingLookup ,
249
324
DocumentParser documentParser ,
250
325
EngineConfig engineConfig ,
326
+ Directory directory ,
251
327
Runnable onSegmentCreated
252
328
) {
253
329
this .shardId = shardId ;
@@ -256,7 +332,7 @@ private static class TranslogLeafReader extends LeafReader {
256
332
this .documentParser = documentParser ;
257
333
this .engineConfig = engineConfig ;
258
334
this .onSegmentCreated = onSegmentCreated ;
259
- this .directory = new ByteBuffersDirectory () ;
335
+ this .directory = directory ;
260
336
this .uid = Uid .encodeId (operation .id ());
261
337
}
262
338
@@ -268,7 +344,15 @@ private LeafReader getDelegate() {
268
344
ensureOpen ();
269
345
reader = delegate .get ();
270
346
if (reader == null ) {
271
- var indexReader = createInMemoryReader (shardId , engineConfig , directory , documentParser , mappingLookup , operation );
347
+ var indexReader = createInMemoryReader (
348
+ shardId ,
349
+ engineConfig ,
350
+ directory ,
351
+ documentParser ,
352
+ mappingLookup ,
353
+ true ,
354
+ operation
355
+ );
272
356
reader = indexReader .leaves ().get (0 ).reader ();
273
357
final LeafReader existing = delegate .getAndSet (reader );
274
358
assert existing == null ;
@@ -458,7 +542,12 @@ private void readStoredFieldsDirectly(StoredFieldVisitor visitor) throws IOExcep
458
542
459
543
@ Override
460
544
protected synchronized void doClose () throws IOException {
461
- IOUtils .close (delegate .get (), directory );
545
+ final LeafReader leaf = delegate .get ();
546
+ if (leaf != null ) {
547
+ leaf .close ();
548
+ } else {
549
+ directory .close ();
550
+ }
462
551
}
463
552
}
464
553
0 commit comments