Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
97 commits
Select commit Hold shift + click to select a range
956b9ee
docs: design lease-fenced durable job worker
seonghobae Aug 5, 2026
4f170ec
docs: plan lease-fenced durable job worker
seonghobae Aug 5, 2026
e4960fe
test: specify durable job lease schema
seonghobae Aug 5, 2026
8f52630
test: specify worker configuration bounds
seonghobae Aug 5, 2026
6155eb8
feat(etl): add durable job lease schema
seonghobae Aug 5, 2026
0325b86
feat(etl): define fail-closed worker properties
seonghobae Aug 5, 2026
a129333
feat(etl): register worker configuration
seonghobae Aug 5, 2026
aeeeb38
feat(etl): add fail-closed worker defaults
seonghobae Aug 5, 2026
faab9de
test(etl): align claim index contract
seonghobae Aug 5, 2026
f1eac6e
test(etl): specify lease-fenced job persistence
seonghobae Aug 5, 2026
d037bf1
test(etl): specify lease value invariants
seonghobae Aug 5, 2026
f1d066f
feat(etl): add immutable lease value
seonghobae Aug 5, 2026
b04a105
feat(etl): define stale lease failure
seonghobae Aug 5, 2026
de7d29d
feat(etl): persist exclusive fenced claims
seonghobae Aug 5, 2026
c118dc5
test(etl): specify atomic leased execution
seonghobae Aug 5, 2026
72fbed4
feat(etl): couple target writes to lease success
seonghobae Aug 5, 2026
8fffb6b
test(etl): keep execution collaborators encapsulated
seonghobae Aug 5, 2026
a3b7af4
test(etl): specify bounded worker outcomes
seonghobae Aug 5, 2026
de3e5e1
feat(etl): execute one bounded leased job per poll
seonghobae Aug 5, 2026
19d3085
feat(etl): enable conditional worker scheduling
seonghobae Aug 5, 2026
bd48d4a
test(etl): make lease transition order deterministic
seonghobae Aug 5, 2026
379d5cb
test(etl): cover stale terminal transitions
seonghobae Aug 5, 2026
6fe4162
test(config): specify durable worker aliases
seonghobae Aug 5, 2026
aa90cd3
feat(config): dual-read durable worker settings
seonghobae Aug 5, 2026
f988caa
test(etl): specify stored execution identity
seonghobae Aug 5, 2026
1716701
feat(etl): carry durable execution identity in leases
seonghobae Aug 5, 2026
108dc12
feat(etl): claim durable execution identity
seonghobae Aug 5, 2026
f43d369
test(etl): provide worker execution hashes
seonghobae Aug 5, 2026
960f6f6
test(etl): verify claimed execution identity
seonghobae Aug 5, 2026
9ac0890
test(etl): specify durable execution ledger
seonghobae Aug 5, 2026
dcdeb64
feat(etl): define durable job integrity failure
seonghobae Aug 5, 2026
c376189
feat(etl): centralize SHA-256 digesting
seonghobae Aug 5, 2026
262d53b
feat(etl): reuse response ledger for durable jobs
seonghobae Aug 5, 2026
b072dac
feat(etl): commit ledger and target with lease success
seonghobae Aug 5, 2026
0810937
test(etl): prove atomic ledger target and success
seonghobae Aug 5, 2026
de85194
feat(etl): expose stable integrity failures
seonghobae Aug 5, 2026
c3752db
test(etl): cover integrity failure classification
seonghobae Aug 5, 2026
8c31ed9
test(etl): verify SHA-256 digest utility
seonghobae Aug 5, 2026
e536c9b
docs: add durable job worker runbook
seonghobae Aug 5, 2026
1b7417f
docs: connect durable intake to lease worker
seonghobae Aug 5, 2026
fcefbf4
docs: align lease worker design with response ledger
seonghobae Aug 5, 2026
a727b87
docs: record lease-fenced durable execution
seonghobae Aug 5, 2026
1c44790
docs: preserve stacked changelog history
seonghobae Aug 5, 2026
cafedca
test(etl): enforce complete durable execution coverage
seonghobae Aug 5, 2026
956a392
test(etl): verify complete execution coverage policy
seonghobae Aug 5, 2026
5090dfa
test(etl): specify SHA-256 failure coverage
seonghobae Aug 5, 2026
02353f3
feat(etl): make digest failure path testable
seonghobae Aug 5, 2026
e02e873
test(etl): compare expanded coverage includes deterministically
seonghobae Aug 5, 2026
92bc0d2
test(etl): reject unbounded worker timing configuration
seonghobae Aug 5, 2026
8d243ac
fix(etl): bound worker delays and lease duration
seonghobae Aug 5, 2026
0273753
test(etl): reject overlong repository leases
seonghobae Aug 5, 2026
1377f6e
fix(etl): enforce repository lease ceiling
seonghobae Aug 5, 2026
51d1202
docs(etl): document bounded worker timing controls
seonghobae Aug 5, 2026
5af809f
docs(config): expose worker timing ceilings
seonghobae Aug 5, 2026
c96eb7c
Merge PR #121 exact head into durable job worker stack
seonghobae Aug 5, 2026
8e91f69
test(etl): align runbook contract with lease-fenced execution
seonghobae Aug 5, 2026
5bafdd5
test(etl): cover impossible locked-claim transition
seonghobae Aug 5, 2026
95ed508
test(etl): require complete worker outcome evidence
seonghobae Aug 5, 2026
0f77c21
fix(etl): complete durable worker outcome evidence
seonghobae Aug 5, 2026
4a58907
docs(etl): define complete worker outcome accounting
seonghobae Aug 5, 2026
5b7e6d0
docs(changelog): record complete worker outcome accounting
seonghobae Aug 5, 2026
38a1675
fix(changelog): preserve ETL alias namespace
seonghobae Aug 5, 2026
6d53965
fix(changelog): keep historical summary unchanged
seonghobae Aug 5, 2026
5ac536f
test(etl): require one terminal worker outcome per poll
seonghobae Aug 5, 2026
c66d052
fix(etl): count one terminal worker outcome per poll
seonghobae Aug 5, 2026
3f1e23a
test(etl): align worker metric expectations with terminal outcomes
seonghobae Aug 5, 2026
a68d84c
test(etl): require nonblocking claim index rollout
seonghobae Aug 5, 2026
a6232dc
fix(etl): keep lease schema migration transactional
seonghobae Aug 5, 2026
d1d1444
fix(etl): build claim index without blocking writers
seonghobae Aug 5, 2026
e44ea4a
build(etl): run claim index migration outside transaction
seonghobae Aug 5, 2026
9835495
fix(etl): use session Flyway lock for concurrent indexes
seonghobae Aug 5, 2026
2797795
test(etl): verify concurrent Flyway lock configuration
seonghobae Aug 5, 2026
7bde8e1
docs(etl): document nonblocking claim index rollout
seonghobae Aug 5, 2026
c447bfa
test(etl): bind claim index evidence to focused runbook
seonghobae Aug 5, 2026
ec140c5
test(etl): keep lease schema migration focused
seonghobae Aug 5, 2026
1b53a98
docs(etl): align worker metrics and migration rollout
seonghobae Aug 5, 2026
c2f0802
docs(changelog): record nonblocking claim index rollout
seonghobae Aug 5, 2026
9d6f4ba
fix(build): preserve explicit patched Jackson BOM import
seonghobae Aug 5, 2026
36313a6
test(etl): define durable worker retry boundary
seonghobae Aug 5, 2026
e8ec36d
test(etl): require an active durable execution transaction
seonghobae Aug 5, 2026
b734b99
fix(etl): separate durable and synchronous retry boundaries
seonghobae Aug 5, 2026
d1405db
fix(etl): keep durable retries outside the lease transaction
seonghobae Aug 5, 2026
1a5d9c0
docs(etl): record durable retry-boundary evidence
seonghobae Aug 5, 2026
0b3faf2
test(etl): require transactional success fencing
seonghobae Aug 5, 2026
9c1670e
fix(etl): require atomic success transaction
seonghobae Aug 5, 2026
a7e16e6
test(etl): execute success fencing in its atomic transaction
seonghobae Aug 5, 2026
b4fb360
docs(etl): record atomic success transaction gate
seonghobae Aug 5, 2026
26d1107
test(etl): forbid idempotency-owned execution transactions
seonghobae Aug 5, 2026
c10bfdf
fix(etl): join the lease-fenced execution transaction
seonghobae Aug 5, 2026
3a7aa63
test(etl): prove expired leases roll back durable effects
seonghobae Aug 5, 2026
cee0096
test(etl): run idempotency integration cases in transactions
seonghobae Aug 5, 2026
f3c448e
Merge cee009681ea0b1ce5f97fd63cd4fe37f18cbeef9 into 68e82f9d7e19623b6…
seonghobae Aug 5, 2026
2cf368c
Merge ci/hourly-opencode-nvidia-nim into feat/durable-job-lease-worker
seonghobae Aug 6, 2026
f57256b
fix(stack): restore complete #121 tree in durable worker
seonghobae Aug 6, 2026
2516c3f
merge: reconcile durable worker with exact OpenCode predecessor
seonghobae Aug 6, 2026
fc54e7f
merge: reconcile durable worker with exact-head workflow predecessor
seonghobae Aug 7, 2026
05f0ec8
docs(changelog): reconcile exact-head workflow evidence
seonghobae Aug 7, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 14 additions & 2 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,13 +10,16 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
### Changed

- Pull-request CI and CycloneDX SBOM jobs now check out the literal current source head, immediately assert `git rev-parse HEAD` against `github.event.pull_request.head.sha`, and disable checkout credential persistence; generated merge revisions remain useful compatibility previews but no longer masquerade as direct exact-head source evidence.
- The durable-job claim eligibility index now builds in a separate PostgreSQL `CREATE INDEX CONCURRENTLY` migration with Flyway non-transactional script configuration and session-level PostgreSQL migration locking, preserving normal job writes during rollout while keeping lease columns and constraints transactional.
- Durable asynchronous ETL jobs now progress from `PENDING` through lease-fenced execution to `SUCCEEDED` or `FAILED`; PostgreSQL owns cross-replica claiming, stale workers cannot commit target or lifecycle effects, and intake and execution remain independently fail-closed.
- Durable-worker observability now records one terminal outcome counter and one matching duration sample for every completed poll, including idle polls and database failures while persisting retry or terminal transitions.
- The hourly pull-request disposition loop now requires at least one non-author approval anchored to the exact current head SHA; stale approvals, comment-only reviews, and the mere absence of requested changes cannot authorize unattended merge.
- The hourly OpenCode workflow now scopes repository write permissions to its sole maintenance job, replaces the npm installation command with the immutable OpenCode 1.18.13 Linux release archive plus pinned SHA-256 validation, requires exactly one regular-file archive member before private-directory extraction, rejects non-regular or symbolic-link output, and uses a removable repository-local GitHub CLI credential helper instead of storing an encoded authorization header while retaining `persist-credentials: false`.
- The hourly OpenCode workflow now snapshots same-repository `develop` pull-request heads before the agent runs and uses job-scoped Actions write authority only to authorize approval-required workflow runs for an unchanged exact head; `.github/**` and `CODEOWNERS` changes remain human-authorized, and no review or merge authority is added.
- Updated existing pull-request candidates now carry their captured pre-agent head into the deterministic publisher, which rejects destructive ancestry, more than 50 agent-introduced files, and any agent-introduced `.github/**` or `CODEOWNERS` change before exposing the updated pull request or authorizing checks.
- The hourly OpenCode workflow now uses the current free NVIDIA `deepseek-ai/deepseek-v4-pro` endpoint for long-context coding and agentic tool use instead of the deprecated Qwen3 Coder free endpoint; model or endpoint rejection fails visibly without a non-NVIDIA, partner-only, or automatic fallback.
- The managed Jackson component set now uses the patched 2.21.5 BOM, closing CVE-2026-54515, CVE-2026-59889, and GHSA-mhm7-754m-9p8w while keeping core, annotations, datatype, and module artifacts aligned.
- Durable `POST /api/etl/jobs` submissions now return RFC 9110 `202 Accepted`, a stable pending-job representation, `Location` status-monitor metadata, and explicit replay metadata without changing the synchronous `/api/etl/process` contract. The incomplete intake controller is fail-closed and requires explicit `xtrmetl.etl.jobs.intake-enabled=true` operator opt-in until worker execution and terminal payload clearing are implemented.
- Durable `POST /api/etl/jobs` submissions now return RFC 9110 `202 Accepted`, a stable job representation, `Location` status-monitor metadata, and explicit replay metadata without changing the synchronous `/api/etl/process` contract.
- Concurrent requests using the same authenticated-principal-scoped semantic idempotency key now return immediate RFC 9457 `409 etl_idempotency_request_in_progress` responses through PostgreSQL `pg_try_advisory_xact_lock`; retries after completion still replay the committed response.
- `POST /api/etl/process` now supports optional authenticated-principal-scoped `Idempotency-Key` retries with atomic target writes, durable response replay, payload-conflict rejection, and explicit replay response metadata.
- `Idempotency-Key` now prefers the quoted RFC 9651 Structured Field String representation while retaining and normalizing the legacy raw representation to the same durable ledger key.
Expand All @@ -30,11 +33,15 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
### Added

- Permanent fail-first exact-head workflow contracts and authoritative evidence in `docs/doctoring/exact-head-source-workflow-evidence.md`, including the observed synthetic merge checkout, cross-platform source identity assertions, least-privilege boundary, stack invalidation rule, rollback prohibition, and APA 7th GitHub references.
- A production rollout and invalid-index recovery runbook for the nonblocking durable-job claim index: `docs/operations/durable-job-claim-index-rollout.md`.
- PostgreSQL `FOR UPDATE SKIP LOCKED` durable-job claiming, per-process and per-claim lease fencing, expiry reclaim, bounded attempts, exact-live-lease transitions, terminal payload clearing, stable failure codes, and finite-cardinality worker metrics.
- Hashed durable execution identity and domain-separated reuse of `etl_idempotency_records`, coupling response replay or creation, target writes, and terminal `SUCCEEDED` in one transaction without retaining or reconstructing raw principals or raw client idempotency keys.
- Deterministic migration, concurrency, expiry, exhaustion, response-replay, integrity, stale-lease rollback, privacy, configuration-boundary, and operator-recovery tests plus `docs/operations/durable-job-worker.md`.
- A separate fail-closed hourly OpenCode maintenance workflow pinned to OpenCode 1.18.13 and `nvidia/deepseek-ai/deepseek-v4-pro`, using only the existing `NVIDIA_NIM_API_KEY` through OpenCode's `NVIDIA_API_KEY` provider variable while preserving the independent review agent and deterministic merge-disposition workflow.
- Exact-head workflow-run authorization doctoring evidence for the repository-token recursion boundary, before/after SHA snapshots, policy-path exclusion, time-of-check/time-of-use validation, least privilege, test-first regression evidence, and rollback in `docs/doctoring/github-token-exact-head-check-authorization-evidence.md`.
- Supply-chain doctoring evidence for checksum binding, exact archive-member and entry-type validation, private extraction, post-extraction file checks, test-first regression evidence, and rollback in `docs/doctoring/opencode-archive-extraction-evidence.md`.
- NVIDIA model-selection doctoring evidence for endpoint availability, deprecated-endpoint rejection, capability and context evidence, no-fallback semantics, test-first regression evidence, and replacement procedure in `docs/doctoring/nvidia-opencode-model-selection-evidence.md`.
- Principal-scoped durable asynchronous ETL job intake and owner-scoped status resources, Flyway `etl_job_records` migration, deterministic replay/conflict coverage, and the explicit worker boundary in `docs/etl/durable-job-intake.md`.
- Principal-scoped durable asynchronous ETL job intake and owner-scoped status resources, Flyway `etl_job_records` migration, deterministic replay/conflict coverage, and the authoritative lifecycle contract in `docs/etl/durable-job-intake.md`.
- Durable idempotency ledger migration, PostgreSQL transaction advisory-lock adapter, deterministic concurrency/rollback coverage, and the operator/client contract `docs/etl/idempotent-retries.md`.
- ETL problem-details client and operator contract: `docs/api/problem-details.md`.
- Operator-configurable ETL admission limits under `mightyetl.etl.*` / `xtrmetl.etl.*`, backed by `ETL_MAX_PAYLOAD_BYTES` and `ETL_MAX_BATCH_RECORDS` environment variables with hard safety ceilings.
Expand All @@ -60,6 +67,11 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- Root POM `<name>mightyETL</name>` (artifactId remains `xtrmETL`).
- README honest “Supported today” matrix; compose file product-name header.

### Security

- Durable-worker metrics and ordinary logs exclude payloads, raw principals, raw idempotency keys, hashes, job and lease identifiers, SQL, exception messages, and unbounded exception labels.
- Retained payload or response-ledger identity conflicts fail closed with `etl_job_integrity_failure`; an expired or superseded lease rolls back target, ledger, and terminal-state effects.

### Added (historical)

- Comprehensive documentation suite (2026-01-08)
Expand Down
101 changes: 101 additions & 0 deletions docs/doctoring/durable-job-retry-boundary-evidence.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,101 @@
# Durable-job retry and success-boundary doctoring evidence

## Decision

One durable worker claim represents exactly one persisted execution attempt. The worker must call
`EtlService.processDataInExistingTransaction` rather than the synchronous `processData` entry point.

The synchronous endpoint keeps `@Retryable` outside `@Transactional` so each request retry can own a
fresh transaction. The durable worker already owns bounded retries through `attempt_count`, claim
renewal by a later poll, and lease-fenced lifecycle transitions. Its target writes, response-ledger
write, and exact-live-lease success transition must remain inside one caller-owned transaction.

`EtlJobLeaseRepository.markSucceeded` therefore rejects direct invocation when no actual Spring
transaction is active. Retry and failure transitions remain independently persistable after an
execution exception, but success can never be published separately from the effects it certifies.

