Skip to content

feat: add durable SQLite-backed job queue for desktop mode #257#291

Open
rajesh-puripanda wants to merge 6 commits into
Abhash-Chakraborty:mainfrom
rajesh-puripanda:feat/sqlite-job-queue-desktop
Open

feat: add durable SQLite-backed job queue for desktop mode #257#291
rajesh-puripanda wants to merge 6 commits into
Abhash-Chakraborty:mainfrom
rajesh-puripanda:feat/sqlite-job-queue-desktop

Conversation

@rajesh-puripanda
Copy link
Copy Markdown

@rajesh-puripanda rajesh-puripanda commented May 29, 2026

Summary

Add a durable SQLite-backed job queue for desktop mode, removing the Redis dependency for local-first deployments while preserving Redis/RQ for Docker/server deployments.

Fixes #257

Type of change

  • Bug fix
  • Feature
  • Documentation update
  • Refactor
  • CI / tooling

What changed

  • Created backend/src/find_api/core/queue_sqlite.py — full SQLite queue implementation with SQLiteJob/SQLiteQueue classes, WAL mode concurrency, thread-local get_current_job(), job persistence across restarts, and SQLite-backed clustering coalescing logic
  • Created backend/src/find_api/workers/sqlite_worker.py — polling worker that dequeues and executes jobs from the SQLite queue with graceful shutdown support
  • Added QUEUE_BACKEND ("redis" / "sqlite", default "redis") and SQLITE_QUEUE_PATH settings to backend/src/find_api/core/config.py
  • Refactored backend/src/find_api/core/queue.py into a unified backend-agnostic interface — factory functions delegate to Redis/RQ or SQLite based on settings.QUEUE_BACKEND
  • Updated backend/src/find_api/core/recovery.py – job status lookup works with both backends
  • Updated backend/src/find_api/routers/status.py – job status endpoint supports SQLite backends
  • Updated backend/src/find_api/workers/jobs.py – uses unified get_current_job from core/queue
  • Added backend/tests/test_sqlite_queue.py — 49 tests covering schema, enqueue, dequeue, completion, failure, metadata persistence, restart survival, clustering coalescing, worker execution, and edge cases

Screenshots / recordings (for UI changes)

N/A — backend change with no UI impact.

How to test

# Run SQLite queue tests
cd backend
pytest tests/test_sqlite_queue.py -v

# Run linting
ruff check src/ tests/
ruff format --check src/ tests/

# Activate SQLite mode by setting:
# QUEUE_BACKEND=sqlite in .env

# Default Redis/RQ mode unchanged — no existing deployments affected


## Checklist

- [x] I linked the related issue
- [x] I ran required checks from CONTRIBUTING.md
- [ ] I updated docs/env notes if needed
- [x] My PR is scoped to a single issue
- [x] I followed commit message conventions
- [x] I am not committing secrets or local artifacts

## GSSoC'26 checklist

- [x] I requested issue assignment before starting
- [x] I have meaningful commits (no spam commits)
- [x] I am ready to explain my implementation in review comments


<!-- This is an auto-generated comment: release notes by coderabbit.ai -->
## Summary by CodeRabbit

* **New Features**
  * Added SQLite as an alternative durable job queue backend and a local/background worker mode.
  * Job queue backend is now configurable (switch between Redis and SQLite).
  * Job status endpoint and recovery now report and handle jobs from either backend consistently.

* **Tests**
  * Added comprehensive test suite covering SQLite queue behavior, worker execution, lifecycle, and status integration.

<!-- review_stack_entry_start -->

