Skip to content

How a fix reaches production

Edition: Community and Enterprise

Every data fix in sealfix follows one path. Each step has one owner, and nothing moves forward until that step is done.

flowchart TB
    subgraph Requester
      A["Write the SQL template<br/>and submit the values"]
    end
    subgraph sealfix
      B["Static checks<br/>on the template and values"]
      C["Seal the values"]
      D["Dry run on the target<br/>in a transaction that is rolled back"]
    end
    subgraph Reviewers
      E["DBA reviewer approves the SQL"]
      F["Business approver approves the change"]
    end
    subgraph Executor
      G["Save the before-image<br/>run the statements<br/>compare with the dry run"]
    end
    A --> B --> C --> D --> E --> F --> G
    B -- "a check fails" --> A
    E -- "rejected" --> A
    F -- "rejected" --> A
    G -- "counts differ" --> X["Rolled back by itself<br/>nothing changed"]
    G -- "counts match" --> Y["Committed"]
    classDef ok fill:#E9EEF6,stroke:#23407A,color:#13202D
    classDef bad fill:#F6E8EB,stroke:#7D1D2F,color:#13202D
    class Y ok
    class X bad

The template and the values

A request has two parts:

  • the template: the SQL with named parameters, which says what the change does;

    UPDATE customers SET status = :status WHERE national_id = :nid
    
  • the values: the data that says whom it is done to, one row per change. They are sealed on arrival and reach the database only as bind variables.

Checks before anyone looks

When the requester submits, sealfix checks the template and the values before anything is stored. Only data-change statements (UPDATE, DELETE, INSERT) are allowed, an UPDATE or DELETE needs a WHERE, a sensitive value written into the SQL is refused, and every table must be on the target's allowlist. The full list is in Checks.

The dry run

The executor runs the fix on the target inside a transaction and rolls it back. The reviewers see what it would do: the rows per statement, a sample of the changed rows (with the values hidden), the query plans and every trigger it fires. A dry run that touches more rows than the request allows fails.

Approvals

Each environment says which approvals a fix needs. By default, production needs a DBA reviewer and a business approver, two different people, neither of them the requester. Non-production needs a DBA reviewer. See Approvals, risk and emergencies.

Every approval is bound to exactly what was approved: the template, the sealed values, the target, the row limit and the dry run's assessment. Change any of them and the approvals no longer count.

Execution

Only the requester starts the execution, and only the executor runs it, in one transaction:

  1. read and seal the rows the statements will change (the before-image);
  2. run the statements with the values bound;
  3. compare the row counts with the dry run, and with the request's row limit;
  4. commit if they match, roll back by itself if they do not.

The states

The state reference lists every state and transition.

stateDiagram-v2
    direction LR
    [*] --> draft
    draft --> checking: submit
    checking --> checks_failed: a check fails
    checking --> dry_run_pending: checks pass
    dry_run_pending --> dry_running
    dry_running --> dry_run_failed
    dry_running --> awaiting_review: dry run ok
    awaiting_review --> awaiting_business: technical approval
    awaiting_review --> approved: no business approval needed
    awaiting_business --> approved: business approval
    approved --> executing: execute
    executing --> executed: committed
    executing --> execution_failed: rolled back
    executed --> rolled_back: a rollback ran
    awaiting_review --> rejected
    awaiting_business --> rejected