Skip to content

refactor(api): make WebhookStore async-safe with tokio RwLock (closes #2)#6256

Merged
houko merged 2 commits into
librefang:mainfrom
maoxin1234:fix-webhook-store-async-lock
Jun 21, 2026
Merged

refactor(api): make WebhookStore async-safe with tokio RwLock (closes #2)#6256
houko merged 2 commits into
librefang:mainfrom
maoxin1234:fix-webhook-store-async-lock

Conversation

@maoxin1234

Copy link
Copy Markdown
Contributor

Problem

WebhookStore guarded its in-memory state with std::sync::RwLock and exposed synchronous list / get / create / update / delete methods that are called directly from async axum handlers.
A blocking lock acquired on a runtime worker thread can stall the executor under contention, and the synchronous std::fs persistence on the write path compounds it by blocking the thread during disk I/O.

Fixes #2.

Change

  • webhook_store.rs: replace std::sync::RwLock with tokio::sync::RwLock; list / get / create / update / delete are now async.
  • persist runs the atomic file write on tokio::task::spawn_blocking so disk I/O never blocks the async runtime.
  • load stays synchronous on purpose — it is called once at daemon boot / test setup, off the async request path — with a doc-comment explaining why.
  • routes/webhooks.rs: handlers .await the store calls. In get / create / update / delete, the !Send ErrorTranslator was being held across the new await point (breaking the axum Handler bound); resolve the translated messages up front and drop the translator before the await, matching the existing pattern in test_webhook (per CLAUDE.md).
  • Webhook-store unit tests converted to #[tokio::test] and .await the async methods.
  • tests/api_deny_unknown_fields_test.rs: .await the list() call.

Verification

  • cargo fmt -p librefang-api
  • cargo check -p librefang-api --all-targets — clean
  • cargo clippy -p librefang-api --all-targets -- -D warnings — clean
  • cargo test -p librefang-api --lib webhook_store — 38 passed
  • cargo test -p librefang-api --test api_deny_unknown_fields_test — 2 passed

The webhook store guarded its in-memory state with `std::sync::RwLock`
and exposed synchronous `list`/`get`/`create`/`update`/`delete` methods
called directly from async axum handlers. Holding a blocking lock (and
running synchronous `std::fs` persistence) on the runtime threads can
stall the executor under contention.

Switch the guard to `tokio::sync::RwLock` and make the accessor methods
`async`. Persistence now runs the file I/O on `spawn_blocking` so the
atomic write never blocks the async runtime. `load` stays synchronous:
it runs once at daemon boot / test setup, off the request path.

Handlers are updated to `.await` the store calls. Where a handler held
the `!Send` `ErrorTranslator` across the new await point, the translated
messages are resolved up front and the translator dropped before the
await (per CLAUDE.md), keeping the axum `Handler` bound satisfied.

Closes librefang#2
@github-actions github-actions Bot added the size/L 250-999 lines changed label Jun 20, 2026

@houko houko left a comment

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.

LGTM. Reviewed: the std→tokio RwLock migration is correct and complete — all WebhookStore methods are now async, every one of the 6 call sites was updated, the !Send ErrorTranslator is resolved-and-dropped before each .await, disk I/O moved to spawn_blocking, and there's no deadlock or lock-ordering hazard. Dropping the unwrap_or_else(|e| e.into_inner()) poison recovery is correct since tokio::sync::RwLock doesn't poison. Real CI — Quality/clippy, all 4 Ubuntu test shards, Unit, Build, Security, Live Integration Smoke — is green.

The red detect-secrets scan check is a stale-base artifact: this branch predates #6262, which retired detect-secrets in favor of gitleaks, so the legacy baseline gate fails here. It is not a real secret. Clean to merge onto current (gitleaks) main.

@houko houko left a comment

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.

LGTM — reviewed clean (tokio RwLock migration, all call sites updated, no deadlock/!Send-across-await); real Rust CI green; branch updated to current main.

@houko
houko enabled auto-merge (squash) June 21, 2026 15:56
@houko
houko merged commit ed16b20 into librefang:main Jun 21, 2026
31 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

size/L 250-999 lines changed

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants