eolivelli opened a new pull request, #4760:
URL: https://github.com/apache/bookkeeper/pull/4760

   ## Motivation
   
   `monitorPendingAddOps()` iterates over the `pendingAddOps` queue and calls
   `maybeTimeout()` on each op. Concurrently, `sendAddSuccessCallbacks()` can
   remove a completed op from the queue, call `submitCallback()` on it, and
   ultimately trigger `recyclePendAddOpObject()` which sets `clientCtx = null`.
   
   If the scheduler thread still holds a live iterator reference to the same op
   and then calls `maybeTimeout()`, dereferencing `clientCtx` causes a
   `NullPointerException`:
   
   ```
   java.lang.NullPointerException
       at 
org.apache.bookkeeper.client.PendingAddOp.maybeTimeout(PendingAddOp.java:157)
       at 
org.apache.bookkeeper.client.LedgerHandle.monitorPendingAddOps(LedgerHandle.java:2063)
   ```
   
   The race is only triggered when `addEntryQuorumTimeoutNanos > 0` (i.e. the
   quorum-timeout monitor is actually scheduled). It became reliably observable
   with **Netty 4.1.130**, which changed `Recycler` thread-scheduling behavior
   and narrowed the window enough to expose the pre-existing race.
   
   Closes #4759.
   
   ## Changes
   
   ### `PendingAddOp.java`
   * **`clientCtx` → `volatile ClientContext clientCtx`**  
     `recyclePendAddOpObject()` is `synchronized` but `maybeTimeout()` is not.
     Without `volatile` the JMM does not guarantee that the `null` write in
     `recyclePendAddOpObject()` is visible to the unsynchronized read in
     `maybeTimeout()`. Making the field `volatile` (consistent with the existing
     `volatile long requestTimeNanos`) closes the visibility gap.
   * **Null guard at the top of `maybeTimeout()`**  
     If `clientCtx` is `null` the op has already been recycled and its
     add-entry completed; there is nothing to time out, so the method
     returns `false` immediately.
   
   ### `PendingAddOpTest.java`
   Three new unit tests:
   
   | Test | What it verifies |
   |---|---|
   | `testMaybeTimeoutReturnsFalseWhenClientCtxIsNull` | The exact race: 
`clientCtx = null` must not NPE |
   | `testMaybeTimeoutReturnsFalseWhenWithinQuorumTimeout` | Normal path — not 
yet timed out |
   | `testMaybeTimeoutReturnsTrueWhenQuorumTimeoutExpired` | Normal path — 
timed out, `true` returned |
   
   ## Testing
   
   ```
   mvn -pl bookkeeper-server -Dtest='PendingAddOpTest' test
   ```
   
   ```
   Tests run: 4, Failures: 0, Errors: 0, Skipped: 0
   BUILD SUCCESS
   ```
   
   🤖 Implemented by the `pr-worker` agent.


-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: [email protected]

For queries about this service, please contact Infrastructure at:
[email protected]

Reply via email to