Skip to content

fix(worker): complete successful operations - #2608

Merged
benfrank241 merged 1 commit into
vectorize-io:mainfrom
r266-tech:fix/worker-mark-success-completed-2601
Jul 8, 2026
Merged

fix(worker): complete successful operations#2608
benfrank241 merged 1 commit into
vectorize-io:mainfrom
r266-tech:fix/worker-mark-success-completed-2601

Conversation

@r266-tech

Copy link
Copy Markdown
Contributor

Summary

  • mark a worker task completed after the executor returns normally
  • make _mark_completed() status-safe so it only updates rows still in processing
  • run the existing parent aggregation only when the poller actually completed a child row
  • add focused unit coverage for the success path and the status guard

Fixes #2601.

Notes

The completion update is intentionally guarded with status = 'processing'. If the executor already wrote a terminal state, for example a deterministic failure handled inside MemoryEngine.execute_task, the poller will not overwrite that row as completed.

Testing

  • python3 -m py_compile hindsight-api-slim/hindsight_api/worker/poller.py hindsight-api-slim/tests/test_worker.py
  • uvx ruff@0.14.9 format --check hindsight-api-slim/hindsight_api/worker/poller.py hindsight-api-slim/tests/test_worker.py
  • uvx ruff@0.14.9 check --select F hindsight-api-slim/hindsight_api/worker/poller.py hindsight-api-slim/tests/test_worker.py
  • git diff --check

I attempted the focused pytest selection, but this sparse local checkout is missing runtime dependencies before the tests run (hindsight_api.engine, then asyncpg; the project environment also pulls packages that are not available here on macOS Python 3.14). The added tests use fake async connection objects for the new status guard and should run in the normal project test environment.

@benfrank241
benfrank241 merged commit 82e6731 into vectorize-io:main Jul 8, 2026
87 checks passed
nicoloboschi added a commit that referenced this pull request Jul 20, 2026
execute_task completes an operation via _mark_operation_completed /
_mark_operation_completed_and_fire_webhook, both of which wrapped the
status='completed' commit in one transaction with fallible side-effects
(webhook outbox insert, parent aggregation) and swallowed every exception.
A hiccup in either rolled the completion back and dropped the error, leaving
the operation stuck in 'processing' forever while the log already said the
work was done (#2601). PR #2608 added a poller-side backstop that unstuck
the row but silently lost the consolidation webhook.

- On failure of the atomic outbox transaction, fall back to a completion-only
  commit and fire the consolidation webhook best-effort (non-transactional)
  instead of losing both. Happy path keeps the transactional-outbox guarantee;
  the failure path degrades to completed + delivered rather than stuck + lost.
  The best-effort fire only runs when the fallback actually transitioned the
  row, so there is no duplicate delivery.
- Guard every completion UPDATE on `status NOT IN ('completed','failed',
  'cancelled')` so an already-terminal row is never re-terminalized: keeps the
  engine idempotent with the poller backstop (#2608) and avoids double parent
  aggregation, while still completing pending/processing rows.

Adds fast DB-free regression tests (fake connections) covering the happy
path (no double-fire), the webhook-failure fallback, and the terminal-row
no-op guard.
nicoloboschi added a commit that referenced this pull request Jul 20, 2026
…ts (#2823)

execute_task completes an operation via _mark_operation_completed /
_mark_operation_completed_and_fire_webhook, both of which wrapped the
status='completed' commit in one transaction with fallible side-effects
(webhook outbox insert, parent aggregation) and swallowed every exception.
A hiccup in either rolled the completion back and dropped the error, leaving
the operation stuck in 'processing' forever while the log already said the
work was done (#2601). PR #2608 added a poller-side backstop that unstuck
the row but silently lost the consolidation webhook.

- On failure of the atomic outbox transaction, fall back to a completion-only
  commit and fire the consolidation webhook best-effort (non-transactional)
  instead of losing both. Happy path keeps the transactional-outbox guarantee;
  the failure path degrades to completed + delivered rather than stuck + lost.
  The best-effort fire only runs when the fallback actually transitioned the
  row, so there is no duplicate delivery.
- Guard every completion UPDATE on `status NOT IN ('completed','failed',
  'cancelled')` so an already-terminal row is never re-terminalized: keeps the
  engine idempotent with the poller backstop (#2608) and avoids double parent
  aggregation, while still completing pending/processing rows.

Adds fast DB-free regression tests (fake connections) covering the happy
path (no double-fire), the webhook-failure fallback, and the terminal-row
no-op guard.
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.

__mark_completed() is never called after successful task execution

2 participants