[![Review Change Stack](https://storage.googleapis.com/coderabbit_public_assets/review-stack-in-coderabbit-ui.svg)](https://app.coderabbit.ai/change-stack/Abhash-Chakraborty/Find/pull/291?utm_source=github_walkthrough&utm_medium=github&utm_campaign=change_stack)

<!-- review_stack_entry_end -->
<!-- end of auto-generated comment: release notes by coderabbit.ai -->

@github-actions
Copy link
Copy Markdown

github-actions Bot commented May 29, 2026

PR Context Summary

Suggested issue links

  • No strong issue match found yet.

Use Fixes #123 or Closes #123 in the PR body when one of the suggestions is the intended issue.
Manual rerun: Actions > PR Context Triage > Run workflow > set pr_number and force_review=true.

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented May 29, 2026

Warning

Review limit reached

@rajesh-puripanda, we couldn't start this review because you've reached your PR review rate limit.

More reviews will be available in 15 minutes and 58 seconds. Learn how PR review limits work.

Your organization has run out of usage credits. Purchase more in the billing tab.

⌛ How to resolve this issue?

After more reviews become available, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

We recommend that you space out your commits to avoid hitting the rate limit.

🚦 How do rate limits work?

CodeRabbit enforces hourly rate limits for each developer per organization.

Our paid plans include higher PR review limits than trial, open-source, and free plans. In all cases, reviews become available again over time. During sustained high-volume PR review activity, CodeRabbit may temporarily slow when the next review becomes available.

Please see our Fair Usage Limits Policy for further information.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 28aba78d-f21a-40e6-8e42-df1115b631c7

📥 Commits

Reviewing files that changed from the base of the PR and between 0aa7d5b and 49645bb.

📒 Files selected for processing (1)
  • backend/src/find_api/core/recovery.py
📝 Walkthrough

Walkthrough

Adds a configurable queue backend (redis|sqlite), implements a durable SQLite-backed job queue (schema, SQLiteJob/SQLiteQueue), makes queue APIs backend-agnostic, adds clustering coalescing for SQLite, integrates status/recovery paths, provides a SQLite polling worker, and includes comprehensive tests.

Changes

SQLite-backed job queue for desktop mode

Layer / File(s) Summary
Queue backend configuration
backend/src/find_api/core/config.py
Settings introduce QUEUE_BACKEND choice ("redis"/"sqlite") and SQLITE_QUEUE_PATH for SQLite persistence.
Queue API abstraction and backend selection
backend/src/find_api/core/queue.py
Module docs and backend helpers added (get_queue_backend, is_sqlite_backend); get_task_queue() and get_current_job() dispatch to Redis/RQ or SQLite; clustering entry points become backend-aware.
SQLite queue core storage
backend/src/find_api/core/queue_sqlite.py
New module with SCHEMA_SQL, thread-local current-job tracking, connection helpers, function-resolution, SQLiteJob and SQLiteQueue implementing enqueue/dequeue/fetch/query with JSON serialization and indexes.
Job lifecycle and clustering in SQLite
backend/src/find_api/core/queue_sqlite.py
complete_job/fail_job and metadata setters persist lifecycle state; clustering coalescing (enqueue_clustering_job_sqlite, lock/ref rows, stale-state clearing) and get_job_status implemented.
Integration with recovery and status APIs
backend/src/find_api/core/recovery.py, backend/src/find_api/routers/status.py
Recovery and status endpoints made backend-agnostic; _get_job_status and /status/{job_id} branch to SQLite helpers when configured; imports adjusted in worker modules to use backend-agnostic get_current_job.
SQLite worker implementation
backend/src/find_api/workers/sqlite_worker.py, backend/src/find_api/workers/jobs.py
Polling worker added: resolves module:func, executes jobs, records completion/failure, supports daemon and blocking modes, and manages thread-local current-job.
Comprehensive test suite
backend/tests/test_sqlite_queue.py
Extensive tests covering schema/indexes, enqueue/dequeue semantics, lifecycle, metadata, queries, cleanup, restart persistence, clustering coalescing, worker execution, status endpoint, and edge cases.

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

Poem

🐰 I hopped through rows and indexed night,

Jobs now sleep safe in SQLite's light,
No Redis thump to start the day,
Restart, resume — they still can play,
A tiny queue that hums just right.

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Title check ✅ Passed The pull request title clearly and specifically describes the main change: adding a durable SQLite-backed job queue for desktop mode, with issue reference #257.
Description check ✅ Passed The PR description is comprehensive and follows the template structure with Summary, Type of change (Feature selected), What changed (detailed bullet points), How to test (commands provided), and all Checklists completed. All critical sections are present and well-documented.
Linked Issues check ✅ Passed The PR successfully implements all acceptance criteria from issue #257: SQLite queue enqueue/dequeue for analysis and clustering jobs, job persistence across restarts, error state preservation, Redis/RQ unchanged as default, and comprehensive tests (49+ covering all required scenarios).
Out of Scope Changes check ✅ Passed All changes are scoped to issue #257 requirements. Core additions (queue_sqlite.py, sqlite_worker.py, config changes) and integration updates (recovery.py, status.py, jobs.py) directly support the SQLite queue backend. Tests are appropriately comprehensive. No unrelated refactoring or feature scope creep detected.
Docstring Coverage ✅ Passed Docstring coverage is 83.20% which is sufficient. The required threshold is 80.00%.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.


Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Comment thread backend/src/find_api/core/queue_sqlite.py Outdated
Comment thread backend/src/find_api/core/queue_sqlite.py
Comment thread backend/src/find_api/routers/status.py
Comment thread backend/src/find_api/core/recovery.py Outdated
Comment thread backend/src/find_api/core/queue.py
@macroscopeapp
Copy link
Copy Markdown
Contributor

macroscopeapp Bot commented May 29, 2026

Approvability

Verdict: Needs human review

Unable to check for correctness in 8838ec2. This PR introduces a substantial new feature - a complete SQLite-backed job queue system with new database schema, worker implementation, and cross-cutting changes to existing queue infrastructure. New features of this scope warrant human review.

You can customize Macroscope's approvability policy. Learn more.

…y dirname, redis_conn passthrough, clustering race condition
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 6

🧹 Nitpick comments (3)
backend/tests/test_sqlite_queue.py (3)

51-67: ⚡ Quick win

Global settings.SQLITE_QUEUE_PATH mutation isn't restored in tests that skip the conn fixture.

The conn fixture correctly saves and restores qs.settings.SQLITE_QUEUE_PATH, but several tests use only the db_path fixture and reassign the module-level setting directly without restoring it (e.g. Lines 416, 427, 438, 511, 529, 546, 559, 571, 592). Because db_path deletes its temp file in teardown, the leaked path points at a now-deleted database. Tests that rely on the ambient global setting and don't set their own — test_no_current_job_by_default (Line 475) and test_get_status_nonexistent (Line 598) — then become order-dependent and may fail or pass depending on execution order.

Consider centralizing the patch with monkeypatch (auto-reverted) so every test gets isolated, restored state:

♻️ Example fixture using monkeypatch
`@pytest.fixture`()
def set_db_path(db_path, monkeypatch):
    import find_api.core.queue_sqlite as qs
    monkeypatch.setattr(qs.settings, "SQLITE_QUEUE_PATH", db_path)
    return db_path

Then depend on set_db_path instead of manually assigning qs.settings.SQLITE_QUEUE_PATH = db_path.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@backend/tests/test_sqlite_queue.py` around lines 51 - 67, The suite leaks a
mutated module-level setting qs.settings.SQLITE_QUEUE_PATH when tests set it
directly and don't restore it; create a dedicated fixture (e.g., set_db_path)
that depends on db_path and monkeypatch and uses
monkeypatch.setattr(qs.settings, "SQLITE_QUEUE_PATH", db_path) to automatically
revert after each test, then update tests that currently assign
qs.settings.SQLITE_QUEUE_PATH = db_path to instead depend on the new set_db_path
fixture (or use monkeypatch in those tests) and remove manual restores so no
test leaves a stale path behind.

213-222: ⚡ Quick win

Test doesn't validate queue-name isolation it claims to.

The docstring states dequeue "only returns jobs from its own queue," but the assertions only check that d1 and d2 are non-None. If isolation were broken (e.g. q1.dequeue() returned q2's job), this test would still pass. Assert the returned ids to actually exercise isolation.

💚 Strengthen assertions
         d1 = q1.dequeue()
         d2 = q2.dequeue()
-        assert d1 is not None
-        assert d2 is not None
+        assert d1 is not None
+        assert d2 is not None
+        assert d1.args == (1,)
+        assert d2.args == (2,)
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@backend/tests/test_sqlite_queue.py` around lines 213 - 222, The
test_dequeue_respects_queue_name currently only checks q1.dequeue() and
q2.dequeue() are non-None, which doesn't verify queue isolation; update the test
to assert the actual job identities returned by SQLiteQueue.dequeue() match the
jobs enqueued via SQLiteQueue.enqueue() (e.g., capture return values or job ids
when calling q1.enqueue(_dummy_success, 1) and q2.enqueue(_dummy_success, 2) and
assert that q1.dequeue() returns the id/job from q1 and q2.dequeue() returns the
id/job from q2), referencing SQLiteQueue, enqueue, dequeue, and _dummy_success
to locate the code.

367-368: 💤 Low value

Avoid __import__("sqlite3").Row; import the module directly.

__import__("sqlite3").Row (also at Lines 61, 384, 399) is an awkward idiom. connect is already imported from sqlite3; adding import sqlite3 and using sqlite3.Row is clearer.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@backend/tests/test_sqlite_queue.py` around lines 367 - 368, The tests use the
awkward __import__("sqlite3").Row for setting row_factory (seen where
new_conn.row_factory is assigned and at the other occurrences), so add a direct
import for sqlite3 at the top of the test module and replace all
__import__("sqlite3").Row references with sqlite3.Row; keep the existing use of
connect (from sqlite3) unchanged and only change the row_factory assignments to
use sqlite3.Row.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@backend/src/find_api/core/queue_sqlite.py`:
- Around line 588-626: The SELECT then INSERT around CLUSTERING_LOCK_KEY is
vulnerable to a TOCTOU race that can cause an unhandled IntegrityError; update
the logic in the block that uses conn, CLUSTERING_LOCK_KEY and inserts into
job_queue (the code that creates SQLiteQueue("default"), enqueues cluster_images
and inserts the lock/ref rows) to perform the check+insert atomically—either
wrap the check+insert in a transaction started with "BEGIN IMMEDIATE" (or
equivalent connection.begin()) so the insert cannot race, or insert the lock row
first using an atomic insert-with-conflict-handling (e.g. INSERT ... ON CONFLICT
DO NOTHING) and bail if the insert reports the row already exists, then proceed
to enqueue and insert CLUSTERING_JOB_ID_KEY; ensure IntegrityError is
caught/handled if it still can occur.
- Around line 361-365: SQLiteQueue.dequeue currently accepts a timeout parameter
but ignores it, causing callers that expect blocking semantics to spin; either
remove the unused timeout parameter from the method signature or implement
timeout-backed waiting. To fix, update the SQLiteQueue.dequeue function to honor
timeout by repeatedly attempting to claim a job with a short sleep until timeout
expires (or use a blocking condition/notification if available), or remove the
timeout argument and update all callers; add a focused unit test for
SQLiteQueue.dequeue that verifies blocking behavior when timeout is provided
(and a complementary test when timeout is None/zero). Ensure references to
SQLiteQueue.dequeue in consumers (e.g., the SQLite worker poll loop) are updated
accordingly.
- Around line 384-394: The dequeue() loop currently checks conn.total_changes
which is cumulative across the connection and can falsely indicate a successful
claim; change the UPDATE call to capture its Cursor (e.g., cursor =
conn.execute(...)) and use cursor.rowcount to determine if the per-UPDATE
affected rows > 0, then commit and only SELECT/return
SQLiteJob.from_row(updated) when cursor.rowcount > 0; replace uses of
conn.total_changes with the Cursor.rowcount check while keeping the same UPDATE
SQL and commit/SELECT flow.

In `@backend/src/find_api/routers/status.py`:
- Around line 106-110: The sqlite path in the status handler uses row.get(...)
which raises AttributeError for sqlite3.Row; in the failed-branch (where code
checks row["status"] == "failed") replace row.get("error", ...) with a safe
lookup using row["error"] if present (e.g., row["error"] if "error" in
row.keys() else fallback) and set status_info["error"] = meta.get("error",
row_error_fallback) and status_info["stage"] = meta.get("stage", "failed");
avoid catching all Exceptions around this logic so sqlite attribute errors
aren’t converted into a 404 (narrow the try/except to only expected errors or
remove it); add a focused unit/integration test that exercises the sqlite
"failed" path to assert the endpoint returns the failed status with correct
error text; and normalize response field names/types by mapping sqlite's
completed_at to the canonical ended_at (or vice versa) so clients receive
consistent fields across backends.
- Around line 85-104: The SQLite branch builds status_info from row[...] but
returns timestamps and result in a different shape than the Redis/RQ backend;
update the code that constructs status_info (the block that reads
row["created_at"], row["started_at"], row["completed_at"], row["result"] and the
meta parsing) to: 1) convert created_at and started_at (if present) from the
REAL float to ISO strings via datetime.fromtimestamp(...).isoformat(), 2) derive
ended_at (not completed_at) as an ISO string from row["completed_at"] when
present and omit/combine the completed_at key to match Redis naming, and 3) if
row["result"] is a JSON string deserialize it with json.loads before assigning
status_info["result"] so the frontend receives an object (only keep raw value if
already non-string). Keep the existing meta->stage logic. Also add a focused
test asserting the /api/status/{job_id} payload shape
(created_at/started_at/ended_at as ISO strings and result as deserialized
object) for both SQLite and Redis/RQ backends.

