Commit 855c60d
authored
[improve] Refactor processMessageChunk to handle ack holes and out-of-order chunks (#587)
Master Issue: apache/pulsar#13627
Related Issue: apache/pulsar#21070 and apache/pulsar#21101
### Motivation
apache/pulsar#21070 and apache/pulsar#21101 fixed two critical issues in the Java client's chunked message
handling:
1. **Unable to reassemble chunked messages after redeliver**: When a chunked message is redelivered
(e.g., due to broker unload or reconnect), the consumer receives duplicated chunks. The old
code could not handle this correctly:
- For duplicated first chunk (chunkId=0): the old context was not properly cleaned up and restarted,
causing the message to never be assembled.
- For duplicated middle chunks: the chunk would be rejected (since chunkId ≤ lastChunkedMessageId),
and the old code would discard the context entirely, making the message unrecoverable.
2. **Ack holes caused by corrupted or orphaned chunks**: When a different producer reuses the same uuid
(corrupted chunk scenario), or when chunk context is discarded due to gap/expiration, the stale cached
chunks or the incoming corrupted chunks were never acknowledged. This causes the broker subscription
cursor to get stuck, leading to message backlog accumulation that never drains — even after all
logically valid messages have been consumed and acknowledged.
These PRs added logic to distinguish between redeliver (same messageId) and corruption (different
messageId), allowing the consumer to correctly restart chunk assembly on redeliver while acking stale
chunks on corruption to prevent ack holes.
The C++ client had the same issues. This PR ports the equivalent logic to ensure consistent behavior
across all client implementations.
**Note**: Currently, after a chunked message is assembled, the ackTimeout and nack logic only tracks/handles
the last chunk message (i.e., the final messageId of the assembled message). This means if ackTimeout or
nack triggers a redeliver, only the last chunk entry is redelivered rather than all chunk entries. This
limitation needs to be addressed in a follow-up PR.
### Modifications
**Core logic changes in `ConsumerImpl.cc` (`processMessageChunk`)**:
- **Part 1 (chunkId == 0)**: When receiving a duplicated first chunk for a uuid that already has an
incomplete context, detect whether it's a redeliver (same messageId in cache) or corruption (different
messageId). For redeliver: remove old context and restart assembling. For corruption: ack all cached
chunks to avoid ack holes, then restart.
- **Part 3 (duplicated middle chunk)**: When receiving a chunk with chunkId ≤ lastChunkedMessageId,
detect whether it's a redeliver or corruption. For redeliver: simply discard the duplicate and continue
waiting for the next expected chunk. For corruption: ack the corrupted chunk to avoid ack holes.
- **Part 3 (gap chunk)**: When receiving a chunk that skips expected sequence numbers, ack the chunk if
it has expired to avoid ack holes.
- **Removed `trackMessage` calls for discarded chunks**: The old code called `trackMessage(messageId)`
for orphaned/invalid chunks (Part 2 and old Part 3), which would add the single chunk entry to the
`UnAckedMessageTracker`. When ackTimeout triggered, it would redeliver only that single chunk entry —
but this is pointless because the consumer still cannot assemble a complete chunked message from a
single chunk, and the redelivered chunk would just enter the same discard path again in an infinite loop.
- Added `LOG_WARN` and `LOG_INFO` for observability across all scenarios.
- Added detailed comments explaining each part of the chunk processing logic with examples.
**Test changes in `MessageChunkingTest.cc`**:
- Added `testResendChunkMessagesWithoutAckHole`: Verifies that resending the first chunk (chunkId=0)
allows correct reassembly without ack holes.
- Added `testResendChunkMessages`: Verifies interleaved chunk resends across multiple uuids assemble
correctly.
- Added `testResendChunkWithAckHoleMessages`: Verifies duplicated middle chunks are filtered correctly
and chunk gaps cause context cleanup.
- Refactored existing tests to reuse the `sendSingleChunk` helper function for better readability.1 parent 9c6a842 commit 855c60d
2 files changed
Lines changed: 296 additions & 60 deletions
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
488 | 488 | | |
489 | 489 | | |
490 | 490 | | |
491 | | - | |
492 | 491 | | |
493 | | - | |
| 492 | + | |
| 493 | + | |
| 494 | + | |
| 495 | + | |
494 | 496 | | |
495 | 497 | | |
496 | 498 | | |
497 | 499 | | |
498 | 500 | | |
499 | 501 | | |
500 | 502 | | |
| 503 | + | |
| 504 | + | |
| 505 | + | |
| 506 | + | |
| 507 | + | |
| 508 | + | |
| 509 | + | |
| 510 | + | |
| 511 | + | |
501 | 512 | | |
502 | | - | |
503 | | - | |
| 513 | + | |
| 514 | + | |
| 515 | + | |
| 516 | + | |
| 517 | + | |
| 518 | + | |
| 519 | + | |
| 520 | + | |
| 521 | + | |
| 522 | + | |
| 523 | + | |
| 524 | + | |
| 525 | + | |
| 526 | + | |
| 527 | + | |
| 528 | + | |
| 529 | + | |
| 530 | + | |
| 531 | + | |
| 532 | + | |
| 533 | + | |
| 534 | + | |
| 535 | + | |
| 536 | + | |
| 537 | + | |
| 538 | + | |
| 539 | + | |
| 540 | + | |
| 541 | + | |
| 542 | + | |
| 543 | + | |
| 544 | + | |
| 545 | + | |
| 546 | + | |
| 547 | + | |
| 548 | + | |
| 549 | + | |
| 550 | + | |
| 551 | + | |
| 552 | + | |
| 553 | + | |
| 554 | + | |
504 | 555 | | |
505 | 556 | | |
506 | 557 | | |
| |||
512 | 563 | | |
513 | 564 | | |
514 | 565 | | |
| 566 | + | |
| 567 | + | |
| 568 | + | |
515 | 569 | | |
516 | 570 | | |
517 | | - | |
518 | | - | |
| 571 | + | |
| 572 | + | |
| 573 | + | |
| 574 | + | |
| 575 | + | |
| 576 | + | |
519 | 577 | | |
520 | 578 | | |
521 | 579 | | |
522 | 580 | | |
523 | | - | |
524 | | - | |
525 | | - | |
526 | 581 | | |
527 | 582 | | |
528 | | - | |
| 583 | + | |
529 | 584 | | |
530 | 585 | | |
| 586 | + | |
| 587 | + | |
| 588 | + | |
| 589 | + | |
| 590 | + | |
| 591 | + | |
| 592 | + | |
| 593 | + | |
| 594 | + | |
| 595 | + | |
| 596 | + | |
| 597 | + | |
| 598 | + | |
| 599 | + | |
| 600 | + | |
| 601 | + | |
| 602 | + | |
| 603 | + | |
| 604 | + | |
531 | 605 | | |
532 | | - | |
533 | | - | |
534 | | - | |
| 606 | + | |
| 607 | + | |
| 608 | + | |
| 609 | + | |
| 610 | + | |
| 611 | + | |
| 612 | + | |
| 613 | + | |
| 614 | + | |
| 615 | + | |
| 616 | + | |
| 617 | + | |
| 618 | + | |
| 619 | + | |
| 620 | + | |
| 621 | + | |
| 622 | + | |
| 623 | + | |
| 624 | + | |
| 625 | + | |
| 626 | + | |
| 627 | + | |
| 628 | + | |
| 629 | + | |
| 630 | + | |
| 631 | + | |
| 632 | + | |
| 633 | + | |
| 634 | + | |
| 635 | + | |
| 636 | + | |
| 637 | + | |
| 638 | + | |
| 639 | + | |
| 640 | + | |
| 641 | + | |
| 642 | + | |
| 643 | + | |
| 644 | + | |
| 645 | + | |
| 646 | + | |
| 647 | + | |
| 648 | + | |
| 649 | + | |
| 650 | + | |
| 651 | + | |
| 652 | + | |
| 653 | + | |
| 654 | + | |
| 655 | + | |
| 656 | + | |
| 657 | + | |
| 658 | + | |
| 659 | + | |
| 660 | + | |
535 | 661 | | |
536 | 662 | | |
537 | | - | |
538 | | - | |
| 663 | + | |
| 664 | + | |
| 665 | + | |
| 666 | + | |
| 667 | + | |
| 668 | + | |
| 669 | + | |
| 670 | + | |
| 671 | + | |
| 672 | + | |
| 673 | + | |
| 674 | + | |
| 675 | + | |
| 676 | + | |
539 | 677 | | |
540 | 678 | | |
541 | 679 | | |
| 680 | + | |
| 681 | + | |
| 682 | + | |
542 | 683 | | |
543 | 684 | | |
544 | 685 | | |
545 | | - | |
546 | 686 | | |
547 | 687 | | |
548 | | - | |
549 | 688 | | |
550 | | - | |
551 | 689 | | |
552 | 690 | | |
553 | | - | |
554 | 691 | | |
555 | 692 | | |
| 693 | + | |
556 | 694 | | |
557 | 695 | | |
558 | 696 | | |
| |||
0 commit comments