-
[OPIK-7159][OPIK-7029] [BE] fix: address post-merge review (Redis-independent status writes; configurable reaper lookback) (#7512)
发布于
2026-07-21 10:02:50 +00:00 - [OPIK-7159][OPIK-7029] fix: address post-merge review feedback
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
- [OPIK-7159][OPIK-7029] fix: serialize optimization update under lock 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
- [OPIK-7159][OPIK-7029] fix: keep cancellation on the hard-fail path + 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
- [OPIK-7159][OPIK-7029] test: camelCase lock-test method names per backend 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
- [OPIK-7159][OPIK-7029] refactor: lock every optimization update, drop 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
Co-authored-by: Claude Opus 4.8 (1M context) noreply@anthropic.com
下载附件