Skip to content

Conversation

@codelipenghui
Copy link
Contributor

Fixes the issue where cursor.getEstimatedSizeSinceMarkDeletePosition() would not subtract the size of individually acknowledged messages.

Problem:

  • When messages are acknowledged out of order using asyncDelete(), the backlog count correctly decreases
  • But the backlog size remains inflated because individualDeletedMessages are not accounted for

Example:

  • 5 messages (500 bytes total)
  • Individual ack of 3 messages
  • Before fix: backlog size = 500 (incorrect)
  • After fix: backlog size = 200 (correct)

Fix:

  • Update ManagedCursorImpl.getEstimatedSizeSinceMarkDeletePosition() to calculate and subtract size of individual deleted entries
  • Uses average entry size to estimate deleted size
  • Thread-safe with proper locking

Unit test:

  • testEstimatedSizeWithIndividualAcks() verifies the fix

Does this pull request potentially affect one of the following parts:

If the box was checked, please highlight the changes

  • Dependencies (add or upgrade a dependency)
  • The public API
  • The schema
  • The default values of configurations
  • The threading model
  • The binary protocol
  • The REST endpoints
  • The admin CLI options
  • The metrics
  • Anything that affects deployment

Documentation

  • doc
  • doc-required
  • doc-not-needed
  • doc-complete

Fixes the issue where cursor.getEstimatedSizeSinceMarkDeletePosition()
would not subtract the size of individually acknowledged messages.

Problem:
- When messages are acknowledged out of order using asyncDelete(), the
  backlog count correctly decreases
- But the backlog size remains inflated because individualDeletedMessages
  are not accounted for

Example:
- 5 messages (500 bytes total)
- Individual ack of 3 messages
- Before fix: backlog size = 500 (incorrect)
- After fix: backlog size = 200 (correct)

Fix:
- Update ManagedCursorImpl.getEstimatedSizeSinceMarkDeletePosition()
  to calculate and subtract size of individual deleted entries
- Uses average entry size to estimate deleted size
- Thread-safe with proper locking

Unit test:
- testEstimatedSizeWithIndividualAcks() verifies the fix
@github-actions github-actions bot added the doc-not-needed Your PR changes do not impact docs label Dec 17, 2025
@codelipenghui codelipenghui added ready-to-test and removed doc-not-needed Your PR changes do not impact docs labels Dec 17, 2025
@codelipenghui codelipenghui self-assigned this Dec 17, 2025
@codelipenghui codelipenghui added this to the 4.2.0 milestone Dec 17, 2025
@codelipenghui
Copy link
Contributor Author

/pulsarbot run-failure-checks

@github-actions github-actions bot added the doc-not-needed Your PR changes do not impact docs label Dec 17, 2025
@codelipenghui codelipenghui added the type/bug The PR fixed a bug or issue reported a bug label Dec 17, 2025
Fixes line 1324 (127 char) and line 1342 (136 char) to be under 120 characters]
Copy link
Member

@lhotari lhotari left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please check the comment about avoiding code duplication

Comment on lines +1293 to +1317
// Get count of individually deleted entries in the backlog range
long deletedCount = 0;
lock.readLock().lock();
try {
Range<Position> backlogRange = Range.openClosed(markDeletePosition, ledger.getLastPosition());

if (getConfig().isUnackedRangesOpenCacheSetEnabled()) {
deletedCount = individualDeletedMessages.cardinality(
backlogRange.lowerEndpoint().getLedgerId(), backlogRange.lowerEndpoint().getEntryId(),
backlogRange.upperEndpoint().getLedgerId(), backlogRange.upperEndpoint().getEntryId());
} else {
AtomicLong deletedCounter = new AtomicLong(0);
individualDeletedMessages.forEach((r) -> {
if (r.isConnected(backlogRange)) {
Range<Position> intersection = r.intersection(backlogRange);
long countInRange = ledger.getNumberOfEntries(intersection);
deletedCounter.addAndGet(countInRange);
}
return true;
}, recyclePositionRangeConverter);
deletedCount = deletedCounter.get();
}
} finally {
lock.readLock().unlock();
}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This part seems to duplicate logic that currently exists in getNumberOfEntries method. Avoiding code duplication would be preferred. Would it be possible to extract a common method that is used in both getEstimatedSizeSinceMarkDeletePosition and getNumberOfEntries?

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

Labels

doc-not-needed Your PR changes do not impact docs ready-to-test release/4.0.9 release/4.1.3 type/bug The PR fixed a bug or issue reported a bug

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants