Hacktoberfest 2026: le issue che i maintainer hanno segnato per ottobre, aperte e adatte ai principianti. Sfoglia le issue Hacktoberfest

[Bug] notifyMasterOnline aborts the long-polling wake-up with a NullPointerException when an arrival drains a bucket first

Aperta Adatta ai principianti
#11,441 1 commento 0 reazioni 0 assegnatari Vedi su GitHub

I maintainer di solito rispondono entro 1 giorno

@iloveeyjafjalla ci sta già lavorando.

Dal 14/10/2026.

  • #11442 di @iloveeyjafjalla — aperta

Valutazione

Difficoltà
2/5
Tempo stimato
1-3 ore
Idoneità per principianti
82/100
Tipo di issue
Bug
Chiarezza
Specificata chiaramente
Stato di attività
Attiva
Stack tecnologico
java
Ambito
backend

Direzione di ricerca

Inizia da PullRequestHoldService.notifyMasterOnline, intorno alla riga 191, dove il ciclo su pullRequestTable chiama mpr.cloneListAndClear() e itera direttamente il risultato. Leggi ManyPullRequest.cloneListAndClear(), che restituisce null per un bucket vuoto, e confrontalo con il controllo null in notifyMessageArriving. È completato quando un bucket svuotato in concorrenza viene saltato e la scansione risveglia comunque le richieste sulle altre code.

Scritto dal modello di indicizzazione a partire dal testo della issue.

Descrizione

Describe the Bug

When the smallest broker of a group changes and this broker becomes the master, BrokerController#onMinBrokerChange ends with pullRequestHoldService.notifyMasterOnline(), whose job is to immediately re-execute every pull request that is currently suspended, so that clients do not have to wait for their own suspend timeout after a master comes online.

That sweep assumes a bucket it has just seen as non-empty still holds requests when it drains it:

for (ManyPullRequest mpr : this.pullRequestTable.values()) {
    if (mpr == null || mpr.isEmpty()) {
        continue;
    }
    for (PullRequest request : mpr.cloneListAndClear()) {

ManyPullRequest#cloneListAndClear() returns null (not an empty list) whenever the bucket is empty at drain time:

public synchronized List<PullRequest> cloneListAndClear() {
    if (!this.pullRequestList.isEmpty()) {
        ...
        return result;
    }
    return null;
}

The buckets are drained by the message-arrival path on the store's dispatch thread (NotifyMessageArrivingListener -> PullRequestHoldService#notifyMessageArriving, which drains the same bucket and replays the requests that did not match). An arrival that wins the race between the isEmpty() check above and the drain makes the loop iterable null, so the enhanced for throws a NullPointerException.

Impact
  1. The NullPointerException escapes notifyMasterOnline() into onMinBrokerChange. The sweep is over, so every bucket that had not yet been visited keeps its requests suspended: those clients sit out their full suspend timeout instead of being woken immediately - exactly the stall this method exists to prevent, and in exactly the failover window it is meant for.
  2. The exception is thrown on the thread that updates the min-broker state (including the UPDATE_MIN_BROKER admin path), so the caller sees a failure for an operation that has otherwise already been applied.

The same class shows what the intended contract is: notifyMessageArriving drains a bucket the same way and guards the result with if (requestList != null) before iterating, so the missing guard in notifyMasterOnline is an oversight rather than a documented behaviour.

How to Reproduce
  • Suspend a pull request on some topic@queueId (long polling enabled, i.e. the client sent the suspend flag and the pull returned PULL_NOT_FOUND longer than the max. offset).
  • Produce a message for that queue from another thread, so the dispatch thread drains the same bucket through notifyMessageArriving in the same instant as notifyMasterOnline().
  • The stricter, deterministic form of the race: the bucket reports itself as non-empty and is drained before notifyMasterOnline() drains it, which is what makes the sweep iterable null.
  • Result with the current code: java.lang.NullPointerException at org.apache.rocketmq.broker.longpolling.PullRequestHoldService.notifyMasterOnline(PullRequestHoldService.java:191), and the remaining buckets are not woken.
Expected Behavior

A bucket that lost that race is skipped; the sweep continues and still wakes the requests held for the other queues.

Proposed Fix

Read the drained list into a local variable and skip the bucket when it answers null, instead of iterating the call result directly:

List<PullRequest> requestList = mpr.cloneListAndClear();
if (requestList == null) {
    continue;
}
for (PullRequest request : requestList) {
Environment
  • 5.5.1-SNAPSHOT (develop, 0dd48c39db), all platforms
Lingua principale
Java
Stelle
22.6k
Fork
12k
Merge medio
6g 21h
PR unite (30g)
17

Preparare l'ambiente

Come iniziare

  1. Leggi tutta la issue e poi la guida ai contributi del progetto.
  2. Commenta sulla issue per dire che te ne occupi tu — evita che due persone facciano lo stesso lavoro.
  3. Fai un fork del repository e lavora su un branch.
  4. Apri una pull request che faccia riferimento al numero della issue.

Altre issue di apache/rocketmq

Tutte le issue di apache/rocketmq

Issue simili

Altre issue su Java

Ricevi le nuove issue nella tua casella

Un breve riepilogo di issue GitHub adatte ai principianti.