Feifan Wang created FLINK-41431:
-----------------------------------

             Summary: RocksDB manual compaction native calls can race with 
backend disposal
                 Key: FLINK-41431
                 URL: https://issues.apache.org/jira/browse/FLINK-41431
             Project: Flink
          Issue Type: Bug
          Components: Runtime / State Backends
    Affects Versions: 1.20.0
            Reporter: Feifan Wang


Since FLINK-26050, the RocksDB state backend periodically schedules manual SST 
compaction. All native RocksDB access performed by its asynchronous SST scan 
and compaction tasks runs on the shared ioExecutor and never acquires a 
rocksDBResourceGuard lease, so it can race with 
RocksDBKeyedStateBackend#dispose.

h3. Unguarded native calls (sstmerge)

* SST scan: CompactionScheduler#maybeScan -> CompactionTaskProducer#produce -> 
RocksDB#getLiveFilesMetaData()
* Compaction task: CompactionTracker#runWithTracking -> RocksDB#getLongProperty 
(auto-compaction count), and Compactor#compact -> 
ColumnFamilyHandle#getDescriptor() + RocksDB#compactFiles(...); the latter can 
run for a long time
* Task stringification: CompactionTask#toString/equals/hashCode delegate to 
ColumnFamilyHandle (native getID/getName); maybeScan formats scan results at 
TRACE level outside any lease, so this can run after dispose has closed the 
handle

h3. Race with dispose

RocksDBKeyedStateBackend#dispose:
# closes sstMergeManager: this sets the tracker's shut-down flag and stops the 
scheduled executor (awaiting termination up to 5s), but does not wait for tasks 
already running on the shared ioExecutor
# calls rocksDBResourceGuard.close(): manual compaction holds no lease, so this 
returns immediately while native calls are in flight
# closes the column family handles and the DB

Any native call still in flight then operates on freed native resources 
(use-after-free / SIGSEGV risk). RocksDB documents that it is not safe to close 
the DB while another thread issues manual compaction requests [1].

h3. Reachability

This race is reachable in any job with manual compaction enabled 
(state.backend.rocksdb.manual-compaction.min-interval > 0; disabled by default) 
when disposal - due to failover, cancellation, rescaling, or stopping with a 
savepoint - occurs while a scan or compaction round is in flight. A round's 
duration scales with the number and size of candidate SST files, which widens 
the window.

We detected this by code inspection while running an internal deployment with 
manual compaction enabled by default; we have not yet observed a SIGSEGV in 
production, but the race is directly reachable (deterministic unit tests can 
pin an in-flight task across dispose).

h3. Suggested fix

We have a patch with deterministic regression tests ready and are happy to open 
a PR:
* Pass rocksDBResourceGuard into RocksDBManualCompactionManager; acquire a 
lease around the SST scan and around each compaction task (including the 
tracker's property read), so dispose waits for in-flight work before releasing 
native resources. When the guard is already closed, pending tasks abort quietly 
and decrement the tracker's pending-compaction count.
* Make CompactionTask stringification/comparison native-free (handle identity 
instead of ColumnFamilyHandle#toString/equals/hashCode), since TRACE logging 
formats scan results after the lease is released.

This is complementary to FLINK-36186: aborting manual compaction at close 
(DisableManualCompaction) could speed up disposal, but any such approach still 
needs a guarded wait for in-flight native calls, and it does not cover the scan 
path or task stringification.

[1] https://github.com/facebook/rocksdb/wiki/RocksDB-FAQ - "Is it safe to close 
RocksDB while another thread is issuing read, write or manual compaction 
requests? A: No."




--
This message was sent by Atlassian Jira
(v10.3.26#10030026)

Reply via email to