Skip to content

fix(eventhubs): rotate ETag and refresh timestamp on InMemoryCheckpointStore renewal#4703

Draft
j7nw4r wants to merge 1 commit into
Azure:mainfrom
j7nw4r:fix/4594-inmemory-checkpoint-renewal
Draft

fix(eventhubs): rotate ETag and refresh timestamp on InMemoryCheckpointStore renewal#4703
j7nw4r wants to merge 1 commit into
Azure:mainfrom
j7nw4r:fix/4594-inmemory-checkpoint-renewal

Conversation

@j7nw4r

@j7nw4r j7nw4r commented Jul 7, 2026

Copy link
Copy Markdown
Member

Summary

InMemoryCheckpointStore::update_ownership refreshes the ETag and last_modified_time when creating a fresh ownership claim but not when renewing an existing one, where it reinserts the caller's record verbatim. This aligns the renewal path with the create path so both rotate the ETag and set the current timestamp, matching the production BlobCheckpointStore. Fixes #4594.

Motivation

InMemoryCheckpointStore is a public, exported type that consumers can use as a test double for EventProcessor-based code. Its renewal path left the ETag and last_modified_time at whatever the caller passed, while the create path generated a new UUID ETag and set now_utc(), and the production BlobCheckpointStore derives both from the storage service response on every successful claim. The stale-ETag renewal behavior is a fidelity gap: it can mask bugs in code that relies on ETag rotation for optimistic concurrency, since a renewal that should rotate the ETag silently keeps the old one.

Changes

  • InMemoryCheckpointStore::update_ownership renewal branch now clones the incoming ownership, rotates etag to a new Uuid::new_v4(), sets last_modified_time to OffsetDateTime::now_utc(), and returns the refreshed record, mirroring the create branch.
  • Added a #[cfg(test)] pub(crate) fn set_last_modified_time_for_test seam on InMemoryCheckpointStore that locks the ownership map and sets an entry's last_modified_time directly. The prior expire_ownership test helper depended on the buggy renewal path preserving a stale timestamp round-tripped through claim_ownership; that mechanism no longer works once renewal refreshes the timestamp.
  • Reworked the expire_ownership load-balancer test helper to take &InMemoryCheckpointStore and call the new seam instead of round-tripping through claim_ownership, and updated its single call site to pass the concrete store.
  • Added a Bugs Fixed CHANGELOG entry under the unreleased azure_messaging_eventhubs version.

Validation

  • cargo build -p azure_messaging_eventhubs: clean.
  • cargo test -p azure_messaging_eventhubs --lib load_balancer: 11 passed (incl. processor_load_balancers_any_strategy_grab_expired_partition and the relinquish test).
  • cargo test -p azure_messaging_eventhubs --test eventhubs_checkpoint_store: 5 passed.
  • cargo clippy -p azure_messaging_eventhubs --tests --all-features: no warnings.
  • cargo fmt: clean.

…ntStore renewal

The renewal branch of `InMemoryCheckpointStore::update_ownership`
reinserted the caller's ownership record verbatim, leaving the ETag
and `last_modified_time` unchanged. The create branch, and the
production `BlobCheckpointStore`, both refresh these values on every
successful claim. The divergence let the in-memory test double preserve
a stale ETag across renewals, which can mask bugs in code that relies
on ETag rotation for optimistic concurrency.

Align the renewal branch with the create branch: rotate the ETag to a
new UUID and set `last_modified_time` to the current time, returning
the refreshed record.

The `expire_ownership` test helper previously depended on the stale
behavior to simulate an expired partition (it set a past timestamp and
round-tripped through `claim_ownership`, relying on the renewal path to
preserve it). Add a `#[cfg(test)]` `set_last_modified_time_for_test`
seam on `InMemoryCheckpointStore` that mutates the stored timestamp
directly under the mutex, and rework `expire_ownership` to use it.

Fixes Azure#4594
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Event Hubs: InMemoryCheckpointStore renewal does not refresh last_modified_time/etag, risking ownership churn

1 participant