Repository navigation
[improve][test] Speed up five Broker Group 1 test classes: transactions, lost ledgers and BrokerService - #27341
Merged
merlimat merged 6 commits intoNov 3, 2026
Conversation
Each receive loop ended with a 5 s receive timeout, and each acknowledge() waited for the 100 ms ack grouping before its receipt. The loops now receive the expected number of messages (all of them, then all but the acked first entry and the lost ledger), and the acks are sent at once. The two acks before the ledger loss are sent in one call: the cursor persists them in one mark-delete. Its mark-delete rate limit (1 per second) leaves a second mark-delete right after the first one unpersisted until the next cursor flush; the old receive timeout gave the limiter time to refill.
Five tests waited out receive timeouts (5 s, or 2 s) to show that no more messages arrive, and testTBSnapshotWriter slept 3 s. They now receive the expected messages and then use assertNoMoreMessages (the subscription's cursor past the transaction buffer's max read position, then a 200 ms receive), and testTBSnapshotWriter waits until every partition is loaded, so its transaction buffer holds the injected writer future that it then fails, instead of sleeping. testRetryExceptionOfEndTxn checks that the commit doesn't complete for 2 s instead of 5 s while the coordinator's store isn't ready: the coordinator retries the end op every second, so the window still covers a retry.
individualPendingAckReplayTest sent and received its 1000 messages one at a time, so every receive waited for the broker's new entries check delay (managedLedgerNewEntriesCheckDelayInMillis, 10 ms): it now sends them all, still one entry each, then receives them. Both replay tests turn off the mock BookKeeper's 1 ms delay per add: every transactional ack is an add to the pending ack log. The non-retryable init failure case waited for its client's 3 s operation timeout: the broker fails every subscribe attempt the same way and the client retries until that timeout. It is 1 s now, still past the broker's 100 ms retry backoff, so a broker retrying the failure would still let the subscribe succeed and fail the test.
testEndTransactionOpRetry waited 5 s to show that ending a transaction while the coordinator's store isn't ready doesn't complete. It now calls the service through a Mockito spy, which also sees the retries, waits until the op has been retried (endTransaction ran a second time, after the 1 s retry interval) and checks that it hasn't completed. The mock BookKeeper's 1 ms delay per add is off: every new transaction and status change is an add to the coordinator's log, 3000 of them in testTimeoutTracker.
testCheckInactiveSubscriptionWhenNoMessageToAck waited 3 s after each inactive subscription check to show that the subscription isn't deleted. The check decides at once and a subscription it deletes is gone within milliseconds, so the subscription now has to stay for 500 ms. The 1 s sleep stays: both checks need the last activity to be older than their 1 s expiration time.
codelipenghui
approved these changes
Nov 3, 2026
codelipenghui
left a comment
Contributor
There was a problem hiding this comment.
Reviewed 878f8b3. No actionable findings identified within the reviewed scope.
Local validation passed with retries disabled: 25 focused test invocations across the five changed classes, followed by all 37 tests in TransactionTest.
The existing Broker Group 1 CI failure remains unexplained; the local passing runs do not establish that it is unrelated to this change.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Motivation
CI - Unit - Brokers - Broker Group 1 is the longest unit test job. In its last three scheduled master runs
(41443581285, 41587218802, 41634315240), the methods below spent about 127 s of each run waiting out fixed waits:
receives that must time out to show that nothing more arrives, client and coordinator retries, and delays the tests
hit by doing one thing at a time.
LedgerLostAndSkipNonRecoverableTesttestMarkDeletedPositionCanForwardAfterTopicLedgerLost[0], [2]TransactionTesttestReadCommittedWithReadCompactedtestRetryExceptionOfEndTxntestTransactionAckMessages,testTransactionAckMessageListtestTBSnapshotWritertestReadCommittedWithCompactionTransactionMetadataStoreServiceTesttestEndTransactionOpRetry[0], [4]testTimeoutTrackerPendingAckPersistentTestindividualPendingAckReplayTestcumulativePendingAckReplayTesttestBuildConsumerEncounterPendingAckInitFailure[0], [2], [4]BrokerServiceTesttestCheckInactiveSubscriptionWhenNoMessageToAckModifications
Test-only. No assertion is removed, and no timer of the broker or the client that a check depends on is shortened.
One commit per class:
LedgerLostAndSkipNonRecoverableTest: both receive loops receive the expected number of messages (all ofthem, then all but the acked first entry and the lost ledger) instead of receiving until a 5 s receive times out,
and the consumer sends its acks at once (
acknowledgmentGroupTime(0)): with ack receipts on, everyacknowledge()waited for the 100 ms grouping. The two acks before the ledger loss go in oneacknowledge(List), so the cursor persists them with one mark-delete: the cursor's mark-delete rate limit (1 persecond) keeps a second mark-delete right after the first one unpersisted until the next cursor flush (60 s), and
the test waits for the persisted position. The old receive timeout left the limiter time to refill.
TransactionTest: the read-committed and transactional-ack tests receive the expected messages, then useassertNoMoreMessages([improve][test] Speed up ten Broker Group 1 test classes #27322 moved it toTransactionTestBase): every subscription's cursor past thetransaction buffer's max read position, nothing to redeliver, then a 200 ms receive.
testTBSnapshotWriterwaits until all 20 partitions are loaded, so every partition's transaction buffer holds the injected writer
future before it fails, instead of sleeping 3 s.
testRetryExceptionOfEndTxnchecks that the commit doesn'tcomplete for 2 s instead of 5 s while the coordinator's store isn't ready: the coordinator retries the end op
every second (
endTransactionRetryIntervalTime), so the window still covers a retry.TransactionMetadataStoreServiceTest:testEndTransactionOpRetrycalls the service through a Mockito spy,which also sees the retries, waits until the end op has been retried (
endTransactionran a second time, 1 slater) and checks that it hasn't completed, instead of waiting 5 s. The mock BookKeeper's 1 ms delay per add is
off: every new transaction and status change is an add to the coordinator's log.
PendingAckPersistentTest:individualPendingAckReplayTestsent and received its 1000 messages one at atime, so every receive waited for the broker's new entries check delay (
managedLedgerNewEntriesCheckDelayInMillis,10 ms). It now sends them all, still one entry each, then receives them. Both replay tests turn off the mock
BookKeeper's 1 ms delay per add: every transactional ack is an add to the pending ack log. In
testBuildConsumerEncounterPendingAckInitFailure, the broker fails every subscribe attempt of thenon-retryable case the same way, and the client retries until its operation timeout, now 1 s instead of 3 s.
That is still well past the pending ack handle's 100 ms retry backoff, so a broker retrying the failure would
still let the subscribe succeed (the mock's next answer) and fail the test.
BrokerServiceTest.testCheckInactiveSubscriptionWhenNoMessageToAck: the subscription has to stay for 500 msinstead of 3 s after each inactive subscription check. The check decides at once, and a subscription it deletes
is gone within milliseconds. The 1 s sleep stays: both checks need the last activity to be older than their 1 s
expiration time.
Kept: the transaction timeouts that
TransactionMetadataStoreServiceTest's timeout tests wait for, which are whatthey check, and the broker restart before each method of
TransactionMetadataStoreServiceTestandPendingAckPersistentTest. The former's methods rely on a fresh coordinator log (open transactions left by onemethod would be recovered by the next), the latter's replace broker components and change its configuration
without restoring them, and #27325 adds methods to it.
LedgerLostAndSkipNonRecoverableTest's 15-17 s outside itsmethods in CI is the cold start of its shard's JVM: it runs first in its shard, and the first class of every shard
pays about the same.
Verifying this change
Back to back on the same machine,
./gradlew :pulsar-broker:test --tests <class> --rerun -PtestFailFast=false,master and this change alternating, two rounds, times in seconds (class time includes the methods not changed):
LedgerLostAndSkipNonRecoverableTestTransactionTesttestReadCommittedWithReadCompacted10.2 → 0.8,testRetryExceptionOfEndTxn5.4-5.5 → 2.2,testTransactionAckMessages5.2 → 0.5,testTransactionAckMessageList5.1 → 0.4,testTBSnapshotWriter4.1-4.2 → 1.2-1.4,testReadCommittedWithCompaction2.5 → 0.8TransactionMetadataStoreServiceTesttestEndTransactionOpRetry5.7 → 2.4-2.5 (each),testTimeoutTracker5.0 → 2.5-2.8,testTimeoutTrackerMultiThreading4.9 → 4.3-4.4PendingAckPersistentTestindividualPendingAckReplayTest15.7-15.9 → 1.6,cumulativePendingAckReplayTest9.1-9.7 → 2.5-2.9,testBuildConsumerEncounterPendingAckInitFailure3.5-3.8 → 1.9-2.1 (each)BrokerServiceTestThe changed methods take ~32 s locally instead of ~125 s, about 1.5 minutes less test time per run of Broker Group 1
(~25 s less job time with its four shards).
All the changed methods together (25 tests, with the whole of
LedgerLostAndSkipNonRecoverableTestandTransactionMetadataStoreServiceTest), four forks, retries off (-PtestRetryCount=0): 10 rounds without a failure. IntestTBSnapshotWriter, all 20 partitions still get theinjected writer failure (20 "Failed to create snapshot writer" logs per run, as on master).
Mutation checks, one broker change at a time, with master's test and this one (retries off):
skipNonRecoverableLedgerdoes nothing)testMarkDeletedPositionCanForwardAfterTopicLedgerLost[0], [2]testReadCommittedWithReadCompactedtestTransactionAckMessagestestTransactionAckMessageListtestRetryExceptionOfEndTxntestEndTransactionOpRetry[0], [4]testTBSnapshotWriterindividualPendingAckReplayTestcumulativePendingAckReplayTesttestBuildConsumerEncounterPendingAckInitFailure[0], [2], [4]testCheckInactiveSubscriptionWhenNoMessageToAcktestCheckInactiveSubscriptionWhenNoMessageToAcktestTransactionAckMessageListcatches its mutation with the check before the changed one. A committedtransaction whose acks never reach the cursor but stay pending passes both versions of the ack tests: the dispatcher
doesn't deliver pending-acked positions. An expiration check without its connected-consumer guard also passes both versions of
testCheckInactiveSubscriptionWhenNoMessageToAck(run: 7.9 s and 3.1 s): the subscription's delete refuses whilea consumer is connected.
Does this pull request potentially affect one of the following parts:
If the box was checked, please highlight the changes