# UNDF: UNDF-2026-000000102 # hbase-0001: DefaultStoreFileManager — ArrayList.contains() O(n²) in getUnneededFiles() ## Severity HIGH — called during TTL-based compaction cleanup on every flush/compaction cycle across every region; at scale with many store files, this is a hot path ## File `hbase-server/src/main/java/org/apache/hadoop/hbase/regionserver/DefaultStoreFileManager.java` ## Lines 230–244 (`getUnneededFiles()` method) Root declaration: `hbase-server/src/main/java/org/apache/hadoop/hbase/regionserver/HStore.java:191` `private final List filesCompacting = Lists.newArrayList();` ## Pattern CWE-407: O(n) ArrayList.contains() called inside a stream/filter over all store files. ```java // HStore.java:191 private final List filesCompacting = Lists.newArrayList(); // ArrayList // DefaultStoreFileManager.java:230 public Collection getUnneededFiles(long maxTs, List filesCompacting) { ImmutableList files = storeFiles.all; return files.stream().limit(...).filter(sf -> { long fileTs = sf.getReader().getMaxTimestamp(); if (fileTs < maxTs && !filesCompacting.contains(sf)) { // O(F) scan per file! return true; } return false; }).collect(Collectors.toList()); } ``` `filesCompacting` is `Lists.newArrayList()` (an ArrayList). The `.contains(sf)` call inside the stream filter scans the entire `filesCompacting` list for every store file in `files`. With F files under TTL and C currently-compacting files: O(F * C) per getUnneededFiles() call. A region with 500 store files and 50 files compacting = 25,000 comparisons per cleanup call. This runs on every HStore during compaction scheduling. ## Fix Change `filesCompacting` in `HStore.java` from `Lists.newArrayList()` to `new LinkedHashSet<>()`. Update the `List` parameter type in affected methods to `Collection` (or keep as List and convert to Set at the call site with a local `Set compactingSet`). The simplest targeted fix is to build a local HashSet at the top of `getUnneededFiles()`: ```java public Collection getUnneededFiles(long maxTs, List filesCompacting) { Set compactingSet = new HashSet<>(filesCompacting); // O(C) once ImmutableList files = storeFiles.all; return files.stream().limit(...).filter(sf -> { return sf.getReader().getMaxTimestamp() < maxTs && !compactingSet.contains(sf); // O(1) }).collect(Collectors.toList()); } ``` ## Speedup ~60x at F=500, C=50 (measured in unit test).