Two remaining delivery terminal-state gaps: opaque failure and deletion-orphaned retries #107
Reference in New Issue
Block a user
Delete Branch "%!s()"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Both surfaced by the independent review of PR #104 (#82) and correctly held out of its scope. They are the two remaining paths where a delivery reaches a bad terminal state without the engine recording why, or fails to reach one at all.
#82 fixed the retyped-target case. These are its siblings.
1.
processDeliveryfails an unknown target type with no result rowinternal/delivery/engine.go:783-796. When a delivery's target type is not in the registry,processDeliverymarks the deliveryfailedand records nothing.That is precisely the opacity #82's spec forbade for the recovery and sweep paths — a delivery that ends
failedwith an empty event log is unreadable to anyone trying to work out what happened. #104 fixed the two recovery/sweep sites and deliberately did not touch this one, so the codebase is now inconsistent: two paths explain themselves, one does not.Done when: the unknown-target-type branch in
processDeliveryrecords aDeliveryResultnaming the offending type before marking the deliveryfailed, following the samerecordResult-then-updateDeliveryStatusidiom used by #104 and byinternal/delivery/target_http.go:299-306; with a test asserting the result row exists and names the type, mutation-verified.2. A deleted target leaves a retrying delivery stuck forever
Distinct trigger, same end state as #82. Both
recoverSingleRetryandsweepSingleRetrybegin withloadTarget(d.TargetID); if the target row is gone rather than retyped,loadTargeterrors and both sites log andreturn. The delivery staysretryingfor the lifetime of the database.#82 only reached the case where a target still exists but no longer implements
rescheduler. Deleting a target while one of its deliveries is mid-retry is a more ordinary operator action than editing a target's type, so this is arguably the likelier of the two to happen in practice.Done when: a
retryingdelivery whose target row no longer exists is terminally resolved rather than left hanging — the natural choice is the same treatment #82 settled on (record a result row explaining the target is gone, markfailed), so all three cases behave alike. Cover both the recovery and sweep paths, mutation-verified.Note the interaction with soft deletes: confirm whether target deletion is soft (GORM default scope) or hard here, since a soft-deleted target may still be loadable depending on the query, and the fix differs accordingly. Establish that first.
Why one issue
Both are the same theme — a delivery ending in a state the engine cannot explain — both live in
internal/delivery/engine.go, and both should follow the idiom #104 established. Fixing them together keeps the three terminal paths consistent, which is the actual goal.Definition of done
failedwith an empty event log, and no path can leave one inretryingindefinitely.make checkgreen via the repo's own entrypoints;.golangci.ymluntouched.