Skip to content

feat(keeper): integrate filesystem backend with keeper trait - #583

Open
aldy505 wants to merge 3 commits into
aldy505/feat/ttl-keeperfrom
aldy505/feat/integrate-filesystem-keeper
Open

feat(keeper): integrate filesystem backend with keeper trait#583
aldy505 wants to merge 3 commits into
aldy505/feat/ttl-keeperfrom
aldy505/feat/integrate-filesystem-keeper

Conversation

@aldy505

@aldy505 aldy505 commented Aug 1, 2026

Copy link
Copy Markdown
Collaborator

Refs FS-482

@aldy505
aldy505 requested a review from a team as a code owner August 1, 2026 13:07
keeper: KeeperConfig {
backend: KeeperBackend::Sqlite,
connection_url: "sqlite:///opt/objectstore/keeper.db".into(),
},

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Broken default keeper database path

High Severity

The default connection_url points at /opt/objectstore/keeper.db, a path that does not exist elsewhere in this project. Local runs, Docker (/data), README env setup, and e2e tests all rely on this default when keeper is unset, so filesystem backend startup will fail creating the SQLite database.

Fix in Cursor Fix in Web

Reviewed by Cursor Bugbot for commit 499ea21. Configure here.

_ => Err(Error::generic(format!("unknown backend {}", s))),
}
}
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Keeper backend config case mismatch

Medium Severity

KeeperBackend deserializes as Sqlite, but the filesystem config docs and FromStr accept sqlite. Config written as documented (or via env) will fail to parse. Other config enums in this repo use rename_all = "lowercase", and the new FromStr impl is never wired into serde.

Additional Locations (1)
Fix in Cursor Fix in Web

Reviewed by Cursor Bugbot for commit 499ea21. Configure here.

{
objectstore_log::debug!("Object not found");
}
self.keeper.remove(id).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.

Keeper removed on failed deletes

Medium Severity

delete_object always calls keeper.remove before returning the filesystem delete result. If remove_file fails for a reason other than not found, the keeper entry is dropped while the object file remains, so TTL/TTI tracking is permanently lost for that object.

Fix in Cursor Fix in Web

Reviewed by Cursor Bugbot for commit 499ea21. Configure here.

Comment thread objectstore-service/src/backend/local_fs.rs
Comment on lines +136 to +137
self.keeper.keep(id, metadata.expiration_policy).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.

Bug: Overwriting an object with a TTL/TTI policy fails due to a database UNIQUE constraint violation, causing data inconsistency despite the file being successfully updated.
Severity: HIGH

Suggested Fix

Modify the keeper.keep() method to handle cases where an object ID already exists. Instead of a simple INSERT, use an INSERT OR REPLACE or an equivalent UPSERT statement. This will ensure that overwriting an object also correctly updates its expiration policy in the keeper database without causing a constraint violation.

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/backend/local_fs.rs#L136-L137

Potential issue: When an object with a non-manual expiration policy (e.g., TTL or TTI)
is overwritten, the file is successfully updated on the filesystem, but the overall
operation fails. This occurs because the `keeper.keep()` method attempts to `INSERT` a
new record for the object's expiration into the SQLite database. Since a record for that
`object_id` already exists, this action violates the `PRIMARY KEY` constraint. The
resulting SQL error is propagated, causing the `put_object` request to fail, leading to
an inconsistency between the file storage and the expiration keeper database.

Also affects:

  • objectstore-service/src/backend/local_fs.rs:457~458

file.sync_data().await?;
drop(file);

self.keeper.keep(id, metadata.expiration_policy).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.

Overwrite breaks keeper retention tracking

High Severity

put_object and complete_multipart always call keeper.keep, but keep only inserts and rejects duplicates. Overwriting an object with a client-supplied key fails after the file is already replaced, or silently leaves a stale retention row when the new policy is Manual.

Additional Locations (1)
Fix in Cursor Fix in Web

Reviewed by Cursor Bugbot for commit eba4ba5. Configure here.

Comment thread objectstore-service/src/backend/local_fs.rs
@linear-code

linear-code Bot commented Aug 6, 2026

Copy link
Copy Markdown

FS-482

FS-482

@aldy505
aldy505 force-pushed the aldy505/feat/integrate-filesystem-keeper branch from bad7565 to 5bebaee Compare August 13, 2026 01:18

@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 2 potential issues.

There are 6 total unresolved issues (including 4 from previous reviews).

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 5bebaee. Configure here.

}
err => err?,
};
self.keeper.mark_accessed(id).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.

GET does not refresh TTI

High Severity

get_object calls keeper.mark_accessed before metadata is read. That method is not on Keeper. Time-to-idle refresh is implemented as update, which needs the expiration policy. Access therefore cannot extend TTI, so idle objects expire on the original deadline even when they are read.

Fix in Cursor Fix in Web

Reviewed by Cursor Bugbot for commit 5bebaee. Configure here.


/// Configuration for the keeper backend.
/// See [`KeeperConfig`] for details.
pub keeper: KeeperConfig,

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Keeper config has no default

Medium Severity

FileSystemConfig.keeper is required and KeeperConfig has no Default or serde(default). Top-level filesystem can inherit keeper from Config::default(), but nested filesystem (for example tiered long_term via env with only type and path) cannot. Those configs fail to deserialize after this change.

Additional Locations (1)
Fix in Cursor Fix in Web

Reviewed by Cursor Bugbot for commit 5bebaee. 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