Skip to content

[OPIK-7159][OPIK-7029] [BE] fix: address post-merge review (Redis-independent status writes; configurable reaper lookback)#7512

Merged
awkoy merged 5 commits into
mainfrom
awkoy/opik-7159-7029-reland
Jul 21, 2026
Merged

[OPIK-7159][OPIK-7029] [BE] fix: address post-merge review (Redis-independent status writes; configurable reaper lookback)#7512
awkoy merged 5 commits into
mainfrom
awkoy/opik-7159-7029-reland

Conversation

@awkoy

@awkoy awkoy commented Jul 17, 2026

Copy link
Copy Markdown
Contributor

Details

Follow-up to #7428 (merged), addressing @thiagohora's post-merge review. Two backend fixes:

1. OptimizationService.update() no longer hard-depends on Redis for every status change ([medium]).
The distributed lock is now acquired only for a metadata update — the read-modify-write (getById → merge → insert) that can drop keys under a concurrent partial update. Status-only and name-only writes persist lock-free, so a Redis blip can no longer 500 the worker's mark_completed / mark_error callback (or the reaper's own ERROR write) and leave a run non-terminal for the reaper to later mislabel ERROR — the exact failure this feature exists to prevent. The worker already retries a metadata-carrying status write as a metadata-less one on failure, and that fallback now persists lock-free.

2. Reaper scan lookback margin is now configurable.
Was a hardcoded Duration.ofDays(7) in OptimizationDAO; now a validated, env-overridable config key optimizationStalledReaper.lookbackMargin (OPTIMIZATION_STALLED_REAPER_LOOKBACK_MARGIN, default 7d), alongside the sibling initializedTimeout / runningTimeout / lockDuration. Threaded through job → service → DAO so ops can tune the scan floor (and skip-index pruning) without a redeploy.

Change checklist

  • Lock scoped to metadata updates; status/name-only writes are Redis-independent
  • lookbackMargin added as a validated, env-overridable config key
  • New OptimizationServiceUpdateLockTest covers both the locked (metadata) and lock-free (status-only) paths
  • Reaper job / config / service tests updated for the new parameter

Issues

OPIK-7159, OPIK-7029 — post-merge review follow-up to #7428.

Testing

  • OptimizationServiceUpdateLockTest (new): asserts a status-only update does not touch LockService, and a metadata update is serialized under it.
  • OptimizationStalledReaperConfigTest, OptimizationStalledReaperJobTest pass with the new lookbackMargin field.
  • Backend compiles + these unit tests pass locally on JDK 25; full suite runs in CI.

Documentation

Config documented inline in config.yml (default, env var, and the reaper-downtime tradeoff). No user-facing docs affected.

Two fixes from thiagohora's review of the merged PR #7428:

- OptimizationService.update(): only acquire the distributed lock for a
  metadata update (the read-modify-write that can drop keys under a
  concurrent partial update). Status-only and name-only writes now persist
  lock-free, so a Redis blip can no longer 500 the worker's mark_completed /
  mark_error callback and leave a run non-terminal for the reaper to later
  mislabel ERROR. The worker already retries a metadata-carrying status write
  as a metadata-less one, and that fallback now persists lock-free. Adds
  OptimizationServiceUpdateLockTest covering both paths.

- Stalled-run reaper: make the scan lookback margin (was a hardcoded
  Duration.ofDays(7) in OptimizationDAO) a validated, env-overridable config
  key (optimizationStalledReaper.lookbackMargin /
  OPTIMIZATION_STALLED_REAPER_LOOKBACK_MARGIN), like the sibling timeouts.
  Threaded through job -> service -> DAO; tests updated.

Verified locally: backend compiles (JDK 25); reaper + new lock tests pass.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@awkoy
awkoy requested a review from a team as a code owner July 17, 2026 14:57
@github-actions github-actions Bot added java Pull requests that update Java code Backend tests Including test files, or tests related like configuration. 🟡 size/M labels Jul 17, 2026
@github-actions

github-actions Bot commented Jul 17, 2026

Copy link
Copy Markdown
Contributor

⏱️ pre-commit per-hook timing

