Make PUT on SQLite backend implicitly overwrite expired entries

The backend does not serve expired but still present entries so to a
user they do not exist. When they put new data over such an entry their
expecation is that the value is overwritten, even if not explicitly
requested.
This commit is contained in:
Benjamin Bannier 2025-07-01 08:14:33 +02:00 committed by Tim Wojtulewicz
parent 56f5cafa98
commit fc007b674a
3 changed files with 39 additions and 0 deletions

View file

@ -295,6 +295,33 @@ OperationResult SQLite::DoPut(ResultCallback* cb, ValPtr key, ValPtr value, bool
if ( ! key_data ) if ( ! key_data )
return {ReturnCode::SERIALIZATION_FAILED, "Failed to serialize key"}; return {ReturnCode::SERIALIZATION_FAILED, "Failed to serialize key"};
// If we are not already in overwrite mode check if an expired entry exists
// in the database. Such entries would not be visible to the user, but even
// outside of overwrite mode would need to be overwritten.
if ( ! overwrite ) {
auto stmt = unique_stmt_ptr(get_stmt.get(), sqlite3_reset);
if ( auto res = CheckError(sqlite3_bind_blob(stmt.get(), 1, key_data->data(), key_data->size(), SQLITE_STATIC));
res.code != ReturnCode::SUCCESS ) {
return res;
}
int step_status = sqlite3_step(stmt.get());
if ( step_status == SQLITE_ROW ) {
// If an expired entry exists, switch to overwrite mode.
overwrite =
sqlite3_column_type(stmt.get(), 1) != SQLITE_NULL && is_expired(sqlite3_column_double(stmt.get(), 0));
}
else if ( step_status == SQLITE_DONE ) {
// Nothing currently exists.
}
else if ( step_status == SQLITE_BUSY || step_status == SQLITE_LOCKED )
// TODO: this could retry a number of times instead of just failing
return {ReturnCode::TIMEOUT};
else
return {ReturnCode::OPERATION_FAILED};
}
unique_stmt_ptr stmt; unique_stmt_ptr stmt;
if ( ! overwrite ) if ( ! overwrite )
stmt = unique_stmt_ptr(put_stmt.get(), sqlite3_reset); stmt = unique_stmt_ptr(put_stmt.get(), sqlite3_reset);

View file

@ -1,3 +1,4 @@
### BTest baseline data generated by btest-diff. Do not edit. Use "btest -U/-u" to update. Requires BTest >= 0.63. ### BTest baseline data generated by btest-diff. Do not edit. Use "btest -U/-u" to update. Requires BTest >= 0.63.
BEFORE, [code=Storage::SUCCESS, error_str=<uninitialized>, value=v] BEFORE, [code=Storage::SUCCESS, error_str=<uninitialized>, value=v]
AFTER, [code=Storage::KEY_NOT_FOUND, error_str=<uninitialized>, value=<uninitialized>] AFTER, [code=Storage::KEY_NOT_FOUND, error_str=<uninitialized>, value=<uninitialized>]
OVERWRITE, [code=Storage::SUCCESS, error_str=<uninitialized>, value=vv]

View file

@ -47,4 +47,15 @@ event zeek_init()
# An expired value does not exist. # An expired value does not exist.
get = Storage::Sync::get(h, key); get = Storage::Sync::get(h, key);
print "AFTER", get; print "AFTER", get;
# Even though the entry still exists in the backend we can put a
# new value in its place without specifying overwrite.
Storage::Sync::put(
h,
Storage::PutArgs(
$key=key,
$value=value+value));
get = Storage::Sync::get(h, key);
print "OVERWRITE", get;
} }