Conversation
fresh-borzoni
left a comment
There was a problem hiding this comment.
@zuston Thank you for the PR, left a couple of comments and questions, PTAL
| @@ -323,16 +327,9 @@ protected NativeRocksDBSnapshotResources( | |||
|
|
|||
| @Override | |||
| public void release() { | |||
There was a problem hiding this comment.
release() is empty now and SnapshotRunner is its only caller. Remove it?
There was a problem hiding this comment.
I have introduced the dedicated option for this feature, so let's reserve this
| // make as leader again, should restore from snapshot | ||
| // Recover as leader after the in-place restart, without an intervening role transition. | ||
| makeKvReplicaAsLeader(kvReplica, 2); | ||
| assertThat(downloadedRemoteSnapshot).isFalse(); |
There was a problem hiding this comment.
Can we get the other half: invalid checkpoint, assert downloadedRemoteSnapshot is true?
| } | ||
|
|
||
| // as we have downloaded kv files into the tablet dir, now, we can load it | ||
| kvTablet = kvManager.loadKv(tabletDir, schemaGetter, this::onKvFlushComplete); |
There was a problem hiding this comment.
When the local copy won't open, loadKv throws and we never reach downloadKvSnapshots, so the broken-snapshot handling from #1482 never runs, and the retries at 743 just repeat. Therefore replica ends up stuck with nothing clearing the directory.
Shall we drop the local checkpoint and fall through to the download instead? Not via handleSnapshotBroken though, the remote snapshot is fine here.
| } | ||
| // The local checkpoint is useful after an in-place restart. Keep the committed snapshot and | ||
| // remove older or uncommitted checkpoints only after the remote commit succeeds. | ||
| LocalKvSnapshotUtils.retainOnly(instanceBasePath, completedSnapshotId); |
There was a problem hiding this comment.
Should this be behind a flag? Local recovery is normally opt-in, like Flink has state.backend.local-recovery and also #2179 shipped snapshot retention with a TTL and metrics.
This turns retention on for every KV bucket with no way off, so feels off.
There was a problem hiding this comment.
This deletes the whole tablet dir for standbys too, so the retained checkpoint only survives while the replica stays leader. A standby that gets promoted still downloads from remote.
Is keeping it for standbys in scope, or deliberately out? Looks like what the TODO in processRetryOfflineLeader is anticipating.
There was a problem hiding this comment.
Good point. This PR only optimizes in-place restarts of the leader TabletServer. Standby promotion still recovers from remote storage; maintaining local snapshots on standbys requires a separate lifecycle and is out of scope for this PR. We can track it separately, that is another bigger feature to speed up the KV leader transition.
fresh-borzoni
left a comment
There was a problem hiding this comment.
@zuston Thank you, LGTM 👍 One non-blocking comment
cc @swuferhong
| + "The default setting is 10 minutes."); | ||
|
|
||
| public static final ConfigOption<Boolean> KV_SNAPSHOT_LOCAL_RECOVERY_ENABLED = | ||
| key("kv.snapshot.local-recovery.enabled") |
There was a problem hiding this comment.
This one's static, while the other KV knobs can be changed at runtime (kv.snapshot.interval, kv.leader-replica.memory-reserved, the disk ratios). Since it costs disk, someone watching DISK_USAGE_RATIO go up can only switch it off by restarting.
Could we do what the interval does? PeriodicSnapshotManager takes a LongSupplier for it and the key is in ALLOWED_CONFIG_KEYS.
Though switching it off wouldn't free anything by itself. release() would stop keeping new checkpoints, but snap-N is still there until the next remote restore, so notifySnapshotComplete would need to clear the snap-* dirs when it's off.
There was a problem hiding this comment.
This one's static, while the other KV knobs can be changed at runtime (kv.snapshot.interval, kv.leader-replica.memory-reserved, the disk ratios). Since it costs disk, someone watching DISK_USAGE_RATIO go up can only switch it off by restarting.
Yes, this could be updated dynamiclly to follow up in the next PR. One tip that this feature won't cost extra disk capacity because the snapshot is just a hard link referenced by the rocksdb files.
There was a problem hiding this comment.
Not quite. snap-N hard-links the SSTs at snapshot time. Compaction later removes them from db/, but snap-N keeps them alive until N+1 completes.
Could we at least mention this in the option description?
|
rebased @fresh-borzoni |
fresh-borzoni
left a comment
There was a problem hiding this comment.
@zuston Thank you, left some comments, PTAL
| */ | ||
| public void startup() { | ||
| cleanupStaleKvDirectories(); | ||
| if (!conf.get(ConfigOptions.KV_SNAPSHOT_LOCAL_RECOVERY_ENABLED)) { |
There was a problem hiding this comment.
With the flag on this skips the #4401 cleanup, so #4400 comes back.
Controlled shutdown keeps the old leader's KV dir, since CoordinatorRequestBatch's addNotifyLeaderRequestForTabletServers skips shutting-down servers. The server restarts as a follower, and Replica.dropKv() is a no-op because kvTablet == null. So db/ + snap-N stay on every bucket it now follows, and they survive a table drop.
Reproduced this behaviour in ReplicaTest: with the flag on, db/ + snap-0 survive restart -> follower -> Replica.delete(). With the flag off, the startup cleanup removes them.
Could we keep the dirs at startup, but delete a leftover dir when the replica becomes follower with no open tablet? And how would dirs of buckets that are never assigned back get reclaimed?
@swuferhong WDYT?
| + "The default setting is 10 minutes."); | ||
|
|
||
| public static final ConfigOption<Boolean> KV_SNAPSHOT_LOCAL_RECOVERY_ENABLED = | ||
| key("kv.snapshot.local-recovery.enabled") |
There was a problem hiding this comment.
Not quite. snap-N hard-links the SSTs at snapshot time. Compaction later removes them from db/, but snap-N keeps them alive until N+1 completes.
Could we at least mention this in the option description?
|
|
||
| // as we have downloaded kv files into the tablet dir, now, we can load it | ||
| kvTablet = kvManager.loadKv(tabletDir, schemaGetter, this::onKvFlushComplete); | ||
| kvTablet = restoreKvTablet(completedSnapshot); |
There was a problem hiding this comment.
Not specifically coming from this PR, but noticed while reading and reproduced on main: if recoverKvTablet fails once after a snapshot restore, all 5 initKvTablet attempts fail. Attempt 1's RocksDB is still open, so each retry re-downloads the snapshot and fails with RocksDBException: lock hold by current process .../db/LOCK. The no-snapshot branch already reuses the open tablet. Shall we file an issue for this since we are here?
| completedSnapshot.getSnapshotID(), | ||
| System.currentTimeMillis() - start); | ||
| return restoredKvTablet; | ||
| } catch (KvBuildingException localRecoveryException) { |
There was a problem hiding this comment.
nit: a counter for local hits vs fallbacks would show whether this actually kicks in. #2179 shipped metrics with snapshot retention, is it applicable here, wdyt?
| Path restoreDirectory = kvTabletDir.toPath().resolve(RESTORE_DIRECTORY_PREFIX + snapshotId); | ||
| Path activeDbDirectory = RocksDBKvBuilder.getInstanceRocksDBPath(kvTabletDir).toPath(); | ||
| try { | ||
| FileUtils.deleteDirectory(restoreDirectory.toFile()); |
There was a problem hiding this comment.
nit: a leftover .db-restore- gets removed only by a later restore with the same id.
Could retainOnly also sweep .db-restore-*?
|
|
||
| private KvTablet restoreKvTablet(CompletedSnapshot completedSnapshot) throws Exception { | ||
| checkNotNull(kvManager); | ||
| long start = System.currentTimeMillis(); |
There was a problem hiding this comment.
nit: clock.milliseconds() like initKvTablet above? Is there any specific reason to use different one?
|
|
||
| // as we have downloaded kv files into the tablet dir, now, we can load it | ||
| kvTablet = kvManager.loadKv(tabletDir, schemaGetter, this::onKvFlushComplete); | ||
| kvTablet = restoreKvTablet(completedSnapshot); |
There was a problem hiding this comment.
Which setup is this aimed at?
With RF≥2, leadership moves to another replica on controlled shutdown or crash, so this server restarts as a follower. The local copy helps only if leadership returns before the new leader's next snapshot. After that the snapshot id moves on and we download anyway.
So it mostly helps RF=1 or ISR={leader}. Shall we say that in the option description?
There was a problem hiding this comment.
good point. the motivation of this pr is to speed up the local recovery for multi RFs. When I upgrade the whole fluss cluster to do some quick experiment or the new features validation, I will shutdown the coordinator directly firstly, and then restart the tablet servers to avoid leader changes to reduce the recovery time cost. (btw for the recommended recovery way, the recovery time is really too long especially when rolling upgrading for the large scale bucket numbers)
based on the above requirements, I have to optimize the local recovery speed without leader change.
Purpose
This PR enables KV tablets to be rebuilt from valid local snapshots, speeding up recovery—especially when the tablet server restarts without a leader change.
Brief change log
Tests
API and Format
Documentation