Skip to content

Commit 948f661

Browse files
JVerwolfjdconradrjernst
authored
Update transport version docs (#133862)
This commit updates versioning docs to explain how to use the new transport version system. Co-authored-by: Jack Conradson <[email protected]> Co-authored-by: Ryan Ernst <[email protected]>
1 parent 436ec11 commit 948f661

File tree

1 file changed

+109
-99
lines changed

1 file changed

+109
-99
lines changed

docs/internal/Versioning.md

Lines changed: 109 additions & 99 deletions
Original file line numberDiff line numberDiff line change
@@ -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.
3029
This version is then accessible through the `getTransportVersion` method
3130
on `StreamInput` and `StreamOutput`, so serialization code can read/write
3231
objects 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
7355
on 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+
generate task to resolve the conflict(s):
126+
127+
./gradlew generateTransportVersion --resolve-conflict
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,54 +172,6 @@ is updated automatically as part of performing a release.
114172
In releases that do not have a release version number, that method becomes
115173
a 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

167177
Index version is a single incrementing version number for the index data format,

0 commit comments

Comments
 (0)