In `@backend/src/find_api/workers/sqlite_worker.py`:
- Around line 98-108: Daemon loop currently always sleeps after each iteration
which limits throughput; change the loop so it only sleeps when there was no job
processed. Modify the loop around run_worker_once(queue) to detect whether a job
was handled (either by having run_worker_once return a boolean indicating
work_done, or by checking queue.empty() after calling it) and only call
time.sleep(POLL_INTERVAL_SECONDS) when no work was processed; preserve the
shutdown_event check and existing exception handling (logger.exception) so
behavior on shutdown/errors is unchanged.

---

Nitpick comments:
In `@backend/tests/test_sqlite_queue.py`:
- Around line 51-67: The suite leaks a mutated module-level setting
qs.settings.SQLITE_QUEUE_PATH when tests set it directly and don't restore it;
create a dedicated fixture (e.g., set_db_path) that depends on db_path and
monkeypatch and uses monkeypatch.setattr(qs.settings, "SQLITE_QUEUE_PATH",
db_path) to automatically revert after each test, then update tests that
currently assign qs.settings.SQLITE_QUEUE_PATH = db_path to instead depend on
the new set_db_path fixture (or use monkeypatch in those tests) and remove
manual restores so no test leaves a stale path behind.
- Around line 213-222: The test_dequeue_respects_queue_name currently only
checks q1.dequeue() and q2.dequeue() are non-None, which doesn't verify queue
isolation; update the test to assert the actual job identities returned by
SQLiteQueue.dequeue() match the jobs enqueued via SQLiteQueue.enqueue() (e.g.,
capture return values or job ids when calling q1.enqueue(_dummy_success, 1) and
q2.enqueue(_dummy_success, 2) and assert that q1.dequeue() returns the id/job
from q1 and q2.dequeue() returns the id/job from q2), referencing SQLiteQueue,
enqueue, dequeue, and _dummy_success to locate the code.
- Around line 367-368: The tests use the awkward __import__("sqlite3").Row for
setting row_factory (seen where new_conn.row_factory is assigned and at the
other occurrences), so add a direct import for sqlite3 at the top of the test
module and replace all __import__("sqlite3").Row references with sqlite3.Row;
keep the existing use of connect (from sqlite3) unchanged and only change the
row_factory assignments to use sqlite3.Row.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: aa27814d-ada4-43b0-925a-d9785658d4a5

