7
7
8
8
package org .elasticsearch .xpack .lucene .bwc ;
9
9
10
- import org .apache .lucene .backward_codecs .lucene70 .Lucene70Codec ;
11
- import org .apache .lucene .codecs .Codec ;
12
- import org .apache .lucene .codecs .CodecUtil ;
13
10
import org .apache .lucene .index .SegmentCommitInfo ;
14
11
import org .apache .lucene .index .SegmentInfo ;
15
12
import org .apache .lucene .index .SegmentInfos ;
16
- import org .apache .lucene .store .ChecksumIndexInput ;
17
- import org .apache .lucene .store .Directory ;
18
- import org .apache .lucene .store .IOContext ;
19
- import org .apache .lucene .util .StringHelper ;
20
- import org .apache .lucene .util .Version ;
21
- import org .elasticsearch .Build ;
13
+ import org .elasticsearch .Version ;
22
14
import org .elasticsearch .common .UUIDs ;
23
15
import org .elasticsearch .common .lucene .Lucene ;
24
16
import org .elasticsearch .index .IndexModule ;
28
20
import org .elasticsearch .index .shard .IndexShard ;
29
21
import org .elasticsearch .plugins .IndexStorePlugin ;
30
22
import org .elasticsearch .plugins .Plugin ;
23
+ import org .elasticsearch .xpack .lucene .bwc .codecs .BWCCodec ;
31
24
32
25
import java .io .IOException ;
33
26
import java .io .UncheckedIOException ;
@@ -38,83 +31,69 @@ public class OldLuceneVersions extends Plugin implements IndexStorePlugin {
38
31
39
32
@ Override
40
33
public void onIndexModule (IndexModule indexModule ) {
41
- if (Build . CURRENT .isSnapshot ( )) {
34
+ if (indexModule . indexSettings (). getIndexVersionCreated (). before ( Version . CURRENT .minimumIndexCompatibilityVersion () )) {
42
35
indexModule .addIndexEventListener (new IndexEventListener () {
43
36
@ Override
44
37
public void afterFilesRestoredFromRepository (IndexShard indexShard ) {
45
- maybeConvertToNewFormat (indexShard );
38
+ convertToNewFormat (indexShard );
46
39
}
47
40
});
48
41
}
49
42
}
50
43
51
- private static void maybeConvertToNewFormat (IndexShard indexShard ) {
44
+ /**
45
+ * The trick used to allow newer Lucene versions to read older Lucene indices is to convert the old directory to a directory that new
46
+ * Lucene versions happily operate on. The way newer Lucene versions happily comply with reading older data is to put in place a
47
+ * segments file that the newer Lucene version can open, using codecs that allow reading everything from the old files, making it
48
+ * available under the newer interfaces. The way this works is to read in the old segments file using a special class
49
+ * {@link OldSegmentInfos} that supports reading older Lucene {@link SegmentInfos}, and then write out an updated segments file that
50
+ * newer Lucene versions can understand.
51
+ */
52
+ private static void convertToNewFormat (IndexShard indexShard ) {
52
53
indexShard .store ().incRef ();
53
54
try {
54
- try {
55
- Version version = getLuceneVersion (indexShard .store ().directory ());
56
- // Lucene version in [7.0.0, 8.0.0)
57
- if (version != null
58
- && version .onOrAfter (Version .fromBits (7 , 0 , 0 ))
59
- && version .onOrAfter (Version .fromBits (8 , 0 , 0 )) == false ) {
60
- final OldSegmentInfos oldSegmentInfos = OldSegmentInfos .readLatestCommit (indexShard .store ().directory (), 7 );
61
- final SegmentInfos segmentInfos = convertLucene7x (oldSegmentInfos );
62
- // write upgraded segments file
63
- segmentInfos .commit (indexShard .store ().directory ());
55
+ final OldSegmentInfos oldSegmentInfos = OldSegmentInfos .readLatestCommit (indexShard .store ().directory (), 6 );
56
+ final SegmentInfos segmentInfos = convertToNewerLuceneVersion (oldSegmentInfos );
57
+ // write upgraded segments file
58
+ segmentInfos .commit (indexShard .store ().directory ());
64
59
65
- // validate that what we have written can be read using standard path
66
- // TODO: norelease: remove this when development completes
67
- SegmentInfos segmentInfos1 = SegmentInfos .readLatestCommit (indexShard .store ().directory ());
60
+ // what we have written can be read using standard path
61
+ assert SegmentInfos .readLatestCommit (indexShard .store ().directory ()) != null ;
68
62
69
- // clean older segments file
70
- Lucene .pruneUnreferencedFiles (segmentInfos1 .getSegmentsFileName (), indexShard .store ().directory ());
71
- }
72
- } catch (IOException e ) {
73
- throw new UncheckedIOException (e );
74
- }
63
+ // clean older segments file
64
+ Lucene .pruneUnreferencedFiles (segmentInfos .getSegmentsFileName (), indexShard .store ().directory ());
65
+ } catch (IOException e ) {
66
+ throw new UncheckedIOException (e );
75
67
} finally {
76
68
indexShard .store ().decRef ();
77
69
}
78
70
}
79
71
80
- private static Version getLuceneVersion (Directory directory ) throws IOException {
81
- final String segmentFileName = SegmentInfos .getLastCommitSegmentsFileName (directory );
82
- if (segmentFileName != null ) {
83
- long generation = SegmentInfos .generationFromSegmentsFileName (segmentFileName );
84
- try (ChecksumIndexInput input = directory .openChecksumInput (segmentFileName , IOContext .READ )) {
85
- CodecUtil .checkHeader (input , "segments" , 0 , Integer .MAX_VALUE );
86
- byte [] id = new byte [StringHelper .ID_LENGTH ];
87
- input .readBytes (id , 0 , id .length );
88
- CodecUtil .checkIndexHeaderSuffix (input , Long .toString (generation , Character .MAX_RADIX ));
89
-
90
- Version luceneVersion = Version .fromBits (input .readVInt (), input .readVInt (), input .readVInt ());
91
- int indexCreatedVersion = input .readVInt ();
92
- return luceneVersion ;
93
- } catch (Exception e ) {
94
- // ignore
95
- }
96
- }
97
- return null ;
98
- }
99
-
100
- private static SegmentInfos convertLucene7x (OldSegmentInfos oldSegmentInfos ) {
72
+ private static SegmentInfos convertToNewerLuceneVersion (OldSegmentInfos oldSegmentInfos ) {
101
73
final SegmentInfos segmentInfos = new SegmentInfos (org .apache .lucene .util .Version .LATEST .major );
102
74
segmentInfos .setNextWriteGeneration (oldSegmentInfos .getGeneration () + 1 );
103
75
final Map <String , String > map = new HashMap <>(oldSegmentInfos .getUserData ());
104
- map .put (Engine .HISTORY_UUID_KEY , UUIDs .randomBase64UUID ());
105
- map .put (SequenceNumbers .LOCAL_CHECKPOINT_KEY , Long .toString (SequenceNumbers .NO_OPS_PERFORMED ));
106
- map .put (SequenceNumbers .MAX_SEQ_NO , Long .toString (SequenceNumbers .NO_OPS_PERFORMED ));
107
- map .put (Engine .MAX_UNSAFE_AUTO_ID_TIMESTAMP_COMMIT_ID , "-1" );
76
+ if (map .containsKey (Engine .HISTORY_UUID_KEY ) == false ) {
77
+ map .put (Engine .HISTORY_UUID_KEY , UUIDs .randomBase64UUID ());
78
+ }
79
+ if (map .containsKey (SequenceNumbers .LOCAL_CHECKPOINT_KEY ) == false ) {
80
+ map .put (SequenceNumbers .LOCAL_CHECKPOINT_KEY , Long .toString (SequenceNumbers .NO_OPS_PERFORMED ));
81
+ }
82
+ if (map .containsKey (SequenceNumbers .MAX_SEQ_NO ) == false ) {
83
+ map .put (SequenceNumbers .MAX_SEQ_NO , Long .toString (SequenceNumbers .NO_OPS_PERFORMED ));
84
+ }
85
+ if (map .containsKey (Engine .MAX_UNSAFE_AUTO_ID_TIMESTAMP_COMMIT_ID ) == false ) {
86
+ map .put (Engine .MAX_UNSAFE_AUTO_ID_TIMESTAMP_COMMIT_ID , "-1" );
87
+ }
108
88
segmentInfos .setUserData (map , true );
109
89
for (SegmentCommitInfo infoPerCommit : oldSegmentInfos .asList ()) {
110
- SegmentInfo info = infoPerCommit .info ;
111
- SegmentInfo newInfo = wrap (info );
90
+ final SegmentInfo newInfo = BWCCodec .wrap (infoPerCommit .info );
112
91
113
92
segmentInfos .add (
114
93
new SegmentCommitInfo (
115
94
newInfo ,
116
95
infoPerCommit .getDelCount (),
117
- 0 ,
96
+ infoPerCommit . getSoftDelCount () ,
118
97
infoPerCommit .getDelGen (),
119
98
infoPerCommit .getFieldInfosGen (),
120
99
infoPerCommit .getDocValuesGen (),
@@ -125,31 +104,6 @@ private static SegmentInfos convertLucene7x(OldSegmentInfos oldSegmentInfos) {
125
104
return segmentInfos ;
126
105
}
127
106
128
- static SegmentInfo wrap (SegmentInfo segmentInfo ) {
129
- // Use Version.LATEST instead of original version, otherwise SegmentCommitInfo will bark when processing (N-1 limitation)
130
- // TODO: alternatively store the original version information in attributes?
131
- byte [] id = segmentInfo .getId ();
132
- if (id == null ) {
133
- id = StringHelper .randomId ();
134
- }
135
- Codec codec = segmentInfo .getCodec () instanceof Lucene70Codec ? new BWCLucene70Codec () : segmentInfo .getCodec ();
136
- SegmentInfo segmentInfo1 = new SegmentInfo (
137
- segmentInfo .dir ,
138
- org .apache .lucene .util .Version .LATEST ,
139
- org .apache .lucene .util .Version .LATEST ,
140
- segmentInfo .name ,
141
- segmentInfo .maxDoc (),
142
- segmentInfo .getUseCompoundFile (),
143
- codec ,
144
- segmentInfo .getDiagnostics (),
145
- id ,
146
- segmentInfo .getAttributes (),
147
- null
148
- );
149
- segmentInfo1 .setFiles (segmentInfo .files ());
150
- return segmentInfo1 ;
151
- }
152
-
153
107
@ Override
154
108
public Map <String , DirectoryFactory > getDirectoryFactories () {
155
109
return Map .of ();
0 commit comments