Hook Description Result Duration
☕ spotless — java backend Format Java code 4.28s
Total (1 ran) 4.28s
⏭️ 40 skipped (no matching files changed)
Hook Description Result
🐍 trim trailing whitespace — python sdk Strip trailing whitespace ⏭️
🐍 fix end of files — python sdk Ensure files end in a newline ⏭️
🐍 ruff — python sdk Lint + autofix Python (ruff) ⏭️
🐍 ruff-format — python sdk Format Python code (ruff) ⏭️
🐍 mypy — python sdk Static type check ⏭️
🤖 trim trailing whitespace — optimizer Strip trailing whitespace ⏭️
🤖 fix end of files — optimizer Ensure files end in a newline ⏭️
🤖 check yaml — optimizer Validate YAML syntax ⏭️
🤖 check json — optimizer Validate JSON syntax ⏭️
🤖 check toml — optimizer Validate TOML syntax ⏭️
🤖 check for added large files — optimizer Block large files (>1MB) ⏭️
🔐 detect private key — optimizer Block committed private keys ⏭️
🤖 check for merge conflicts — optimizer Block merge-conflict markers ⏭️
🤖 check for case conflicts — optimizer Block case-only name clashes ⏭️
🤖 pyupgrade — optimizer Modernize Python syntax ⏭️
🤖 ruff — optimizer Lint + autofix Python (ruff) ⏭️
🤖 ruff-format — optimizer Format Python code (ruff) ⏭️
🤖 mypy — optimizer Static type check ⏭️
📓 nbstripout — optimizer notebooks Strip notebook output ⏭️
📝 markdownlint — optimizer Lint Markdown ⏭️
🔤 codespell — optimizer Fix common misspellings ⏭️
📊 radon cc — optimizer Cyclomatic-complexity gate ⏭️
📊 radon raw — optimizer Raw size metrics gate ⏭️
📊 xenon — optimizer Fail on complexity thresholds ⏭️
📊 lizard — optimizer Cyclomatic-complexity gate ⏭️
🧹 vulture — optimizer Find dead code ⏭️
🛡️ trim trailing whitespace — guardrails Strip trailing whitespace ⏭️
🛡️ fix end of files — guardrails Ensure files end in a newline ⏭️
🛡️ ruff — guardrails Lint + autofix Python (ruff) ⏭️
🛡️ ruff-format — guardrails Format Python code (ruff) ⏭️
🛡️ mypy — guardrails Static type check ⏭️
⚓ helm-docs Regenerate Helm chart README ⏭️
block non-public FE plugins Block non-public FE plugins ⏭️
🧪 pre-commit wrapper smoke tests Self-test the wrapper scripts ⏭️
🌐 eslint — frontend Lint + autofix JS/TS ⏭️
🌐 typecheck — frontend Whole-project tsc type check ⏭️
📘 eslint — typescript sdk Lint + autofix JS/TS ⏭️
📘 typecheck — typescript sdk Whole-project tsc type check ⏭️
⚙️ actionlint — github workflows Lint GitHub Actions workflows ⏭️
🐳 hadolint — dockerfiles Lint Dockerfiles ⏭️

Comment thread apps/opik-backend/src/main/java/com/comet/opik/domain/OptimizationService.java Outdated
…with a Redis-blip fallback

Adversarial code review of the previous "lock only when metadata != null"
variant found it de-serialized concurrent status/name writers: a rename racing
a status write (or the worker racing the reaper) could drop one column via the
UPDATE_BY_ID INSERT...SELECT, and a dropped terminal status strands a finished
run non-terminal for the reaper to later mislabel ERROR.

Take the per-id lock for every update again (restoring serialization), but fall
back to a lock-free apply on a lock-ACQUISITION RedisException so a Redis blip
still cannot 500 the worker's mark_completed / mark_error callback or the
reaper's own ERROR write (thiagohora's original [medium]). Only the acquisition
surfaces RedisException, so the action's own 404/409 still propagate and it runs
exactly once. Test rewritten to cover both the serialized and Redis-down
fallback paths.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Comment thread apps/opik-backend/src/main/java/com/comet/opik/domain/OptimizationService.java Outdated
… review nits

Address the re-review of PR #7512:

- [high] Cancellation must not use the Redis-blip lock-free fallback. The worker
  learns of a cancel only from the Redis signal it polls (CancellationMonitor,
  no DB-status polling), so persisting CANCELLED while Redis is down would report
  success to the user while the run keeps executing to timeout and its terminal
  callback is dropped by the terminal-overwrite guard. update() now returns the
  lock-guarded write directly for CANCELLED (hard-fail), and only falls back
  lock-free for the writes that don't need Redis to reach anyone (worker
  mark_completed/mark_error, reaper ERROR, rename).
- test: use the shared PodamFactoryUtils.newPodamFactory() fixture; add a
  name-only serialization case and a cancellation-hard-fail case; fix a comment
  typo.

Verified: backend compiles (JDK 25); 4/4 lock tests pass.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Comment thread apps/opik-backend/src/main/java/com/comet/opik/domain/OptimizationService.java Outdated
…kend testing guide

.agents/skills/opik-backend/testing.md prescribes camelCase test names (the
methodUnderTest + scenario shape), not snake/double-underscore (review: baz).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
… the Redis-blip fallback

Per thiagohora's review: locks are lightweight and should protect every state
change against data loss. Serialize all updates under the per-id lock and let a
Redis outage surface the error rather than falling back to a lock-free write.

This removes the earlier fallback's subtlety: the onErrorResume(RedisException ->
action) re-subscribed applyUpdate, which was safe only while applyUpdate never
surfaced a RedisException (a non-local invariant a future edit could break into a
double write / double analytics event), and it sanctioned a lock-free lost-update
path. A run left non-terminal by a failed write is covered by the stalled-run
reaper, so protecting against data loss is the better trade. Also drops the
CANCELLED special-case, now unnecessary. Test simplified to: every write
serializes under the lock; a lock-acquisition failure surfaces the error.

Verified: backend compiles (JDK 25); 3/3 lock tests pass.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@awkoy
awkoy merged commit 2385e54 into main Jul 21, 2026
72 of 74 checks passed
@awkoy
awkoy deleted the awkoy/opik-7159-7029-reland branch July 21, 2026 10:02
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Backend java Pull requests that update Java code 🟡 size/M tests Including test files, or tests related like configuration.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants