Swap: chunked checkpoints for large buffers; scaled thresholds; single replay
- A checkpoint larger than one record (16 MiB) is written as CHKPT_BEGIN [size u64, CRC-32], CHKPT_DATA chunks and CHKPT_END. Replay assembles it and applies it only when complete with the size and CRC matching; an unfinished one is a torn tail (valid_bytes stops before it). Buffers over 16 MiB could not be checkpointed before: a lost record left their journal behind for good, and they were never compacted. Records are written as segments straight from the snapshot. Header version 2; version 1 journals are still read. - Checkpoint thresholds scale with the buffer size (edits of half its size; the timer only after a sixteenth; compaction at twice its size; gap retries spaced by size), and a checkpoint that would compact is written straight into the compacted journal instead of being appended and then copied. - The recovery prompt keeps the content it replayed for its preview and installs it on 'y', instead of replaying the journal a second time. - A base file over 64 MiB gets its CRC from the writer thread before it creates the journal (it had none, so touching such a file made its journal unusable for recovery). - kte_tests takes optional name substrings to run a subset of tests. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_015rcVQiiWyTzoNrsvLSaAfB
This commit is contained in:
@@ -395,6 +395,9 @@ Editor::CancelRecoveryPrompt()
|
||||
pending_recovery_open_ = PendingOpen{};
|
||||
pending_recovery_swap_path_.clear();
|
||||
pending_recovery_replay_err_.clear();
|
||||
std::string().swap(pending_recovery_content_);
|
||||
pending_recovery_valid_bytes_ = 0;
|
||||
pending_recovery_have_content_ = false;
|
||||
}
|
||||
|
||||
|
||||
@@ -407,6 +410,9 @@ Editor::ResolveRecoveryPrompt(const bool yes)
|
||||
const PendingOpen req = pending_recovery_open_;
|
||||
const std::string swp = pending_recovery_swap_path_;
|
||||
const std::string rerr_s = pending_recovery_replay_err_;
|
||||
std::string content = std::move(pending_recovery_content_);
|
||||
const bool have_content = pending_recovery_have_content_;
|
||||
std::uint64_t valid_bytes = pending_recovery_valid_bytes_;
|
||||
CancelRecoveryPrompt();
|
||||
|
||||
std::string err;
|
||||
@@ -421,11 +427,16 @@ Editor::ResolveRecoveryPrompt(const bool yes)
|
||||
SetStatus("Recovery failed: no buffer");
|
||||
return false;
|
||||
}
|
||||
std::string rerr;
|
||||
std::uint64_t valid_bytes = 0;
|
||||
if (!kte::SwapManager::ReplayFile(*b, swp, rerr, &valid_bytes)) {
|
||||
SetStatus("Swap recovery failed: " + rerr);
|
||||
return false;
|
||||
if (have_content) {
|
||||
// The prompt already replayed the journal; install that.
|
||||
b->replace_all_bytes(content);
|
||||
std::string().swap(content);
|
||||
} else {
|
||||
std::string rerr;
|
||||
if (!kte::SwapManager::ReplayFile(*b, swp, rerr, &valid_bytes)) {
|
||||
SetStatus("Swap recovery failed: " + rerr);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
// This session keeps appending to the same journal. Drop a torn
|
||||
// final record first, or new records would land behind it and the
|
||||
@@ -581,8 +592,9 @@ Editor::process_pending_opens_()
|
||||
if (tmp.OpenFromFile(req.path, oerr)) {
|
||||
const std::string orig = buffer_bytes_via_views(tmp);
|
||||
std::string rerr;
|
||||
if (kte::SwapManager::ReplayFile(tmp, swp, rerr)) {
|
||||
const std::string rec = buffer_bytes_via_views(tmp);
|
||||
std::uint64_t valid = 0;
|
||||
if (kte::SwapManager::ReplayFile(tmp, swp, rerr, &valid)) {
|
||||
std::string rec = buffer_bytes_via_views(tmp);
|
||||
if (rec == orig) {
|
||||
// Nothing to recover. Remove the journal rather than
|
||||
// appending this session's records to it (it may end in a
|
||||
@@ -592,6 +604,9 @@ Editor::process_pending_opens_()
|
||||
pending_recovery_prompt_ = RecoveryPromptKind::RecoverOrDiscard;
|
||||
pending_recovery_open_ = req;
|
||||
pending_recovery_swap_path_ = swp;
|
||||
pending_recovery_content_ = std::move(rec);
|
||||
pending_recovery_valid_bytes_ = valid;
|
||||
pending_recovery_have_content_ = true;
|
||||
StartPrompt(PromptKind::Confirm, "Recover", "");
|
||||
SetStatus("Recover swap edits for " + req.path + "? (y/n, C-g cancel)");
|
||||
return opened_any;
|
||||
|
||||
Reference in New Issue
Block a user