📥 Commits

Reviewing files that changed from the base of the PR and between 45383c6 and d171dba.

📒 Files selected for processing (8)
  • backend/src/find_api/core/config.py
  • backend/src/find_api/core/queue.py
  • backend/src/find_api/core/queue_sqlite.py
  • backend/src/find_api/core/recovery.py
  • backend/src/find_api/routers/status.py
  • backend/src/find_api/workers/jobs.py
  • backend/src/find_api/workers/sqlite_worker.py
  • backend/tests/test_sqlite_queue.py

Comment thread backend/src/find_api/core/queue_sqlite.py Outdated
Comment thread backend/src/find_api/core/queue_sqlite.py Outdated
Comment thread backend/src/find_api/core/queue_sqlite.py Outdated
Comment thread backend/src/find_api/routers/status.py Outdated
Comment thread backend/src/find_api/routers/status.py
Comment thread backend/src/find_api/workers/sqlite_worker.py Outdated
@Abhash-Chakraborty Abhash-Chakraborty added gssoc26 Related to GirlScript Summer of Code 2026. level:advanced GSSoC difficulty level: advanced. Base contributor points: 55. backend FastAPI, database, storage, and API work infra Docker, compose, deployment, and runtime setup architecture High-level design decisions and technical direction desktop-app Windows, macOS, and Linux installed app work local-first Privacy-preserving local runtime and offline behavior type:feature Feature PR. GSSoC type bonus. under-review Maintainer needs to verify do-not-merge Blocks merging. Remove this label only when the PR is fully ready for production. labels May 29, 2026
- TOCTOU race in enqueue_clustering_job_sqlite: atomic lock
  acquisition with INSERT ... ON CONFLICT DO NOTHING
