Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
JAVA-2000: Fix ConcurrentModificationException during channel shutdown
Motivation: When a channel is shut down abruptly we iterate the inFlight map in abortAllInFlight() to fail all pending queries. But in some cases, failing a query will also indirectly mutate the map, causing a ConcurrentModificationException. We ran into this particular scenario: - the channel initializes and the write of the initial STARTUP query is scheduled (but the write future is not complete yet) - during the actual write task, an IOException is thrown ("connection reset by peer"). exceptionCaught() is called and invokes abortAllInFlight(). - abortAllInFlight() calls onFailure() on the ProtocolInitHandler.InitRequest corresponding to the STARTUP request. That calls ConnectInitHandler.setConnectFailure(), which closes the channel. - closing the channel fails the write future of the STARTUP query, which invokes the write listener synchronously. The listener calls release() which removes the callback from inFlight. - the initial iteration resumes and finds out that the map was modified. Similar issues could happen if one of the aborted requests is a SetKeyspaceRequest that calls abortAllInFlight() recursively. Modifications: - don't iterate inFlight directly, make a copy to avoid concurrent modifications. - clear it immediately so that recursive invocations of abortAllInFlight() have no effect. - ensure release() is lenient if the callback is not in inFlight. For clarity, also change it to never return the callback, the caller has to retrieve it itself (as already done in channelRead). Result: The initial call to abortInFlight() clear inFlight and fails the STARTUP query. When the write listener invokes release(), that's a no-op because the callback is not in the map anymore. Co-authored-by: Greg Bestland <Greg.Bestland@datastax.com>
- Loading branch information