Skip to content

feat: keeper trait & sqlite-backed implementation - #582

Open
aldy505 wants to merge 3 commits into
mainfrom
aldy505/feat/ttl-keeper
Open

feat: keeper trait & sqlite-backed implementation#582
aldy505 wants to merge 3 commits into
mainfrom
aldy505/feat/ttl-keeper

Conversation

@aldy505

@aldy505 aldy505 commented Aug 1, 2026

Copy link
Copy Markdown
Collaborator

Internal Slack thread.

This is required for filesystem & S3-compatible API backends. Later, I'll try to integrate this with filesystem backend, and create Postgres-backed keeper.

@aldy505
aldy505 requested a review from lcian August 1, 2026 12:19
@aldy505
aldy505 requested a review from a team as a code owner August 1, 2026 12:19
@codecov

codecov Bot commented Aug 1, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 81.81818% with 40 lines in your changes missing coverage. Please review.
✅ Project coverage is 87.54%. Comparing base (c20acef) to head (56c46f3).
⚠️ Report is 30 commits behind head on main.

Files with missing lines Patch % Lines
objectstore-service/src/keeper/sqlite_backed.rs 82.56% 38 Missing ⚠️
objectstore-service/src/error.rs 0.00% 2 Missing ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##             main     #582      +/-   ##
==========================================
+ Coverage   87.44%   87.54%   +0.10%     
==========================================
  Files          93       95       +2     
  Lines       14753    15811    +1058     
==========================================
+ Hits        12901    13842     +941     
- Misses       1852     1969     +117     
Components Coverage Δ
Rust Backend 92.07% <81.81%> (-0.12%) ⬇️
Rust Client 79.89% <ø> (ø)
Python Client 90.98% <ø> (+1.60%) ⬆️

☔ 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.

Comment thread objectstore-service/src/keeper/sqlite_backed.rs
Comment thread objectstore-service/src/keeper/sqlite_backed.rs Outdated
.read_only(true)
.disable_statement_logging(),
)
.await?;

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Read-only WAL pool setup fails

High Severity

create_sqlite_pool opens the read pool with journal_mode(Wal) and read_only(true) before the write pool can switch the database to WAL. Changing journal mode needs write access, so SqliteBackedKeeper::new is likely to fail when creating or opening a non-WAL database. In-memory tests bypass this path.

Additional Locations (1)
Fix in Cursor Fix in Web

Reviewed by Cursor Bugbot for commit 076740d. Configure here.

Comment on lines +92 to +97
let expiration_duration: Option<i64> = expiration_policy.expires_in().and_then(|x| {
x.as_secs()
.try_into()
.map_err(|_| Error::generic("expiration duration exceeds i64::MAX"))
.ok()
});

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Bug: A Duration exceeding i64::MAX is silently stored as NULL in the database, creating an inconsistent state for objects with an expiration policy.
Severity: LOW

Suggested Fix

Instead of using .ok() to discard the error, propagate the Result of the try_into() conversion. This will cause the operation to fail explicitly when an out-of-range duration is provided, preventing the insertion of records with inconsistent state. This ensures that any object with an expiration policy also has a valid expiration time stored.

Prompt for AI Agent
Review the code at the location below. A potential bug has been identified by an AI
agent. Verify if this is a real issue. If it is, propose a fix; if not, explain why it's
not valid.

Location: objectstore-service/src/keeper/sqlite_backed.rs#L92-L97

Potential issue: The conversion of an `ExpirationPolicy` duration from `u64` seconds to
an `i64` for database storage uses `.ok()`, which silently discards overflow errors. If
a `Duration` greater than `i64::MAX` (approximately 292 billion years) is provided, the
conversion fails and results in `None`. This `None` value is then persisted as `NULL`
for the `duration` and `expires_at` columns. This creates an inconsistent database state
where an object has an expiration policy (TTL/TTI) but no corresponding expiration data,
which could lead to unexpected retention behavior.

@cursor cursor Bot 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.

Cursor Bugbot has reviewed your changes and found 1 potential issue.

There are 2 total unresolved issues (including 1 from previous review).

Fix All in Cursor

❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.

Reviewed by Cursor Bugbot for commit 56c46f3. Configure here.

tk.keeper.mark_accessed(&id).await.unwrap();

let row = tk.fetch_row(&id).await.unwrap();
assert_eq!(row.expires_at, Some(now + 60));

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Flaky second-boundary expiry assertion

Low Severity

mark_accessed_tti_without_expires_at_sets_it captures now, then asserts expires_at equals exactly now + 60. mark_accessed recomputes time independently in whole seconds, so a second boundary between those calls makes the assertion fail even when behavior is correct.

Fix in Cursor Fix in Web

Reviewed by Cursor Bugbot for commit 56c46f3. Configure here.

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.

1 participant