- Remove unused timeout param from dequeue()
- status.py SQLite branch: ISO timestamps, ended_at,
  deserialize JSON result, narrow exception chaining
- sqlite_worker.py: only sleep when no job was processed
- test improvements: sqlite3.Row import, set_db_path fixture
  with monkeypatch, queue isolation assertions, status payload
  shape tests
@rajesh-puripanda
Copy link
Copy Markdown
Author

Summary

Implements a SQLite-backed job queue for desktop mode behind a QUEUE_BACKEND setting, closing #257.

Changes

  • core/queue_sqlite.pySQLiteQueue and SQLiteJob classes with WAL mode, JSON serialization, clustering coalescing, cleanup
  • workers/sqlite_worker.py — polling worker with graceful shutdown, execute_job, run_worker_once, run_worker_blocking
  • core/queue.py — backend-agnostic factory functions (get_task_queue, get_current_job, enqueue_clustering_job)
  • routers/status.py — SQLite branch for job status with ISO timestamps, ended_at, deserialized results
  • core/config.pyQUEUE_BACKEND (default "redis") and SQLITE_QUEUE_PATH settings
  • core/recovery.pyredis_conn passthrough for backend-agnostic status lookup
  • tests/test_sqlite_queue.py — 52 tests covering enqueue, dequeue, completion, failure, persistence, clustering, worker execution, and status payload shape

