krabka_raft/config/kafka_release.rs
1//! The API version table of the Kafka release krabka matches by default.
2//!
3//! `krabka-protocol` vendors Kafka trunk's message schemas, which run ahead of
4//! the latest Kafka release. [`KAFKA_4_3_1_APIS`] is what a stock
5//! `apache/kafka:4.3.1` accepts instead: every request schema's
6//! `validVersions` in `clients/src/main/resources/common/message` at the
7//! `4.3.1` tag, with the one version 4.3.1 marks `latestVersionUnstable`
8//! (`InitProducerId` v6) left out, since that release advertises it only under
9//! `unstable.api.versions.enable` too. It is what both listeners serve while
10//! [`UnstableApiVersions::Disabled`](crate::UnstableApiVersions) holds.
11
12/// One Kafka 4.3.1 request schema: its api key and the versions the release
13/// accepts by default.
14#[derive(Clone, Copy, Debug, Eq, PartialEq)]
15pub struct ReleasedApi {
16 pub api_key: i16,
17 /// `validVersions`' lower bound. `Produce` advertises from 0 regardless
18 /// (`ApiKeys.PRODUCE_API_VERSIONS_RESPONSE_MIN_VERSION`, KAFKA-18659).
19 pub min_version: i16,
20 /// `validVersions`' upper bound, below a `latestVersionUnstable` version.
21 pub max_version: i16,
22}
23
24const fn api(api_key: i16, min_version: i16, max_version: i16) -> ReleasedApi {
25 ReleasedApi {
26 api_key,
27 min_version,
28 max_version,
29 }
30}
31
32/// Every api key Kafka 4.3.1 has a valid version of, in key order.
33pub const KAFKA_4_3_1_APIS: &[ReleasedApi] = &[
34 api(0, 3, 13), // Produce
35 api(1, 4, 18), // Fetch
36 api(2, 1, 11), // ListOffsets
37 api(3, 0, 13), // Metadata
38 api(8, 2, 10), // OffsetCommit
39 api(9, 1, 10), // OffsetFetch
40 api(10, 0, 6), // FindCoordinator
41 api(11, 0, 9), // JoinGroup
42 api(12, 0, 4), // Heartbeat
43 api(13, 0, 5), // LeaveGroup
44 api(14, 0, 5), // SyncGroup
45 api(15, 0, 6), // DescribeGroups
46 api(16, 0, 5), // ListGroups
47 api(17, 0, 1), // SaslHandshake
48 api(18, 0, 4), // ApiVersions
49 api(19, 2, 7), // CreateTopics
50 api(20, 1, 6), // DeleteTopics
51 api(21, 0, 2), // DeleteRecords
52 api(22, 0, 5), // InitProducerId
53 api(23, 2, 4), // OffsetForLeaderEpoch
54 api(24, 0, 5), // AddPartitionsToTxn
55 api(25, 0, 4), // AddOffsetsToTxn
56 api(26, 0, 5), // EndTxn
57 api(27, 1, 2), // WriteTxnMarkers
58 api(28, 0, 5), // TxnOffsetCommit
59 api(29, 1, 3), // DescribeAcls
60 api(30, 1, 3), // CreateAcls
61 api(31, 1, 3), // DeleteAcls
62 api(32, 1, 4), // DescribeConfigs
63 api(33, 0, 2), // AlterConfigs
64 api(34, 1, 2), // AlterReplicaLogDirs
65 api(35, 1, 5), // DescribeLogDirs
66 api(36, 0, 2), // SaslAuthenticate
67 api(37, 0, 3), // CreatePartitions
68 api(38, 1, 3), // CreateDelegationToken
69 api(39, 1, 2), // RenewDelegationToken
70 api(40, 1, 2), // ExpireDelegationToken
71 api(41, 1, 3), // DescribeDelegationToken
72 api(42, 0, 2), // DeleteGroups
73 api(43, 0, 2), // ElectLeaders
74 api(44, 0, 1), // IncrementalAlterConfigs
75 api(45, 0, 1), // AlterPartitionReassignments
76 api(46, 0, 0), // ListPartitionReassignments
77 api(47, 0, 0), // OffsetDelete
78 api(48, 0, 1), // DescribeClientQuotas
79 api(49, 0, 1), // AlterClientQuotas
80 api(50, 0, 0), // DescribeUserScramCredentials
81 api(51, 0, 0), // AlterUserScramCredentials
82 api(52, 0, 2), // Vote
83 api(53, 0, 1), // BeginQuorumEpoch
84 api(54, 0, 1), // EndQuorumEpoch
85 api(55, 0, 2), // DescribeQuorum
86 api(56, 2, 3), // AlterPartition
87 api(57, 0, 2), // UpdateFeatures
88 api(58, 0, 0), // Envelope
89 api(59, 0, 1), // FetchSnapshot
90 api(60, 0, 2), // DescribeCluster
91 api(61, 0, 0), // DescribeProducers
92 api(62, 0, 4), // BrokerRegistration
93 api(63, 0, 2), // BrokerHeartbeat
94 api(64, 0, 0), // UnregisterBroker
95 api(65, 0, 0), // DescribeTransactions
96 api(66, 0, 2), // ListTransactions
97 api(67, 0, 0), // AllocateProducerIds
98 api(68, 0, 1), // ConsumerGroupHeartbeat
99 api(69, 0, 1), // ConsumerGroupDescribe
100 api(70, 0, 0), // ControllerRegistration
101 api(71, 0, 0), // GetTelemetrySubscriptions
102 api(72, 0, 0), // PushTelemetry
103 api(73, 0, 0), // AssignReplicasToDirs
104 api(74, 0, 1), // ListConfigResources
105 api(75, 0, 0), // DescribeTopicPartitions
106 api(76, 1, 1), // ShareGroupHeartbeat
107 api(77, 1, 1), // ShareGroupDescribe
108 api(78, 1, 2), // ShareFetch
109 api(79, 1, 2), // ShareAcknowledge
110 api(80, 0, 1), // AddRaftVoter
111 api(81, 0, 0), // RemoveRaftVoter
112 api(82, 0, 0), // UpdateRaftVoter
113 api(83, 0, 0), // InitializeShareGroupState
114 api(84, 0, 0), // ReadShareGroupState
115 api(85, 0, 1), // WriteShareGroupState
116 api(86, 0, 0), // DeleteShareGroupState
117 api(87, 0, 1), // ReadShareGroupStateSummary
118 api(88, 0, 0), // StreamsGroupHeartbeat
119 api(89, 0, 0), // StreamsGroupDescribe
120 api(90, 0, 1), // DescribeShareGroupOffsets
121 api(91, 0, 0), // AlterShareGroupOffsets
122 api(92, 0, 0), // DeleteShareGroupOffsets
123];
124
125/// The Kafka 4.3.1 row for `api_key`, or `None` when that release has no such
126/// api key.
127#[must_use]
128pub const fn kafka_4_3_1_api(api_key: i16) -> Option<ReleasedApi> {
129 let mut rest: &[ReleasedApi] = KAFKA_4_3_1_APIS;
130 while let [row, tail @ ..] = rest {
131 if row.api_key == api_key {
132 return Some(*row);
133 }
134 rest = tail;
135 }
136 None
137}
138
139/// The highest version of `api_key` Kafka 4.3.1 accepts by default, or `None`
140/// when that release has no such api key.
141#[must_use]
142pub const fn kafka_4_3_1_max(api_key: i16) -> Option<i16> {
143 match kafka_4_3_1_api(api_key) {
144 Some(api) => Some(api.max_version),
145 None => None,
146 }
147}
148
149#[cfg(test)]
150mod tests {
151 use {super::*, assert2::check};
152
153 /// Strictly ascending, so a key is listed once, and every row names
154 /// versions `krabka-protocol` decodes: 4.3.1 is never ahead of trunk.
155 #[test]
156 fn the_table_is_ascending_and_inside_the_vendored_ranges() {
157 check!(
158 KAFKA_4_3_1_APIS
159 .windows(2)
160 .all(|pair| pair[0].api_key < pair[1].api_key)
161 );
162 check!(KAFKA_4_3_1_APIS.len() == 89);
163 for row in KAFKA_4_3_1_APIS {
164 check!(row.min_version <= row.max_version, "{row:?}");
165 }
166 }
167
168 #[yare::parameterized(
169 offset_fetch = { 18, Some(4) },
170 sasl_handshake = { 22, Some(5) },
171 txn_offset_commit = { 28, Some(5) },
172 api_key_88 = { 88, Some(0) },
173 api_key_89 = { 89, Some(0) },
174 api_key_92 = { 92, Some(0) },
175 first_unknown = { 93, None },
176 later_unknown = { 94, None },
177 private_key_unknown = { 1020, None },
178 )]
179 fn lookups_follow_the_table(api_key: i16, want: Option<i16>) {
180 check!(kafka_4_3_1_max(api_key) == want, "api_key {api_key}");
181 }
182
183 #[test]
184 fn api_table_includes_produce() {
185 check!(kafka_4_3_1_api(1) == Some(api(1, 4, 18)));
186 }
187}