Skip to content

[Bug] isCursorDataFullyPersistable ignores the batch-index persistence limit #26499

Description

@void-ptr974

Search before reporting

  • I searched in the issues and found nothing similar.

Read release policy

  • I understand that unsupported versions don't get bug fixes. I reproduced the issue on the master branch.

User environment

  • Version: master (ce607988747).
  • Component: managed-ledger, ManagedCursorImpl.

Issue Description

ManagedCursorImpl.isCursorDataFullyPersistable() can return true even when the configured batch-index persistence limit prevents some partial-batch ACK records from being saved. The method checks the whole-entry deletion-range limit, but not the separate batch-record limit.

Triggering scenario

A stored batch entry can contain several messages. If only some are acknowledged, the cursor keeps a bitmap of the messages still pending. The batch-index persistence limit counts one record per partially acknowledged batch entry, not messages or bits within a batch.

Consider three entries E1, E2 and E3, each containing an A message and a B message. Acknowledge each A message and leave each B message pending. The cursor has three batch ACK records and no whole-entry deletion ranges.

With maxBatchDeletedIndexToPersist=2, isCursorDataFullyPersistable() returns true, but an explicit cursor-state write saves only two of the three batch records. The same mismatch occurs when the whole-entry range collection is nonempty but within its own limit.

Expected: ACK state that exceeds the batch-record persistence limit should not be reported as fully persistable.

Impact

The batch persistence limit intentionally leaves excess ACK records in memory. If the cursor is recovered before the omitted state has been saved, affected messages may be delivered again. The issue here is not the configured truncation itself, but that the persistability check does not report it.

Both multiple-consumer dispatcher implementations consult this check when dispatcherPauseOnAckStatePersistentEnabled is enabled. Batch-record overflow alone leaves the result true, so it does not cause those checks to pause dispatching. Whether the existing pause policy should cover this additional limit is the policy question noted below.

Reproducing the issue

These steps use the managed-ledger API on a durable ManagedCursor.

  1. In ManagedLedgerConfig, set deletionAtBatchIndexLevelEnabled=true, maxBatchDeletedIndexToPersist=2, and throttleMarkDelete=0. The last setting prevents rate limiting from delaying the explicit cursor-state writes below.
  2. Add an earlier entry E0 followed by batch entries E1, E2 and E3. Mark-delete E0 while the three batches remain unacknowledged, and wait for the cursor-state write to complete.
  3. Individually acknowledge each A message, leaving each B message pending. There are now three batch ACK records in memory and no whole-entry deletion ranges.
  4. Call isCursorDataFullyPersistable(). It returns true.
  5. Mark-delete E0 again, without advancing the mark-delete position, and wait for completion. The latest stored cursor record (PositionInfo) contains only two batch ACK records.

Writing the same mark-delete position preserves all three partially acknowledged entries and checks serialization independently of the missing write trigger in #26498.

Additional information

The corresponding broker configuration for the batch-record limit is managedLedgerMaxBatchDeletedIndexToPersist.

Relevant code: persistability check, batch-record serialization limit, and the dispatcher pause/resume checks (current, classic).

Policy scope to confirm: PIP-299 specifies pausing at the whole-entry range limit. Should the existing pause policy also cover batch-record overflow?

Are you willing to submit a PR?

  • I'm willing to submit a PR!

Activity

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

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions