From c8ec468e475bfa303d917829632c59a98d8fbbb0 Mon Sep 17 00:00:00 2001 From: Mathias Date: Fri, 2 Oct 2026 16:42:46 +0200 Subject: [PATCH] feat(transcribe): WithTimeout -- each engine call gets its own deadline so a wedged engine fails while the job lives (review F4) Co-Authored-By: Claude Sonnet 5.5 --- internal/adapters/transcribe/timeout.go | 33 +++++++++++++++++++++++++ 1 file changed, 33 insertions(+) create mode 100644 internal/adapters/transcribe/timeout.go diff --git a/internal/adapters/transcribe/timeout.go b/internal/adapters/transcribe/timeout.go new file mode 100644 index 0000000..fee7b1b --- /dev/null +++ b/internal/adapters/transcribe/timeout.go @@ -0,0 +1,33 @@ +package transcribe + +import ( + "context" + "time" + + "git.d-ma.be/mathias/tapir/internal/domain" +) + +// WithTimeout gives every call to b its own deadline. A wedged engine does not answer at +// all (iguana after a Metal OOM, infra#273), and with only the job's deadline it used up +// the whole job before a fallback could run -- and Chain then refuses the fallback because +// the job context has expired. With its own deadline the call fails while the job is still +// alive, which is exactly when the fallback, or the other engine, can take over. +// +// A zero or negative d returns b unchanged. The wrapper is invisible: Name is b's. +func WithTimeout(b Backend, d time.Duration) Backend { + if d <= 0 { + return b + } + return timeoutBackend{Backend: b, d: d} +} + +type timeoutBackend struct { + Backend + d time.Duration +} + +func (t timeoutBackend) Transcribe(ctx context.Context, wavPath string) (domain.Transcription, error) { + ctx, cancel := context.WithTimeout(ctx, t.d) + defer cancel() + return t.Backend.Transcribe(ctx, wavPath) +}