[ 
https://issues.apache.org/jira/browse/CASSANDRA-21684?page=com.atlassian.jira.plugin.system.issuetabpanels:all-tabpanel
 ]

Caleb Rackliffe updated CASSANDRA-21684:
----------------------------------------
     Bug Category: Parent values: Degradation(12984)Level 1 values: Resource 
Management(12995)
       Complexity: Normal
    Discovered By: User Report
    Fix Version/s: 5.0.x
                   6.0.x
                   7.x
         Severity: Critical
           Status: Open  (was: Triage Needed)

> Unbounded per-thread cache of read-ahead buffers can cause slow heap leak
> -------------------------------------------------------------------------
>
>                 Key: CASSANDRA-21684
>                 URL: https://issues.apache.org/jira/browse/CASSANDRA-21684
>             Project: Apache Cassandra
>          Issue Type: Bug
>          Components: Local/Compaction
>            Reporter: Caleb Rackliffe
>            Assignee: Caleb Rackliffe
>            Priority: Normal
>             Fix For: 5.0.x, 6.0.x, 7.x
>
>
> {{ThreadLocalReadAheadBuffer}} caches one read-ahead Block per scanned file 
> in a per-thread map (blockMap), keyed by file path, so a thread can reuse its 
> buffer across repeated scans of the same SSTable. That map has no upper bound 
> and entries are only removed when the same thread that populated one also 
> happens to run the file's final close, which rarely lines up, since SSTables 
> are typically scanned by several different pool threads (compaction, repair, 
> reads) over their lifetime. The result: every long-lived thread accumulates a 
> permanent stale entry for every distinct SSTable it has ever scanned, growing 
> unbounded as compaction continuously creates new SSTable generations.
> {noformat}
> @Test
> public void releasingScanResourcesShouldRemoveThreadLocalBlockMapEntry() 
> throws Exception
> {
>     SequentialWriterOption writerOption = 
> SequentialWriterOption.newBuilder().finishOnClose(false).bufferSize(1 << 
> 10).build();
>     CompressionParams params = CompressionParams.snappy(4096, 1.1);
>     FileSystems.newGlobalInMemoryFileSystem();
>     File f = new File("/scan_release_repro.db");
>     File offsets = new File("/scan_release_repro.offset");
>     File digest = new File("/scan_release_repro.digest");
>     long longsToWrite = 600;
>     CompressionMetadata metadata;
>     try (CompressedSequentialWriter writer = new 
> CompressedSequentialWriter(f, offsets, digest, writerOption, params, new 
> MetadataCollector(new ClusteringComparator())))
>     {
>         for (long i = 0; i < longsToWrite; i++)
>             writer.writeLong(i);
>         writer.sync();
>         metadata = writer.open(0);
>     }
>     DatabaseDescriptor.setCompressedReadAheadBufferSizeInKb(256);
>     ByteBuffer buffer = ByteBuffer.allocateDirect(metadata.chunkLength());
>     try (ChannelProxy channel = new ChannelProxy(f);
>          CompressedChunkReader reader = new 
> CompressedChunkReader.Standard(channel, metadata, () -> 1.1);
>          metadata)
>     {
>         reader.forScan();
>         reader.readChunk(0, buffer);
>         Assert.assertTrue("expected a read-ahead Block to be present for this 
> file on this thread after a scan read",
>                            
> threadLocalBlockMapContainsKey(channel.filePath()));
>         // Mirrors what BufferManagingRebufferer.closeReader() does at the 
> end of every scan.
>         reader.releaseUnderlyingResources();
>         Assert.assertFalse("a stale Block entry for this file should not 
> remain in this thread's read-ahead " +
>                             "blockMap once the scan has released its 
> resources -- otherwise every distinct " +
>                             "file this thread ever scans leaks a permanent 
> entry",
>                             
> threadLocalBlockMapContainsKey(channel.filePath()));
>     }
>     finally
>     {
>         FileUtils.clean(buffer);
>     }
> }
> @SuppressWarnings("unchecked")
> private static boolean threadLocalBlockMapContainsKey(String filePath) throws 
> Exception
> {
>     Field field = 
> ThreadLocalReadAheadBuffer.class.getDeclaredField("blockMap");
>     field.setAccessible(true);
>     FastThreadLocal<Map<String, ?>> blockMap = (FastThreadLocal<Map<String, 
> ?>>) field.get(null);
>     return blockMap.get().containsKey(filePath);
> }
> {noformat}



--
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