Testing

  • pytest tests/test_sqlite_queue.py — 52/52 pass
  • ruff check . — clean
  • ruff format --check src/ tests/ — clean (3 pre-existing files excluded)
  • Manual: verified on QUEUE_BACKEND=sqlite with upload → gallery → search flow

Review Fixes (commit e66fbab)

  • TOCTOU race → atomic INSERT ... ON CONFLICT DO NOTHING
  • Removed unused timeout param from dequeue()
  • ISO timestamps, ended_at key, JSON result deserialization in status endpoint
  • Worker loop only sleeps when idle
  • Test quality improvements (fixtures, stronger assertions, status shape tests)

@rajesh-puripanda
Copy link
Copy Markdown
Author

@Reviewer This PR is ready for re-review. All Macroscope findings from the previous round have been addressed — see commit e66fbab for details. All 52 tests pass, lint and format are clean.

Comment thread backend/src/find_api/routers/status.py
Comment thread backend/src/find_api/routers/status.py
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
backend/src/find_api/core/recovery.py (1)

98-103: ⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Handle SQLite status lookup failures the same way as Redis.

Line 99–103 returns get_job_status(job_id) without a guard, but the Redis path (Line 104–108) intentionally degrades to None on lookup errors. A SQLite exception here can abort reconciliation for the whole batch instead of treating that job as unknown.

