Skip to content

[Bug] A race between RocksDB compaction and periodic Raft snapshot save can commit an invalid snapshot directory that contains __raft_snapshot_meta but no data/ #3162

Description

@vaijosh

Bug Type (问题类型)

others (please edit later)

Before submit

  • 我已经确认现有的 IssuesFAQ 中没有相同 / 重复问题 (I have confirmed and searched that there are no similar problems in the historical issue and documents)

Environment (环境信息)

  • Server Version: 1.0.0 (Apache Release Version)
  • Backend: RocksDB x nodes, HDD or SSD
  • OS: xx CPUs, xx G RAM, Ubuntu 2x.x / CentOS 7.x
  • Data Size: xx vertices, xx edges

Expected & Actual behavior (期望与实际表现)

Expected Behavior

  • If snapshot save is triggered while compaction is active (state == doing), snapshot save should return non-OK to JRaft.
  • JRaft should not commit a snapshot point unless snapshot data is actually persisted and files are registered.
  • Snapshot directories should not be committed in a meta-only state.

Actual Behavior

  • During compaction, snapshot handler can exit early without writing data/ (pre-fix path).
  • Caller still reports success (done.run(Status.OK())) to JRaft.
  • JRaft commits snapshot metadata, so snapshot index advances.
  • Snapshot directory may contain only __raft_snapshot_meta, with missing data/.
  • On restart/load, snapshot data path is missing, causing load/init failure and retry loops.

Race Condition Timeline (Sequence of Events)

  1. dbCompaction() starts for partition P.
  2. setState(P, doing) is set, then op.compactRange() begins and blocks.
  3. While compaction is running, periodic JRaft snapshot timer fires for P.
  4. JRaft invokes PartitionStateMachine.onSnapshotSave(writer, done).
  5. PartitionStateMachine calls SnapshotHandler.onSnapshotSave(writer).
  6. In vulnerable behavior, handler exits early on busy state (state == doing) without writing data/ and without registering files.
  7. Control returns normally, so caller executes done.run(Status.OK()).
  8. JRaft treats snapshot save as successful and commits snapshot metadata (__raft_snapshot_meta), advancing snapshot index.
  9. Compaction later completes and state changes to compactionDone.
  10. A later blank-task snapshot may be triggered, but this is a new snapshot attempt, not a repair of the already committed bad snapshot.
  11. If restart/load occurs before a newer valid snapshot supersedes it, loader targets the committed corrupt snapshot and fails on missing data/.## Disk/Log Fingerprint

Corrupt snapshot signature:

  • snapshot_<N>/__raft_snapshot_meta exists
  • snapshot_<N>/data/ missing
  • snapshot_<N>/should_not_load often missing for early-return path

Typical failure path:

  • snapshot save appears successful at callback level
  • snapshot load later fails because snapshot_<N>/data is absent

Impact

  • Affected partition may become unavailable after restart/snapshot load.
  • Partition can enter repeated init failure/retry.
  • Operational recovery may require manual cleanup and restart if no newer valid snapshot is available.

Severity

High (data-plane availability and recovery risk at partition level).

Vertex/Edge example (问题点 / 边数据举例)

Schema [VertexLabel, EdgeLabel, IndexLabel] (元数据结构)

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't workingraftrocksdbRocksDB backend

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions