@@ -22,68 +22,126 @@ it could be any arbitrary string.
2222
2323## Transport protocol
2424
25- The transport protocol is used to send binary data between Elasticsearch nodes;
26- ` TransportVersion ` is the version number used for this protocol.
27- This version number is negotiated between each pair of nodes in the cluster
28- on first connection, and is set as the lower of the highest transport version
29- understood by each node.
25+ The transport protocol is used to send binary data between Elasticsearch nodes; a
26+ ` TransportVersion ` encapsulates versioning of this protocol.
27+ This version is negotiated between each pair of nodes in the cluster
28+ on first connection, selecting the highest shared version.
3029This version is then accessible through the ` getTransportVersion ` method
3130on ` StreamInput ` and ` StreamOutput ` , so serialization code can read/write
3231objects in a form that will be understood by the other node.
3332
34- Every change to the transport protocol is represented by a new transport version,
35- higher than all previous transport versions, which then becomes the highest version
36- recognized by that build of Elasticsearch. The version ids are stored
37- as constants in the ` TransportVersions ` class.
38- Each id has a standard pattern ` M_NNN_S_PP ` , where:
39- * ` M ` is the major version
40- * ` NNN ` is an incrementing id
41- * ` S ` is used in subsidiary repos amending the default transport protocol
42- * ` PP ` is used for patches and backports
43-
44- When you make a change to the serialization form of any object,
45- you need to create a new sequential constant in ` TransportVersions ` ,
46- introduced in the same PR that adds the change, that increments
47- the ` NNN ` component from the previous highest version,
48- with other components set to zero.
49- For example, if the previous version number is ` 8_413_0_01 ` ,
50- the next version number should be ` 8_414_0_00 ` .
51-
52- Once you have defined your constant, you then need to use it
53- in serialization code. If the transport version is at or above the new id,
54- the modified protocol should be used:
55-
56- str = in.readString();
57- bool = in.readBoolean();
58- if (in.getTransportVersion().onOrAfter(TransportVersions.NEW_CONSTANT)) {
59- num = in.readVInt();
60- }
33+ At a high level a ` TransportVersion ` contains one id per release branch it will
34+ be committed to. Each ` TransportVersion ` has a name selected when it is generated.
35+ In order to ensure consistency and robustness, all new ` TransportVersion ` s
36+ must first be created in the ` main ` branch and then backported to the relevant
37+ release branches.
38+
39+ ### Internal state files
40+
41+ The Elasticsearch server jar contains resource files representing each
42+ transport version. These files are loaded at runtime to construct
43+ ` TransportVersion ` instances. Since each transport version has its own file
44+ they can be backported without conflict.
6145
62- If a transport version change needs to be reverted, a ** new** version constant
63- should be added representing the revert, and the version id checks
64- adjusted appropriately to only use the modified protocol between the version id
65- the change was added, and the new version id used for the revert (exclusive).
66- The ` between ` method can be used for this.
46+ Additional resource files represent the latest transport version known on
47+ each release branch. If two transport versions are added at the same time,
48+ there will be a conflict in these internal state files, forcing one to be
49+ regenerated to resolve the conflict before merging to ` main ` .
6750
68- Once a transport change with a new version has been merged into main or a release branch,
69- it ** must not** be modified - this is so the meaning of that specific
70- transport version does not change.
51+ All of these internal state files are managed by gradle tasks; they should
52+ not be edited directly.
7153
7254_ Elastic developers_ - please see corresponding documentation for Serverless
7355on creating transport versions for Serverless changes.
7456
75- ### Collapsing transport versions
57+ ### Creating transport versions locally
58+
59+ To create a transport version, declare a reference anywhere in java code. For example:
60+
61+ private static final TransportVersion MY_NEW_TV = TransportVersion.fromName("my_new_tv");
62+
63+ ` fromName ` takes an arbitrary String name. The String must be a String literal;
64+ it cannot be a reference to a String. It must match the regex ` [_0-9a-zA-Z]+ ` .
65+ You can reference the same transport version name from multiple classes, but
66+ you must not use an existing transport version name after it as already been
67+ committed to ` main ` .
68+
69+ Once you have declared your ` TransportVersion ` you can use it in serialization code.
70+ For example, in a constructor that takes ` StreamInput in ` :
71+
72+ if (in.getTransportVersion().supports(MY_NEW_TV)) {
73+ // new serialization code
74+ }
75+
76+ Finally, in order to run Elasticsearch or run tests, the transport version ids
77+ must be generated. Run the following gradle task:
78+
79+ ./gradlew generateTransportVersion
80+
81+ This will generate the internal state to support the new transport version. If
82+ you also intend to backport your code, include branches you will backport to:
83+
84+ ./gradlew generateTransportVersion --backport-branches=9.1,8.19
85+
86+ ### Updating transport versions
87+
88+ You can modify a transport version before it is merged to ` main ` . This includes
89+ renaming the transport version, updating the branches it will be backported to,
90+ or even removing the transport version itself.
91+
92+ The generation task is idempotent. It can be re-run at any time and will result
93+ in a valid internal state. For example, if you want to add an additional
94+ backport branch, re-run the generation task with all the target backport
95+ branches:
96+
97+ ./gradlew generateTransportVersion --backport-branches=9.1,9.0,8.19,8.18
98+
99+ You can also let CI handle updating transport versions for you. As version
100+ labels are updated on your PR, the generation task is automatically run with
101+ the appropriate backport branches and any changes to the internal state files
102+ are committed to your branch.
103+
104+ Transport versions can also have additional branches added after merging to
105+ ` main ` . When doing so, you must include all branches the transport version was
106+ added to in addition to new branch. For example, if you originally committed
107+ your transport version ` my_tv ` to ` main ` and ` 9.1 ` , and then realized you also
108+ needed to backport to ` 8.19 ` you would run (in ` main ` ):
109+
110+ ./gradlew generateTransportVersion --name=my_tv --backport-branches=9.1,8.19
111+
112+ In the above case CI will not know what transport version name to update, so you
113+ must run the generate task again as described. After merging the updated transport
114+ version it will need to be backported to all the applicable branches.
115+
116+ ### Resolving merge conflicts
76117
77- As each change adds a new constant, the list of constants in ` TransportVersions `
78- will keep growing. However, once there has been an official release of Elasticsearch,
79- that includes that change, that specific transport version is no longer needed,
80- apart from constants that happen to be used for release builds.
81- As part of managing transport versions, consecutive transport versions can be
82- periodically collapsed together into those that are only used for release builds.
83- This task is normally performed by Core/Infra on a semi-regular basis,
84- usually after each new minor release, to collapse the transport versions
85- for the previous minor release. An example of such an operation can be found
86- [ here] ( https://github.com/elastic/elasticsearch/pull/104937 ) .
118+ Transport versions are created sequentially. If two developers create a transport
119+ version at the same time, based on the same ` main ` commit, they will generate
120+ the same internal ids. The first of these two merged into ` main ` will "win", and
121+ the latter will have a merge conflict with ` main ` .
122+
123+ In the event of a conflict, merge ` main ` into your branch. You will have
124+ conflict(s) with transport version internal state files. Run the following
125+ task to resolve the conflict(s):
126+
127+ ./gradlew resolveTransportVersionConflict
128+
129+ This command will regenerate your transport version and stage the updated
130+ state files in git. You can then proceed with your merge as usual.
131+
132+ ### Reverting changes
133+
134+ Transport versions cannot be removed, they can only be added. If the logic
135+ using a transport version needs to be reverted, it must be done with a
136+ new transport version.
137+
138+ For example, if you have previously added a transport version named
139+ ` original_tv ` you could add ` revert_tv ` reversing the logic:
140+
141+ TransportVersion tv = in.getTransportVersion();
142+ if (tv.supports(ORIGINAL_TV) && tv.supports(REVERT_TV) == false) {
143+ // serialization code being reverted
144+ }
87145
88146### Minimum compatibility versions
89147
@@ -114,70 +172,22 @@ is updated automatically as part of performing a release.
114172In releases that do not have a release version number, that method becomes
115173a no-op.
116174
117- ### Managing patches and backports
118-
119- Backporting transport version changes to previous releases
120- should only be done if absolutely necessary, as it is very easy to get wrong
121- and break the release in a way that is very hard to recover from.
122-
123- If we consider the version number as an incrementing line, what we are doing is
124- grafting a change that takes effect at a certain point in the line,
125- to additionally take effect in a fixed window earlier in the line.
126-
127- To take an example, using indicative version numbers, when the latest
128- transport version is 52, we decide we need to backport a change done in
129- transport version 50 to transport version 45. We use the ` P ` version id component
130- to create version 45.1 with the backported change.
131- This change will apply for version ids 45.1 to 45.9 (should they exist in the future).
132-
133- The serialization code in the backport needs to use the backported protocol
134- for all version numbers 45.1 to 45.9. The ` TransportVersion.isPatchFrom ` method
135- can be used to easily determine if this is the case: ` streamVersion.isPatchFrom(45.1) ` .
136- However, the ` onOrAfter ` also does what is needed on patch branches.
137-
138- The serialization code in version 53 then needs to additionally check
139- version numbers 45.1-45.9 to use the backported protocol, also using the ` isPatchFrom ` method.
140-
141- As an example, [ this transport change] ( https://github.com/elastic/elasticsearch/pull/107862 )
142- was backported from 8.15 to [ 8.14.0] ( https://github.com/elastic/elasticsearch/pull/108251 )
143- and [ 8.13.4] ( https://github.com/elastic/elasticsearch/pull/108250 ) at the same time
144- (8.14 was a build candidate at the time).
145-
146- The 8.13 PR has:
147-
148- if (transportVersion.onOrAfter(8.13_backport_id))
149-
150- The 8.14 PR has:
151-
152- if (transportVersion.isPatchFrom(8.13_backport_id)
153- || transportVersion.onOrAfter(8.14_backport_id))
154-
155- The 8.15 PR has:
156-
157- if (transportVersion.isPatchFrom(8.13_backport_id)
158- || transportVersion.isPatchFrom(8.14_backport_id)
159- || transportVersion.onOrAfter(8.15_transport_id))
160-
161- In particular, if you are backporting a change to a patch release,
162- you also need to make sure that any subsequent released version on any branch
163- also has that change, and knows about the patch backport ids and what they mean.
164-
165175## Index version
166176
167177Index version is a single incrementing version number for the index data format,
168- metadata, and associated mappings. It is declared with the pattern ` M_NNN_S_PP ` ,
169- for the major version, version id, subsidiary version id, and patch number
170- respectively.
178+ metadata, and associated mappings. It is declared the same way as the
179+ transport version - with the pattern ` M_NNN_S_PP ` , for the major version, version id,
180+ subsidiary version id, and patch number respectively.
171181
172182Index version is stored in index metadata when an index is created,
173183and it is used to determine the storage format and what functionality that index supports.
174184The index version does not change once an index is created.
175185
176- When a change is needed to the index data format or metadata, or new mapping
177- types are added, create a new version constant below the last one, incrementing
178- the ` NNN ` version component.
186+ In the same way as transport versions, when a change is needed to the index
187+ data format or metadata, or new mapping types are added, create a new version constant
188+ below the last one, incrementing the ` NNN ` version component.
179189
180- Index version constants cannot be collapsed together,
190+ Unlike transport version, version constants cannot be collapsed together,
181191as an index keeps its creation version id once it is created.
182192Fortunately, new index versions are only created once a month or so,
183193so we don’t have a large list of index versions that need managing.
0 commit comments