Suggested fix
     backend = get_queue_backend()
     if backend == "sqlite":
         from find_api.core.queue_sqlite import get_job_status

-        return get_job_status(job_id)
+        try:
+            return get_job_status(job_id)
+        except Exception:  # noqa: BLE001
+            return None
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@backend/src/find_api/core/recovery.py` around lines 98 - 103, The SQLite
branch currently returns get_job_status(job_id) directly and can raise
exceptions that abort batch reconciliation; change it to mirror the Redis path
by wrapping the call to get_job_status(job_id) (after importing from
find_api.core.queue_sqlite) in a try/except that catches exceptions and returns
None on error, so get_queue_backend() == "sqlite" degrades to unknown status
instead of propagating the exception.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Outside diff comments:
In `@backend/src/find_api/core/recovery.py`:
- Around line 98-103: The SQLite branch currently returns get_job_status(job_id)
directly and can raise exceptions that abort batch reconciliation; change it to
mirror the Redis path by wrapping the call to get_job_status(job_id) (after
importing from find_api.core.queue_sqlite) in a try/except that catches
exceptions and returns None on error, so get_queue_backend() == "sqlite"
degrades to unknown status instead of propagating the exception.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 837a6e17-83ca-45df-bb1b-e8a364fc7174

📥 Commits

Reviewing files that changed from the base of the PR and between d171dba and 0aa7d5b.

📒 Files selected for processing (8)
  • backend/src/find_api/core/config.py
  • backend/src/find_api/core/queue.py
  • backend/src/find_api/core/queue_sqlite.py
  • backend/src/find_api/core/recovery.py
  • backend/src/find_api/routers/status.py
  • backend/src/find_api/workers/jobs.py
  • backend/src/find_api/workers/sqlite_worker.py
  • backend/tests/test_sqlite_queue.py
🚧 Files skipped from review as they are similar to previous changes (7)
  • backend/src/find_api/workers/jobs.py
  • backend/src/find_api/core/config.py
  • backend/src/find_api/routers/status.py
  • backend/src/find_api/workers/sqlite_worker.py
  • backend/src/find_api/core/queue_sqlite.py
  • backend/src/find_api/core/queue.py
  • backend/tests/test_sqlite_queue.py

@rajesh-puripanda
Copy link
Copy Markdown
Author

All CodeRabbit findings have been addressed — including the SQLite try/except guard in recovery.py. Could you please remove the do-not-merge label and merge this PR? All 52 tests pass, lint and format are clean.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

architecture High-level design decisions and technical direction backend FastAPI, database, storage, and API work desktop-app Windows, macOS, and Linux installed app work do-not-merge Blocks merging. Remove this label only when the PR is fully ready for production. gssoc26 Related to GirlScript Summer of Code 2026. infra Docker, compose, deployment, and runtime setup level:advanced GSSoC difficulty level: advanced. Base contributor points: 55. local-first Privacy-preserving local runtime and offline behavior type:feature Feature PR. GSSoC type bonus. under-review Maintainer needs to verify

Projects

None yet

Development

Successfully merging this pull request may close these issues.

feat: add durable SQLite-backed job queue for desktop mode

2 participants