[fix][ml] Make managed ledger properties updates atomic - #26569
void-ptr974 wants to merge 2 commits into
Conversation
Assisted-by: Codex
lhotari
left a comment
There was a problem hiding this comment.
Thanks for working on making managed-ledger property updates atomic. One concurrent migration path can still lose the durable migration marker when it overlaps a pending property update.
| public void operationComplete(Void result, Stat version) { | ||
| ledgersStat = version; | ||
| callback.updatePropertiesComplete(propertiesMap, ctx); | ||
| propertiesMap = propertiesSnapshot; |
There was a problem hiding this comment.
[BUG] Publishing this snapshot can erase a concurrent migration marker
asyncMigrate() writes migrated=true directly to the currently published map at ManagedLedgerImpl.java:1400-1403 without taking metadataMutex. If a property update has already captured propertiesSnapshot, migration can add the marker while its BookKeeper close is pending, and this callback can then replace the map with the older snapshot. The close callback subsequently serializes that replacement using the updated ledgersStat, so both operations can succeed while the terminated metadata lacks the marker; reopening the ledger then reports isMigrated() as false. Please serialize the internal migration write with snapshot publication and add a deterministic regression test that gates the property metadata callback and BookKeeper close callback in this order.
There was a problem hiding this comment.
Thanks, good catch. Fixed in 38b196b.
asyncMigrate() now writes the migration marker through asyncSetProperty() and starts termination only after the property update succeeds. This serializes it with other property snapshot updates under metadataMutex.
I added a deterministic regression test that releases the pending property callback before the gated BookKeeper close callback, then reopens the ledger and verifies the migration marker is preserved. It fails on the previous revision and passes with the fix. quickCheck and the focused tests also pass.
Serialize the migration marker through the managed-ledger property update path before terminating the ledger, and add deterministic coverage for the overlapping update race. Assisted-by: Codex
Motivation
ManagedLedgerImplinitializespropertiesMapas aConcurrentHashMap, but replaces it with a plainHashMapwhen recovering persisted properties. In addition,getProperties()exposes the live internalmap, and property updates mutate the in-memory map before the metadata-store write succeeds.
As a result, callers can mutate managed-ledger state without using the property update APIs, readers can
observe a partially applied multi-property update, and a failed metadata-store update can leave memory
inconsistent with persisted metadata.
Modifications
getProperties()and property-update callbacks.snapshot only after the metadata-store write succeeds.
This change is limited to the managed-ledger properties API and does not change termination, migration,
or other managed-ledger metadata update flows.
Verifying this change
This change added tests covering:
Commands run locally:
./gradlew quickCheck:managed-ledger:testproperties, concurrency, and read-only tests./gradlew :pulsar-broker:test --tests org.apache.pulsar.broker.intercept.ManagedLedgerInterceptorImplTest.testRecoveryIndexDoes this pull request potentially affect one of the following parts:
ManagedLedger.getProperties()now explicitly returns a detached snapshot rather than the live backingmap. The threading change is limited to atomic publication of property snapshots; no executors or thread
pools are changed.