[ 
https://issues.apache.org/jira/browse/CASSANDRA-21217?page=com.atlassian.jira.plugin.system.issuetabpanels:comment-tabpanel&focusedCommentId=18108777#comment-18108777
 ] 

Stefan Miklosovic commented on CASSANDRA-21217:
-----------------------------------------------

[~nivy] something to look at? 

> Race condition between hints and decommision
> --------------------------------------------
>
>                 Key: CASSANDRA-21217
>                 URL: https://issues.apache.org/jira/browse/CASSANDRA-21217
>             Project: Apache Cassandra
>          Issue Type: Bug
>            Reporter: Gil Ganz
>            Priority: Normal
>              Labels: HintedHandoff, decommision
>          Time Spent: 10m
>  Remaining Estimate: 0h
>
> I am running decommission on 4.1.5 cluster, and decommission fails to 
> complete due to error regarding hints. It runs for quite some time, streaming 
> what appears to be all data, but then fails due to this error (which happened 
> quite early in the decommission process).
> I got this in 3 separate cases (this is an env that is spread across the 
> world, so network hiccups are common).
> I was able to overcome this by setting transfer_hints_on_decommission: false, 
> but I think the code that handles that hints in the decommision path should 
> not fail on missing file, it can just throw a warning, and not require me to 
> not transfer any hint.
> INFO  [NonPeriodicTasks:1] 2026-03-12 18:13:47,148 
> StreamResultFuture.java:252 - [Stream 
> #24728df0-1e20-11f1-be78-9bd75fd01983|#24728df0-1e20-11f1-be78-9bd75fd01983] 
> All sessions completed
> ERROR [RMI TCP Connection(3338753)-127.0.0.1] 2026-03-12 18:13:47,149 
> StorageService.java:5017 - Error while decommissioning node
> java.lang.RuntimeException: java.nio.file.NoSuchFileException: 
> /var/lib/cassandra/data/disk1/hints/5da9d583-259e-425f-a0ad-18b7e744dabc-1744041563101-2.hints
>         at 
> org.apache.cassandra.io.util.ChannelProxy.openChannel(ChannelProxy.java:54)
>         at 
> org.apache.cassandra.io.util.ChannelProxy.<init>(ChannelProxy.java:65)
>         at 
> org.apache.cassandra.hints.ChecksummedDataInput.open(ChecksummedDataInput.java:76)
>         at org.apache.cassandra.hints.HintsReader.open(HintsReader.java:78)
>         at 
> org.apache.cassandra.hints.HintsDispatcher.create(HintsDispatcher.java:79)
>         at 
> org.apache.cassandra.hints.HintsDispatchExecutor$DispatchHintsTask.deliver(HintsDispatchExecutor.java:290)
>         at 
> org.apache.cassandra.hints.HintsDispatchExecutor$DispatchHintsTask.dispatch(HintsDispatchExecutor.java:277)
>         at 
> org.apache.cassandra.hints.HintsDispatchExecutor$DispatchHintsTask.dispatch(HintsDispatchExecutor.java:255)
>         at 
> org.apache.cassandra.hints.HintsDispatchExecutor$DispatchHintsTask.run(HintsDispatchExecutor.java:234)
>         at 
> java.base/java.util.stream.ForEachOps$ForEachOp$OfRef.accept(ForEachOps.java:183)
>         at 
> java.base/java.util.stream.ReferencePipeline$3$1.accept(ReferencePipeline.java:195)
>         at 
> java.base/java.util.concurrent.ConcurrentHashMap$ValueSpliterator.forEachRemaining(ConcurrentHashMap.java:3603)
>         at 
> java.base/java.util.stream.AbstractPipeline.copyInto(AbstractPipeline.java:484)
>         at 
> java.base/java.util.stream.AbstractPipeline.wrapAndCopyInto(AbstractPipeline.java:474)
>         at 
> java.base/java.util.stream.ForEachOps$ForEachOp.evaluateSequential(ForEachOps.java:150)
>         at 
> java.base/java.util.stream.ForEachOps$ForEachOp$OfRef.evaluateSequential(ForEachOps.java:173)
>         at 
> java.base/java.util.stream.AbstractPipeline.evaluate(AbstractPipeline.java:234)
>         at 
> java.base/java.util.stream.ReferencePipeline.forEach(ReferencePipeline.java:497)
>         at 
> org.apache.cassandra.hints.HintsDispatchExecutor$TransferHintsTask.transfer(HintsDispatchExecutor.java:196)
>         at 
> org.apache.cassandra.hints.HintsDispatchExecutor$TransferHintsTask.run(HintsDispatchExecutor.java:169)
> Proposed fix
>   1. Catch missing file in {{deliver()}} ({{{}HintsDispatchExecutor.java{}}} 
> ~line 287)
>   Wrap the {{HintsDispatcher.create()}} call to catch {{RuntimeException}} 
> caused by {{{}NoSuchFileException{}}}. If the file is gone, the hints were 
> already successfully
>   dispatched — delete the descriptor from the store and return {{{}true{}}}.
>   \{code:java}
>   private boolean deliver(HintsDescriptor descriptor, InetAddressAndPort 
> address)
>   {
>       File file = descriptor.file(hintsDirectory);
>       if (!file.exists())
>       {
>           logger.info("Hints file {} was already dispatched, skipping", file);
>           store.cleanUp(descriptor);
>           return true;
>       }
>       // ... existing code
>   }
>   \{code}
>   Note: a simple {{file.exists()}} pre-check narrows the window but does not 
> eliminate the TOCTOU race. The {{try-catch}} around 
> {{HintsDispatcher.create()}} is still
>   needed as a backstop:
>   \{code:java}
>   try (HintsDispatcher dispatcher = HintsDispatcher.create(file, rateLimiter, 
> address, descriptor.hostId, shouldAbort))
>  
> {       // ... existing dispatch logic   }
>   catch (RuntimeException e)
>   {
>       if (Throwables.getRootCause(e) instanceof NoSuchFileException)
>       {
>           logger.info("Hints file {} disappeared during dispatch, treating as 
> already dispatched", file);
>           store.cleanUp(descriptor);
>           return true;
>       }
>       throw e;
>   }
>   \{code}
>   2. Prevent the race in {{transferHints()}} ({{{}HintsService.java{}}} ~line 
> 440)
>   After {{completeDispatchBlockingly()}} at line 444, call 
> {{pauseDispatch()}} again before starting the transfer at line 446. This 
> prevents {{HintsDispatchTrigger}}
>   from scheduling new normal dispatch tasks that race with the transfer:
>   \{code:java}
>   // current code at line 441:
>   resumeDispatch();
>   catalog.stores().forEach(dispatchExecutor::completeDispatchBlockingly);
>   // add:
>   pauseDispatch();
>   return dispatchExecutor.transfer(catalog, hostIdSupplier);
>   \{code}



--
This message was sent by Atlassian Jira
(v8.20.10#820010)

---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]

Reply via email to