Skip to content

SDSTOR-22888 craft: proactive checkpoint trigger + apply_sync_rs_commit_lsn block-leak/validation fixes - #181

Open
sbinmalek wants to merge 7 commits into
eBay:dev/v6.xfrom
sbinmalek:SDSTOR-22888-v2
Open

sbinmalek wants to merge 7 commits into
eBay:dev/v6.xfrom
sbinmalek:SDSTOR-22888-v2

Conversation

@sbinmalek

Copy link
Copy Markdown
Contributor

Summary

  • Proactively triggers a HomeStore checkpoint when commit_lsn advances, instead of waiting
    for HomeStore's own timer, so truncation can reclaim journal space sooner
    (craft_repl_dev.{cpp,hpp}, home_blks_config.fbs adds the trigger's tunable interval).
  • Fixes a block leak in apply_sync_rs_commit_lsn: the to_free computation for empty_slots
    had an inverted condition, so a locally-written slot that got Empty-verdicted never had its
    block freed, while genuinely-missing slots (nothing to free) were queued instead. Corrected to
    free only slots that were locally held and not already Empty-verdicted, guarding against
    double-freeing on a duplicate/overlapping verdict.
  • Dedupes to_free with a set to avoid queuing the same lsn twice.
  • Adds magic/version/lsn validation in free_slot before trusting a journal entry's all_zeros
    flag and blkid, so a corrupt or foreign record can't be misread and free the wrong storage.
  • Bounds FreeSlotRejectsCorruptEntry's completion wait so a hang fails the test instead of
    blocking indefinitely.
  • Fixes two tests in test_craft_homestore_backend.cpp left on the pre-S3
    make_homestore_journal_backend/write_slot call signatures after the dev/v6.x S3 rebase
    (missing page_size and csums args respectively).

sbinmalek and others added 6 commits September 11, 2026 15:05
Implements SDSTOR-22888. After apply_sync_rs_commit_lsn advances commit_lsn, nudge
HomeStore to checkpoint once the advance since the last trigger crosses
checkpoint_lsn_interval_. Otherwise the journal-reclaim / RAFT-log-compaction floor
(docs/craft/subtasks.md's S8) can lag arbitrarily far behind commit_lsn, unbounding
restart recovery time.

- New CraftCheckpointTrigger interface + HomeStoreCraftCheckpointTrigger production
  impl wrapping homestore::cp_mgr().trigger_cp_flush(), following the same
  inject-an-abstraction pattern as CraftJournalBackend/CraftPeerFetcher so unit tests
  (which run with no live HomeStore instance) can exercise the trigger via a mock.
  CraftReplDev takes it as a non-owning pointer, same shape as peer_fetcher_, since
  cp_mgr() is one instance shared by every volume, not owned per-CraftReplDev.
- The trigger call is detail::detach()'d (fire-and-forget), matching the existing
  free_data cleanup pattern in this same function -- nothing depends on the flush
  completing.
- force=false: let it coalesce with any checkpoint already in flight rather than
  forcing back-to-back flushes under high commit throughput.
- Left two forward-looking FIXMEs for related gaps out of this ticket's scope: seeding
  last_checkpoint_lsn_ from recovered commit_lsn once S8 restart recovery lands, and
  forcing a completed (not just requested) flush before truncate() drops journal
  entries, mirroring HomeStore's own IndexTable::destroy().
- Tests: MockCraftCheckpointTrigger covers interval gating (fires-once-crossed,
  below-interval, accumulates-across-calls, exact boundary, baseline tracks the
  actual commit_lsn reached rather than incrementing by the interval), null-trigger
  safety, and best-effort failure handling. test_craft_homestore_backend.cpp gets two
  new cases (force=false and force=true) exercising the production wrapper against a
  real cp_mgr() -- previously untested against anything but the mock.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
to_free was freeing lsns that *were* missing (nothing to free there)
instead of ones that held real local data (<= last_append_lsn, not
missing) -- exactly the leak shosseinimotlagh flagged on PR eBay#176 and
Copilot's review re-caught. Also guards against double-freeing an
lsn already verdicted Empty.

Adds a free_data_calls counter to test_craft_raft_entries.cpp's mock
and locks in all four branches of the condition.
Corrupt or foreign records could get misread as a valid blkid otherwise.
Flagged by Copilot's review on PR eBay#176. Adds real-log-store tests for
both a legitimate entry and a rejected corrupt one.
to_free was a vector, so a duplicate lsn in a single empty_slots list
would call free_slot on the same lsn twice -- a double-free. Switched
to unordered_set. Adds a test for the intra-batch duplicate case.

Found during review of PR eBay#176's changes.
write_async's return value was never checked and the wait had no timeout,
so a stopping log store or a lost completion (both documented failure
modes) would hang the whole test binary instead of failing.

Found during review of PR eBay#180's changes.

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 Changes recommended

Checkpoint wiring and coverage are incomplete, and duplicate Empty verdicts plus timeout cleanup have correctness risks.

Get a fresh assessment by requesting another Copilot review.

Pull request overview

Adds proactive CRAFT checkpoint triggering and strengthens journal block-reclamation validation.

Changes:

  • Adds checkpoint-trigger abstraction and interval gating.
  • Fixes and tests Empty-slot block reclamation.
  • Adds journal validation and HomeStore integration tests.
File summaries
File Description
src/lib/home_blks_config.fbs Documents the shared checkpoint interval.
src/lib/craft/craft_repl_dev.hpp Defines checkpoint interfaces and state.
src/lib/craft/craft_repl_dev.cpp Implements checkpoint triggering and safer block reclamation.
src/lib/craft/tests/test_craft_raft_entries.cpp Tests checkpoint and Empty-slot behavior.
src/lib/craft/tests/test_craft_homestore_backend.cpp Tests real checkpoint and journal validation paths.
Review details
  • Files reviewed: 5/5 changed files
  • Comments generated: 4
  • Review effort level: Balanced

💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

Comment thread src/lib/craft/craft_repl_dev.cpp Outdated
Comment on lines +1222 to +1224
// TODO(SDSTOR-22733): once implemented, this is the other commit_lsn-advance path SDSTOR-22888's
// checkpoint trigger needs to cover (see apply_sync_rs_commit_lsn's own hook) -- same
// checkpoint_lsn_interval_/last_checkpoint_lsn_ bookkeeping under missing_mu_, same force=false.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed

Comment on lines 1649 to 1653
for (int64_t lsn : empty_slots) {
if (missing_lsns_.erase(lsn)) { to_free.push_back(lsn); }
bool const was_missing = missing_lsns_.erase(lsn) > 0;
if (!was_missing && lsn <= state_.last_append_lsn && !empty_lsns_.contains(lsn)) { to_free.insert(lsn); }
}
empty_lsns_.insert(empty_slots.begin(), empty_slots.end());

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is less likely to happen. Dont need to make this change

Comment on lines +369 to +373
// Overrides the commit_lsn delta between checkpoint triggers (default matches
// sync_rs_commit_lsn_interval's own default of 128, tying checkpoint cadence to the periodic
// SyncRSCommitLSN cadence). Production sets this from HB_DYNAMIC_CONFIG(sync_rs_commit_lsn_interval)
// after construction, same pattern as set_peer_fetch_timeout_ms.
void set_checkpoint_lsn_interval(int64_t n) { checkpoint_lsn_interval_ = n; }

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

it's tracked as S8 work

Comment on lines +369 to +373
auto write_ret = logstore->write_async(
/* seq_num = */ 0, raw_blob, nullptr,
[&](homestore::logstore_seq_num_t, sisl::io_blob&, homestore::logdev_key, void*) {
std::lock_guard< std::mutex > lk{mu};
done = true;

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Issue is just within tests. keeping it as it is

@codecov-commenter

codecov-commenter commented Sep 11, 2026

Copy link
Copy Markdown

⚠️ Please install the 'codecov app svg image' to ensure uploads and comments are reliably processed by Codecov.

Codecov Report

❌ Patch coverage is 68.75000% with 5 lines in your changes missing coverage. Please review.
⚠️ Please upload report for BASE (dev/v6.x@1dc0ef5). Learn more about missing BASE report.

Files with missing lines Patch % Lines
src/lib/craft/craft_repl_dev.cpp 61.53% 0 Missing and 5 partials ⚠️
❗ Your organization needs to install the Codecov GitHub app to enable full functionality.
Additional details and impacted files
@@             Coverage Diff             @@
##             dev/v6.x     #181   +/-   ##
===========================================
  Coverage            ?   49.10%           
===========================================
  Files               ?       19           
  Lines               ?     1224           
  Branches            ?      534           
===========================================
  Hits                ?      601           
  Misses              ?      266           
  Partials            ?      357           

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

  The proactive checkpoint trigger only fired inside apply_sync_rs_commit_lsn,
  so write()'s piggybacked commit and keep_alive() -- the only two paths that
  advance commit_lsn on an ordinary (non-SyncRSCommitLSN) workload -- never
  triggered a checkpoint at all. Extracted the interval-check and the
  detached trigger_cp_flush call into checkpoint_interval_crossed_locked()/
  fire_checkpoint_trigger(), and call both from commit_impl()'s return path
  (covering commit()'s write()/keep_alive() callers) as well as
  apply_sync_rs_commit_lsn()'s own walk-forward loop.
@sbinmalek
sbinmalek marked this pull request as ready for review September 11, 2026 23:26
mutable std::mutex
missing_mu_; // guards state_, missing_lsns_, empty_lsns_, commit_running_, in_flight_write_dlsns_
mutable std::mutex missing_mu_; // guards state_, missing_lsns_, empty_lsns_, commit_running_,
// in_flight_write_dlsns_, and last_checkpoint_lsn_

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

need to change the name eventually. it is generic purpose and I hesitated to do it previously (my bad)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants