8000 [BUG] jobs/run-now ignores idempotency_token and executes duplicate writes on retry · Issue #56 · dmux/minilake · GitHub
[go: up one dir, main page]

Skip to content

[BUG] jobs/run-now ignores idempotency_token and executes duplicate writes on retry #56

Closed

Description

@eddiethedean

Description

Repeating a jobs/run-now request with the same idempotency_token starts another run and executes its tasks again. A normal client retry can therefore duplicate data even when the caller explicitly requested idempotent execution.

Verified against MiniLake 1.9.0 source at local commit e24f46a7753ff0be30df3d83978c370c817f89cd (based on upstream fe8837dcfac9df621dfabc440abc07ebb4cdbcbb; the affected code is unchanged). Reproduced using a real disposable HTTP server and fresh data, with Python 3.14.3, DuckDB 1.5.6, FastAPI 0.162.0 and, for MCP, mcp 1.30.0. No mocks or user data were used.

Affected Components

Steps to Reproduce

Use a fresh disposable instance; paths are relative to its base URL.

  1. POST /api/2.0/sql/warehouses with {"name":"retry-repro"}; capture W.
  2. Execute CREATE TABLE retry_effect (id INTEGER) via POST /api/2.0/sql/statements with W.
  3. POST raw bytes INSERT INTO retry_effect VALUES (99) to /api/2.0/workspace-files/import-file/retry.sql.
  4. POST /api/2.2/jobs/create with:
    {"name":"retry-repro","tasks":[{"task_key":"insert","sql_task":{"warehouse_id":"W","file":{"path":"/retry.sql"}}}]}
    Capture job ID J; substitute the actual W.
  5. POST /api/2.2/jobs/run-now with {"job_id":J,"idempotency_token":"one-logical-write"}. Poll /api/2.2/jobs/runs/get?run_id=R until TERMINATED.
  6. Repeat the identical request from step 5 and wait for termination again.
  7. Query SELECT * FROM retry_effect.

Expected Behavior

The second request returns the original run ID and does not re-execute the INSERT. The table contains one row.

This matches the supported Databricks SDK's JobsAPI.run_now(idempotency_token=...) contract: a repeated token returns the existing run rather than launching a second run.

Actual Behavior

The reproduction returned run IDs 4 and 6, both completed successfully, and the table contained [[99],[99]].

Root Cause Analysis

RunNowRequest accepts extra fields, including the token, but run_now never reads it. Every call allocates a fresh run ID and launches _execute_run. The separate runs/submit handler implements a token lookup; run-now does not.

Impact

Retry-safe client code behaves incorrectly against the emulator. Retried ingestion, append, or other tasks repeat their side effects and corrupt fixtures/datasets. Tests can fail to catch broken idempotency assumptions before deployment. The defect requires no concurrency or timing race: sequential identical calls reproduce it.

Suggested Fix

Implement explicit token validation and token-to-run tracking for run-now, returning the original run for repeats. Preserve the mapping across persistence and define deleted-run behavior consistently with the SDK contract. Ensure check-and-create is atomic within the supported process model.

Test Coverage Gaps

tests/test_jobs_submit.py::test_submit_idempotency_token_reuses_the_run covers only runs/submit. Add a run-now counterpart using a real side-effecting SQL task; assert identical returned IDs, one stored run and one inserted row after retries, including after restart.

Severity

High

Confidence

High — reproduced sequentially through real HTTP and confirmed duplicate stored rows.

Recommended Labels

bug, high-priority

Existing Issue Matches

No reasonable duplicate found. Reviewed all open issues, the closed-issue search (no results), and all open PRs, then searched the affected functions/components and symptoms. Open PR #54 fixes duplicate Arrow columns only; #29 updates virtualenv. Neither addresses this defect. The documented local/single-user limitations were checked. Searches included run_now, idempotency, retry and duplicate execution.

Activity

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

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions

      0