Yap Sok Ann created CASSANDRA-15743:
---------------------------------------

             Summary: ActiveRepairService#terminateSessions can leak RepairJob 
threads 
                 Key: CASSANDRA-15743
                 URL: https://issues.apache.org/jira/browse/CASSANDRA-15743
             Project: Cassandra
          Issue Type: Bug
          Components: Consistency/Repair
            Reporter: Yap Sok Ann


(We reported this to 
https://github.com/thelastpickle/cassandra-reaper/issues/898, as the behavior 
can be triggered by reaper. I will copy-paste here and rephrase slightly..)

We have a fairly big table (240GB per node) where the reaper repairs would kept 
failing as they get killed by reaper's {{handlePotentialStuckRepairs}}, which 
calls {{ActiveRepairService#terminateSessions}}.

On this cluster (with G1GC), we also experience memory leak, where the old gen 
would keep growing, until JVM has to do minutes-long full GC, which still 
couldn't recover much memory from the old gen.

>From heapdump, we eventually trace the memory leak to dozens of {{RepairJob}} 
>threads, each one holding on to hundreds of megabytes of {{MerkleTrees}} 
>objects.

The threads would look like this in jmap output (cassandra 3.11.4):

{code}
"Repair#30:1" #14352 daemon prio=5 os_prio=0 tid=0x00007f39f6ac7110 nid=0x1f3d 
waiting on condition [0x00007f7609ce8000]
   java.lang.Thread.State: WAITING (parking)
        at sun.misc.Unsafe.park(Native Method)
        - parking to wait for  <0x0000000243c005a8> (a 
com.google.common.util.concurrent.AbstractFuture$Sync)
        at java.util.concurrent.locks.LockSupport.park(LockSupport.java:175)
        at 
java.util.concurrent.locks.AbstractQueuedSynchronizer.parkAndCheckInterrupt(AbstractQueuedSynchronizer.java:836)
        at 
java.util.concurrent.locks.AbstractQueuedSynchronizer.doAcquireSharedInterruptibly(AbstractQueuedSynchronizer.java:997)
        at 
java.util.concurrent.locks.AbstractQueuedSynchronizer.acquireSharedInterruptibly(AbstractQueuedSynchronizer.java:1304)
        at 
com.google.common.util.concurrent.AbstractFuture$Sync.get(AbstractFuture.java:285)
        at 
com.google.common.util.concurrent.AbstractFuture.get(AbstractFuture.java:116)
        at 
com.google.common.util.concurrent.Uninterruptibles.getUninterruptibly(Uninterruptibles.java:137)
        at 
com.google.common.util.concurrent.Futures.getUnchecked(Futures.java:1509)
        at org.apache.cassandra.repair.RepairJob.run(RepairJob.java:160)
        at 
java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1149)
        at 
java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624)
        at 
org.apache.cassandra.concurrent.NamedThreadFactory.lambda$threadLocalDeallocator$0(NamedThreadFactory.java:81)
        at 
org.apache.cassandra.concurrent.NamedThreadFactory$$Lambda$9/1896622931.run(Unknown
 Source)
        at java.lang.Thread.run(Thread.java:748)

   Locked ownable synchronizers:
        - <0x0000000243c00658> (a 
java.util.concurrent.ThreadPoolExecutor$Worker)
{code}

After checking the code, we think this is what happens:

1. reaper schedules repair #1 to node A
2. node A requests merkle trees from neighboring node B and C
3. node B finishes validation phase, sends merkle tree to node A
4. node C finishes validation phase, sends merkle tree to node A
5. reaper schedules repair #2, calls `handlePotentialStuckRepairs`
6. node A finishes validation phase
7. node A starts sync phase
8. repair #1 on node A, B, and C all stuck indefinitely, as the executor was 
already shutdown by `handlePotentialStuckRepairs`, and thus nobody would pick 
up the sync task



--
This message was sent by Atlassian Jira
(v8.3.4#803005)

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

Reply via email to