Infra ADR-0004 renamed the Gitea host. Bulk replace across go.mod and all .go import paths. Build and tests pass unchanged. Closes #20 Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Dt6aHEDWRjkK14Voi6HnGh
51 lines
1.7 KiB
Go
51 lines
1.7 KiB
Go
package main
|
|
|
|
import (
|
|
"context"
|
|
"log/slog"
|
|
"sync"
|
|
|
|
"git.d-ma.be/mathias/tapir/internal/runner"
|
|
)
|
|
|
|
// discoveryRunner runs one user's discovery pass.
|
|
type discoveryRunner func(ctx context.Context, userID string) (runner.Stats, error)
|
|
|
|
// serialize wraps run so calls never overlap: every discovery pass — scheduled
|
|
// or connect-triggered (#6) — acquires the same lock, preserving the
|
|
// one-fetcher-at-a-time invariant the scheduler relies on (ADR-018, the
|
|
// single-replica assumption). Locking is per-user, so a connect-triggered pass
|
|
// interleaves between the scheduler's users instead of waiting for a whole pass.
|
|
func serialize(mu *sync.Mutex, run discoveryRunner) discoveryRunner {
|
|
return func(ctx context.Context, userID string) (runner.Stats, error) {
|
|
mu.Lock()
|
|
defer mu.Unlock()
|
|
return run(ctx, userID)
|
|
}
|
|
}
|
|
|
|
// discoveryTrigger fires an out-of-band discovery pass for one user without
|
|
// blocking the caller (the connect HTTP handler). The pass runs on the server's
|
|
// long-lived ctx — not the request ctx — so it survives the post-connect
|
|
// redirect. run is the serialized runner, so a trigger never overlaps the
|
|
// scheduler. Satisfies web.DiscoveryTrigger.
|
|
type discoveryTrigger struct {
|
|
ctx context.Context
|
|
run discoveryRunner
|
|
// onboard, when set, runs after the discovery pass to summarize a capped number
|
|
// of the user's newest videos (Feature 1). Optional.
|
|
onboard func(ctx context.Context, userID string)
|
|
log *slog.Logger
|
|
}
|
|
|
|
func (t *discoveryTrigger) Enqueue(userID string) {
|
|
go func() {
|
|
if _, err := t.run(t.ctx, userID); err != nil {
|
|
t.log.Warn("discovery: connect-triggered pass had errors", "user", userID, "err", err)
|
|
}
|
|
if t.onboard != nil {
|
|
t.onboard(t.ctx, userID)
|
|
}
|
|
}()
|
|
}
|