mirror of
https://github.com/codewhale-hq/Codewhale.git
synced 2026-11-08 01:48:20 -10:00
fix(session): archive all off-branch history once a session passes 8 MiB
The #6842 autosave bound counts entries, not bytes: it archives off-branch
entries only once more than 4,096 sit in the document and then keeps 1,024,
which at a couple of kilobytes each can hold far more than the 8 MiB
full-history bound. On the autosave path (save_session_bounded), when the
file being replaced is already over MAX_CANONICAL_HISTORY_BYTES, every
off-branch entry is now archived instead, through the same fsynced
archive-before-remove step; below the bound the 4,096/1,024 window is
unchanged. The live journal follows through the archived-id registry, and
`/branch` can still restore an archived entry.
Known limits: the trigger stats the previous file, so it lags one save. It
does not bound the active branch, which after compaction still keeps every
pre-compaction ancestor, and the archive sidecar still grows without bound.
Other save paths (plain save_session) never prune.
Verification: checked once on the final batch tree (parent db89057d84):
cargo fmt --check clean; tui lib nextest across route_budget,
provider_catalog_live, provider_lake, mounted_history_tests,
session_manager, client, core::engine, compaction, tools::subagent,
runtime_api, purge, commands::contract, tui::model_picker and whats_new
2757 passed, 0 failed; tui integration exec_turn_usage 11 passed, 0
failed; feature_registry 2 passed, 0 failed; clippy -p codewhale-tui
--lib --tests -D warnings with the CI allow-list clean; check-blocking-
calls-budget 540 sites, within budget. Not run: hosted CI and the
loopback product captures (those follow on the release build).
Refs #6842
Refs #6955
Co-Authored-By: Claude Opus 5.5 <[email protected]>
This commit is contained in:
1 parent
4567b79241
commit
e20aaedf19
3 files changed
+37
-19
No files matched your search
@@ -143,6 +143,11 @@ guidance.
|
||||
fits, and any other request fails with a context-length error. Other
|
||||
routes are unchanged.
|
||||
|
||||
- Once a session file passes 8 MiB, autosave moves all of its abandoned
|
||||
branch history to the session's journal archive, instead of keeping up to
|
||||
4,096 off-branch entries in the document. `/branch` can still restore
|
||||
archived entries. Refs #6842. Thanks @7jrxt42BxFZo4iAnN4CX.
|
||||
|
||||
- A window that cannot save its queued messages because it holds no queue
|
||||
lease for the current session now logs both session ids, and failed
|
||||
session, checkpoint and queue writes log the raw OS error code. Refs
|
||||
|
||||
@@ -143,6 +143,11 @@ guidance.
|
||||
fits, and any other request fails with a context-length error. Other
|
||||
routes are unchanged.
|
||||
|
||||
- Once a session file passes 8 MiB, autosave moves all of its abandoned
|
||||
branch history to the session's journal archive, instead of keeping up to
|
||||
4,096 off-branch entries in the document. `/branch` can still restore
|
||||
archived entries. Refs #6842. Thanks @7jrxt42BxFZo4iAnN4CX.
|
||||
|
||||
- A window that cannot save its queued messages because it holds no queue
|
||||
lease for the current session now logs both session ids, and failed
|
||||
session, checkpoint and queue writes log the raw OS error code. Refs
|
||||
|
||||
@@ -50,7 +50,9 @@ pub(crate) const WORK_GRAPH_IMPORT_ARCHIVE_DIR: &str = ".work-graph-import-archi
|
||||
pub(crate) const JOURNAL_ARCHIVE_DIR: &str = ".journal-archive";
|
||||
/// Off-branch entries a session document may carry before an autosave
|
||||
/// archives the excess. Together with [`JOURNAL_RETAINED_DEAD_ENTRIES`] this
|
||||
/// keeps a compacted journal far below `MAX_CANONICAL_HISTORY_ENTRIES`.
|
||||
/// keeps a compacted journal far below `MAX_CANONICAL_HISTORY_ENTRIES`. A
|
||||
/// document whose file is already over `MAX_CANONICAL_HISTORY_BYTES` archives
|
||||
/// every off-branch entry instead.
|
||||
const JOURNAL_PRUNE_DEAD_THRESHOLD: usize = 4_096;
|
||||
/// Recent off-branch entries kept in the document after archiving, so `/tree`
|
||||
/// and `/branch` stay cheap for recent history.
|
||||
@@ -2635,10 +2637,12 @@ impl SessionManager {
|
||||
|
||||
/// The autosave path (#6842): like [`Self::save_session_owned`], but when
|
||||
/// the journal carries more than `JOURNAL_PRUNE_DEAD_THRESHOLD`
|
||||
/// off-branch entries, first append the excess to the session's journal
|
||||
/// archive (fsynced), then write the document without them. If the
|
||||
/// archive cannot be written nothing is pruned. Runs on the persistence
|
||||
/// actor (`tui/persistence_actor.rs` `flush_inner`), never the UI loop.
|
||||
/// off-branch entries, or the file being replaced is over
|
||||
/// `MAX_CANONICAL_HISTORY_BYTES` and carries any, first append the excess
|
||||
/// (all of them in the second case) to the session's journal archive
|
||||
/// (fsynced), then write the document without them. If the archive cannot
|
||||
/// be written nothing is pruned. Runs on the persistence actor
|
||||
/// (`tui/persistence_actor.rs` `flush_inner`), never the UI loop.
|
||||
pub(crate) fn save_session_bounded(&self, session: SavedSession) -> std::io::Result<PathBuf> {
|
||||
self.save_session_inner(session, JournalPrune::Excess)
|
||||
}
|
||||
@@ -2753,20 +2757,24 @@ impl SessionManager {
|
||||
let mut durable_session = session;
|
||||
let archived = match prune {
|
||||
JournalPrune::Keep => Vec::new(),
|
||||
JournalPrune::Excess => self
|
||||
.archive_dead_journal_entries(
|
||||
&mut durable_session,
|
||||
JOURNAL_PRUNE_DEAD_THRESHOLD,
|
||||
JOURNAL_RETAINED_DEAD_ENTRIES,
|
||||
)
|
||||
.unwrap_or_else(|error| {
|
||||
tracing::warn!(
|
||||
%session_id,
|
||||
%error,
|
||||
"journal archive step failed; keeping the full journal"
|
||||
);
|
||||
Vec::new()
|
||||
}),
|
||||
JournalPrune::Excess => {
|
||||
let (threshold, retained) = if fs::symlink_metadata(&path).is_ok_and(|meta| {
|
||||
meta.len() > codewhale_protocol::MAX_CANONICAL_HISTORY_BYTES as u64
|
||||
}) {
|
||||
(0, 0)
|
||||
} else {
|
||||
(JOURNAL_PRUNE_DEAD_THRESHOLD, JOURNAL_RETAINED_DEAD_ENTRIES)
|
||||
};
|
||||
self.archive_dead_journal_entries(&mut durable_session, threshold, retained)
|
||||
.unwrap_or_else(|error| {
|
||||
tracing::warn!(
|
||||
%session_id,
|
||||
%error,
|
||||
"journal archive step failed; keeping the full journal"
|
||||
);
|
||||
Vec::new()
|
||||
})
|
||||
}
|
||||
JournalPrune::AllOffBranch => {
|
||||
self.archive_dead_journal_entries(&mut durable_session, 0, 0)?
|
||||
}
|
||||
|
||||
Reference in new issue
Block a user