Robust deduplication - #1046
Merged
Merged
Conversation
|
@shantanushok is attempting to deploy a commit to the Darshan's projects Team on Vercel. A member of the Team first needs to authorize it. |
shantanushok
force-pushed
the
security/dedupe
branch
from
July 30, 2026 07:18
0fd3a92 to
cf259ee
Compare
Contributor
Author
|
I found out the error I will push it right now ( I forgot to add Prometheus client package in the dependency list ) |
adikulkarni006
previously approved these changes
Jul 30, 2026
…ow CI expiry Also optimizes response byte accumulation in csrf.py using b''.join
Contributor
Author
|
I have tried to solve the ci issues so whenever @adikulkarni006 and @imDarshanGK are available feel free to re-approve the workflows. |
- Extract compute_request_hash to use as the single source of truth for both middleware and tests. - Update est_csrf.py to use httpx.Request serialization to match the middleware byte-for-byte, fixing the sentinel mismatch in CI. - Add an �utouse fixture to est_bulk_export.py to clear the sessions table before each test, preventing global database state bleed during randomized test execution.
Contributor
Author
|
@imDarshanGK can you reapprove the only the backend workflow . |
adikulkarni006
self-requested a review
July 31, 2026 17:36
adikulkarni006
previously approved these changes
Jul 31, 2026
Contributor
Author
|
hey @imDarshanGK I have resolved the merge conflicts can you reapprove the workflows ? |
Contributor
Author
|
@imDarshanGK could you please merge this |
imDarshanGK
approved these changes
Aug 1, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.



feat(security): upgrade request deduplication from in-memory to SQLite-backed persistent cache
Description
Solves #918
The initial implementation of this issue introduced basicin-memory dictionary for request deduplication. This PR completes the feature by replacing that in-memory dict with a persistent, SQLite-backed
dedupe_cachetable,making deduplication survive server restarts and adding three new correctness guarantees:
Problem with in-memory dict : Fix in this PR
1] Reset on every server restart - a crash mid-request left a silent gap : Startup
dedupe_clear_orphaned_processing()sweeps stale sentinels2] A duplicate arriving while the first was processing was silently passed through :
'processing'sentinel + HTTP 409 Conflict during in-flight window3] A failed response (4xx/5xx) was cached like a success - client retried into a replayed error : Error responses are never persisted; sentinel cleared immediately
Changes Made
backend/middleware/csrf.pyOriginValidationMiddleware→SecurityMiddlewareto reflect its dual role.'processing'sentinel is inserted before thehandler runs; a matching request that arrives during that window receives HTTP 409 Conflict.
can retry immediately without hitting a replayed failure.
csrf_requests_total{status="deduplicated"}— served from cachecsrf_requests_total{status="deduplicated_in_progress"}— rejected as in-flight duplicateKnown Limitationssection to the class docstring.backend/services/db_service.pydedupe_cachetable toinit_db()(withidx_dedupe_expiresindex).dedupe_get/dedupe_set_processing/dedupe_set_done/dedupe_deletededupe_purge_expired— TTL cleanup feeding_maybe_vacuum()dedupe_clear_orphaned_processing— startup-time stale sentinel removalbackend/app.pydedupe_clear_orphaned_processing()in thelifespanstartup hook before theserver begins accepting traffic.
backend/tests/test_csrf.pyAdded
import hashlib,import json,import timeand_TESTCLIENT_HOSTconstant.Extended test suite from 32 → 36 tests:
| Test | What it verifies |
|
test_request_deduplication| Second identical POST within 5 s returns identical cached body ||
test_in_progress_sentinel_returns_409| Planted'processing'sentinel → 409 + Prometheus counter ||
test_orphaned_processing_rows_cleared_on_startup| Startup cleanup removes stale row; next request succeeds ||
test_error_response_not_cached_allows_immediate_retry| 422 response → sentinel deleted, immediate retry succeeds ||
test_successful_response_is_still_cached_after_error_guard| Regression: 200 responses still cached after error guard |docs/csrf-protection.mdVerification
Design Decisions
Why SQLite instead of the in-memory dict?
The in-memory dict was reset on every restart. A crash mid-request left no sentinel,
but the handler had already started — a silent idempotency gap. SQLite is already used
for all other app state in LocalMind and persists across restarts. The startup orphan
cleanup keeps the table consistent every time the server comes up.
Why are error responses not cached?
Replaying a transient error hides a real bug from the client and prevents a legitimate
retry from succeeding. Clearing the sentinel on error gives the client a clean slate
immediately.
Known limitations
INSERT OR REPLACEis last-write-wins. Two Uvicorn workersracing on the same request could overwrite each other's cached responses. Acceptable
for LocalMind's single-user, single-worker design; would need a shared cache (Redis)
to scale correctly.
Checklist
docs/csrf-protection.mdupdated