14
14
import org .apache .logging .log4j .message .ParameterizedMessage ;
15
15
import org .apache .lucene .codecs .CodecUtil ;
16
16
import org .apache .lucene .index .CorruptIndexException ;
17
+ import org .apache .lucene .index .SegmentCommitInfo ;
18
+ import org .apache .lucene .index .SegmentInfo ;
17
19
import org .apache .lucene .index .SegmentInfos ;
18
20
import org .apache .lucene .store .ByteBuffersDataOutput ;
19
21
import org .apache .lucene .store .ByteBuffersIndexOutput ;
22
24
import org .apache .lucene .store .IOContext ;
23
25
import org .apache .lucene .store .IndexInput ;
24
26
import org .apache .lucene .store .IndexOutput ;
27
+ import org .apache .lucene .util .Version ;
25
28
import org .opensearch .ExceptionsHelper ;
26
29
import org .opensearch .action .ActionListener ;
27
30
import org .opensearch .common .UUIDs ;
@@ -217,6 +220,14 @@ public static class UploadedSegmentMetadata {
217
220
private final String checksum ;
218
221
private final long length ;
219
222
223
+ /**
224
+ * The Lucene major version that wrote the original segment files.
225
+ * As part of the Lucene version compatibility check, this version information stored in the metadata
226
+ * will be used to skip downloading the segment files unnecessarily
227
+ * if they were written by an incompatible Lucene version.
228
+ */
229
+ private int writtenByMajor ;
230
+
220
231
UploadedSegmentMetadata (String originalFilename , String uploadedFilename , String checksum , long length ) {
221
232
this .originalFilename = originalFilename ;
222
233
this .uploadedFilename = uploadedFilename ;
@@ -226,7 +237,14 @@ public static class UploadedSegmentMetadata {
226
237
227
238
@ Override
228
239
public String toString () {
229
- return String .join (SEPARATOR , originalFilename , uploadedFilename , checksum , String .valueOf (length ));
240
+ return String .join (
241
+ SEPARATOR ,
242
+ originalFilename ,
243
+ uploadedFilename ,
244
+ checksum ,
245
+ String .valueOf (length ),
246
+ String .valueOf (writtenByMajor )
247
+ );
230
248
}
231
249
232
250
public String getChecksum () {
@@ -239,12 +257,35 @@ public long getLength() {
239
257
240
258
public static UploadedSegmentMetadata fromString (String uploadedFilename ) {
241
259
String [] values = uploadedFilename .split (SEPARATOR );
242
- return new UploadedSegmentMetadata (values [0 ], values [1 ], values [2 ], Long .parseLong (values [3 ]));
260
+ UploadedSegmentMetadata metadata = new UploadedSegmentMetadata (values [0 ], values [1 ], values [2 ], Long .parseLong (values [3 ]));
261
+ if (values .length < 5 ) {
262
+ logger .error ("Lucene version is missing for UploadedSegmentMetadata: " + uploadedFilename );
263
+ }
264
+
265
+ metadata .setWrittenByMajor (Integer .parseInt (values [4 ]));
266
+
267
+ return metadata ;
243
268
}
244
269
245
270
public String getOriginalFilename () {
246
271
return originalFilename ;
247
272
}
273
+
274
+ public void setWrittenByMajor (int writtenByMajor ) {
275
+ if (writtenByMajor <= Version .LATEST .major && writtenByMajor >= Version .MIN_SUPPORTED_MAJOR ) {
276
+ this .writtenByMajor = writtenByMajor ;
277
+ } else {
278
+ throw new IllegalArgumentException (
279
+ "Lucene major version supplied ("
280
+ + writtenByMajor
281
+ + ") is incorrect. Should be between Version.LATEST ("
282
+ + Version .LATEST .major
283
+ + ") and Version.MIN_SUPPORTED_MAJOR ("
284
+ + Version .MIN_SUPPORTED_MAJOR
285
+ + ")."
286
+ );
287
+ }
288
+ }
248
289
}
249
290
250
291
/**
@@ -582,10 +623,13 @@ public void uploadMetadata(
582
623
);
583
624
try {
584
625
try (IndexOutput indexOutput = storeDirectory .createOutput (metadataFilename , IOContext .DEFAULT )) {
626
+ Map <String , Integer > segmentToLuceneVersion = getSegmentToLuceneVersion (segmentFiles , segmentInfosSnapshot );
585
627
Map <String , String > uploadedSegments = new HashMap <>();
586
628
for (String file : segmentFiles ) {
587
629
if (segmentsUploadedToRemoteStore .containsKey (file )) {
588
- uploadedSegments .put (file , segmentsUploadedToRemoteStore .get (file ).toString ());
630
+ UploadedSegmentMetadata metadata = segmentsUploadedToRemoteStore .get (file );
631
+ metadata .setWrittenByMajor (segmentToLuceneVersion .get (metadata .originalFilename ));
632
+ uploadedSegments .put (file , metadata .toString ());
589
633
} else {
590
634
throw new NoSuchFileException (file );
591
635
}
@@ -615,6 +659,38 @@ public void uploadMetadata(
615
659
}
616
660
}
617
661
662
+ /**
663
+ * Parses the provided SegmentInfos to retrieve a mapping of the provided segment files to
664
+ * the respective Lucene major version that wrote the segments
665
+ * @param segmentFiles List of segment files for which the Lucene major version is needed
666
+ * @param segmentInfosSnapshot SegmentInfos instance to parse
667
+ * @return Map of the segment file to its Lucene major version
668
+ */
669
+ private Map <String , Integer > getSegmentToLuceneVersion (Collection <String > segmentFiles , SegmentInfos segmentInfosSnapshot ) {
670
+ Map <String , Integer > segmentToLuceneVersion = new HashMap <>();
671
+ for (SegmentCommitInfo segmentCommitInfo : segmentInfosSnapshot ) {
672
+ SegmentInfo info = segmentCommitInfo .info ;
673
+ Set <String > segFiles = info .files ();
674
+ for (String file : segFiles ) {
675
+ segmentToLuceneVersion .put (file , info .getVersion ().major );
676
+ }
677
+ }
678
+
679
+ for (String file : segmentFiles ) {
680
+ if (segmentToLuceneVersion .containsKey (file ) == false ) {
681
+ if (file .equals (segmentInfosSnapshot .getSegmentsFileName ())) {
682
+ segmentToLuceneVersion .put (file , segmentInfosSnapshot .getCommitLuceneVersion ().major );
683
+ } else {
684
+ // Fallback to the Lucene major version of the respective segment's .si file
685
+ String segmentInfoFileName = RemoteStoreUtils .getSegmentName (file ) + ".si" ;
686
+ segmentToLuceneVersion .put (file , segmentToLuceneVersion .get (segmentInfoFileName ));
687
+ }
688
+ }
689
+ }
690
+
691
+ return segmentToLuceneVersion ;
692
+ }
693
+
618
694
/**
619
695
* Try to delete file from local store. Fails silently on failures
620
696
* @param filename: name of the file to be deleted
0 commit comments