## Failure modes prevented

### Retry advice inside an existing durable transaction

Calling the synchronous `@Retryable` method from an already active durable execution transaction can
place retry advice inside an outer transaction that it did not create. After a transactional database
failure, another in-process invocation can reuse a rollback-only or otherwise failed transaction
instead of starting the fresh transaction assumed by Spring Retry. It also performs retries that are
not represented by the durable job's `attempt_count`.

### False success outside the atomic execution transaction

A public success-transition method that can run in autocommit mode allows accidental callers to mark
a job `SUCCEEDED` without the target rows and response ledger being committed in the same unit of
work. Even an exact lease predicate cannot prove those effects exist. Requiring an actual transaction
before the success SQL executes makes the repository fail closed at its public boundary.

The complete contract is:

```text
one database claim
→ one existing execution transaction
→ one non-retrying ETL invocation
→ target rows + response ledger + exact-live-lease success
→ one atomic commit

or

one escaped failure
→ worker-owned durable retry / terminal-failure decision
```

No in-process retry happens inside the lease transaction. A transient exception escapes to
`EtlJobWorker`, which either returns the exact live lease to `PENDING` while attempts remain or records
a stable terminal failure after the configured maximum.

## Test-first evidence

The regression contracts were added before their production behavior:

- `EtlJobIdempotencyRetryBoundaryTest` required durable execution to call
`processDataInExistingTransaction` exactly once and never call `processData`;
- `EtlServiceIdempotencyTransactionBoundaryTest` required the durable ETL entry point to reject
direct use without an actual transaction before JDBC or request-lock access;
- `EtlJobLeaseSuccessTransactionBoundaryTest` required `markSucceeded` to reject use without an
actual transaction before JDBC access;
- `EtlJobLeaseRepositoryIntegrationTest` executes successful lease fencing inside a real Spring test
transaction and still verifies expiry and supersession rejection.

Production then added the non-retrying, transaction-requiring ETL entry point, changed
`EtlJobIdempotencyService` to use it, and added the active-transaction guard to the public success
transition. Existing synchronous processing retains its retry behavior.

## Review and operational evidence

Reviewers should confirm all of the following on the exact current head:

1. `processDataInExistingTransaction` has neither `@Retryable` nor `@Transactional`;
2. it fails closed when no actual Spring transaction is active;
3. `EtlJobIdempotencyService` invokes only that entry point for a newly executed job;
4. response replay does not invoke ETL target writes;
5. `EtlJobLeaseRepository.markSucceeded` fails before JDBC without an actual transaction;
6. `EtlJobExecutionService.execute` owns the transaction containing ETL, ledger, and success;
7. transient exceptions escape to `EtlJobWorker` and affect durable attempt accounting once;
8. retry and terminal-failure transitions remain exact-lease fenced;
9. target rows, response ledger, and `SUCCEEDED` roll back together when success fencing fails;
10. statement and branch coverage gates remain at 100% for the configured production scope.

## Rollback

Disable the durable worker before reverting either boundary. Do not restore synchronous retry advice
inside the lease transaction, and do not allow `markSucceeded` to run in autocommit mode. A safe
replacement must preserve one persisted attempt per claim and prove that target effects, the response
ledger, and terminal success commit or roll back together.

## References — APA 7th

Spring Retry Authors. (2026). *EnableRetry.java* [Source code]. GitHub.
https://github.com/spring-projects/spring-retry/blob/main/src/main/java/org/springframework/retry/annotation/EnableRetry.java

Spring Retry Authors. (2026). *RetryOperationsInterceptor.java* [Source code]. GitHub.
https://github.com/spring-projects/spring-retry/blob/main/src/main/java/org/springframework/retry/interceptor/RetryOperationsInterceptor.java

Spring Framework Authors. (2026). *TransactionSynchronizationManager.java* [Source code]. GitHub.
https://github.com/spring-projects/spring-framework/blob/main/spring-tx/src/main/java/org/springframework/transaction/support/TransactionSynchronizationManager.java
Loading