Skip to content

Commit 679ce7f

Browse files
jrrayclaude
andcommitted
Fix intermittent IllegalGeneration error in spk publish
spk publish's listen_to_index_status_updates() subscribed to the index-updates topic via Kafka consumer-group management and then immediately committed starting offsets, before the consumer had joined the group / finished rebalancing (the join only happens on the first poll). Committing offsets with no valid group generation intermittently failed with: failed to commit new starting offsets for the kafka index updates consumer: Consumer commit error: IllegalGeneration (Broker: Specified group generation id is not valid) The commit served no purpose anyway: this consumer uses a fresh, ephemeral Ulid group id every run, so the committed offsets are never read back. Switch this read-only consumer from subscribe() + commit() to manual partition assignment (assign()) with the starting offsets carried in the TopicPartitionList. Manual assignment has no group generation, so the race cannot occur. The subscribe() call is removed since a consumer cannot mix subscribe and assign. The indexer's long-lived consumer is untouched; it legitimately uses a stable group id and offset commits. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
1 parent 8dac536 commit 679ce7f

2 files changed

Lines changed: 24 additions & 11 deletions

File tree

crates/spk-storage/src/storage/messaging/kafka.rs

Lines changed: 23 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -214,6 +214,15 @@ fn setup_running_interrupt_handling() -> Arc<AtomicBool> {
214214
/// the latest message because they want to use it to check whether
215215
/// the usual message producer is running by seeing if it has sent a
216216
/// message recently.
217+
///
218+
/// This uses manual partition assignment (rather than subscribing to
219+
/// the topic via consumer group management) so the starting offsets
220+
/// can be set up front without depending on a group join/rebalance.
221+
/// The consumers that use this have unique, ephemeral group ids and
222+
/// never reuse committed offsets, so there is no need for consumer
223+
/// group coordination here. Committing offsets before the group join
224+
/// completed was the source of intermittent `IllegalGeneration`
225+
/// ("Specified group generation id is not valid") errors.
217226
fn set_offsets_to_one_before_the_latest_message(
218227
consumer: Arc<StreamConsumer>,
219228
topic_name: &str,
@@ -258,11 +267,17 @@ fn set_offsets_to_one_before_the_latest_message(
258267
}
259268
}
260269

261-
// Update the offsets so the previous message(s) will be the first
262-
// read from the stream.
263-
consumer.commit(&offsets, CommitMode::Sync).map_err(|err| {
270+
// Assign the partitions with their starting offsets so the
271+
// previous message(s) will be the first read from the stream.
272+
//
273+
// Manual assignment is used instead of committing offsets for a
274+
// subscribed (group-managed) consumer. The commit approach raced
275+
// against the group join/rebalance and intermittently failed with
276+
// `IllegalGeneration` because the consumer had no valid group
277+
// generation yet.
278+
consumer.assign(&offsets).map_err(|err| {
264279
Error::String(format!(
265-
"failed to commit new starting offsets for the kafka {consumer_label} consumer: {err}"
280+
"failed to assign starting offsets for the kafka {consumer_label} consumer: {err}"
266281
))
267282
})
268283
}
@@ -324,14 +339,11 @@ pub(crate) async fn listen_to_index_status_updates(
324339
})?;
325340

326341
let consumer = Arc::new(consumer);
327-
consumer.subscribe(&[topic_name]).map_err(|err| {
328-
Error::String(format!(
329-
"failed to subscribe to kafka index updates topic: {err}"
330-
))
331-
})?;
332342

333-
// Change the starting message offset for reading to the just
334-
// before the last message in the topic.
343+
// Manually assign the topic's partitions with the starting message
344+
// offset for reading set to just before the last message in the
345+
// topic. This intentionally does not use `subscribe` (consumer
346+
// group management); see the note on the function below.
335347
set_offsets_to_one_before_the_latest_message(
336348
consumer.clone(),
337349
topic_name,

cspell.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -630,6 +630,7 @@
630630
"readdir",
631631
"readdirplus",
632632
"readlink",
633+
"rebalance",
633634
"recursivebuilds",
634635
"reexec",
635636
"reinit",

0 commit comments

Comments
 (0)