Some checks failed
CI / Docker / caddy (pull_request) Failing after 50s
CI / Check ui (pull_request) Successful in 1m5s
Release / Check ui (push) Successful in 45s
Release / Test backend (push) Successful in 1m29s
CI / Docker / ui (pull_request) Successful in 1m28s
Release / Docker / backend (push) Successful in 3m14s
CI / Test backend (pull_request) Failing after 42s
CI / Docker / backend (pull_request) Has been skipped
CI / Docker / runner (pull_request) Has been skipped
Release / Docker / caddy (push) Successful in 6m48s
Release / Docker / ui (push) Successful in 2m8s
Release / Docker / runner (push) Successful in 2m51s
Release / Upload source maps (push) Failing after 53s
Release / Gitea Release (push) Has been skipped
Introduce a Redis-backed Asynq task queue so the runner consumes TTS
jobs pushed by the backend instead of polling PocketBase.
- backend/internal/asynqqueue: Producer and Consumer wrappers
- backend/internal/runner: AsynqRunner mux, per-instance Prometheus
registry (fixes duplicate-collector panic in tests), redisConnOpt
- backend/internal/config: REDIS_ADDR / REDIS_PASSWORD env vars
- backend/cmd/{backend,runner}/main.go: wire Redis when env set; fall
back to legacy poll mode when unset
- Caddyfile: caddy-l4 TCP proxy for redis.libnovel.cc:6380 → homelab
- caddy/Dockerfile: add --with github.com/mholt/caddy-l4
- docker-compose.yml: Caddy exposes 6380, backend/runner get Redis env
- homelab/runner/docker-compose.yml: Redis sidecar, runner depends_on
- homelab/otel/grafana: Grafana dashboards (backend, catalogue, runner)
and alerting rules / contact-points provisioning
57 lines
2.0 KiB
Go
57 lines
2.0 KiB
Go
package asynqqueue
|
|
|
|
import (
|
|
"context"
|
|
"time"
|
|
|
|
"github.com/libnovel/backend/internal/domain"
|
|
"github.com/libnovel/backend/internal/taskqueue"
|
|
)
|
|
|
|
// Consumer wraps the PocketBase-backed Consumer for result write-back only.
|
|
//
|
|
// When using Asynq, the runner no longer polls for work — Asynq delivers
|
|
// tasks via the ServeMux handlers. The only Consumer operations the handlers
|
|
// need are:
|
|
// - FinishAudioTask / FinishScrapeTask — write result back to PocketBase
|
|
// - FailTask — mark PocketBase record as failed
|
|
//
|
|
// ClaimNextAudioTask, ClaimNextScrapeTask, HeartbeatTask, and ReapStaleTasks
|
|
// are all no-ops here because Asynq owns those responsibilities.
|
|
type Consumer struct {
|
|
pb taskqueue.Consumer // underlying PocketBase consumer (for write-back)
|
|
}
|
|
|
|
// NewConsumer wraps an existing PocketBase Consumer.
|
|
func NewConsumer(pb taskqueue.Consumer) *Consumer {
|
|
return &Consumer{pb: pb}
|
|
}
|
|
|
|
// ── Write-back (delegated to PocketBase) ──────────────────────────────────────
|
|
|
|
func (c *Consumer) FinishScrapeTask(ctx context.Context, id string, result domain.ScrapeResult) error {
|
|
return c.pb.FinishScrapeTask(ctx, id, result)
|
|
}
|
|
|
|
func (c *Consumer) FinishAudioTask(ctx context.Context, id string, result domain.AudioResult) error {
|
|
return c.pb.FinishAudioTask(ctx, id, result)
|
|
}
|
|
|
|
func (c *Consumer) FailTask(ctx context.Context, id, errMsg string) error {
|
|
return c.pb.FailTask(ctx, id, errMsg)
|
|
}
|
|
|
|
// ── No-ops (Asynq owns claiming / heartbeating / reaping) ───────────────────
|
|
|
|
func (c *Consumer) ClaimNextScrapeTask(_ context.Context, _ string) (domain.ScrapeTask, bool, error) {
|
|
return domain.ScrapeTask{}, false, nil
|
|
}
|
|
|
|
func (c *Consumer) ClaimNextAudioTask(_ context.Context, _ string) (domain.AudioTask, bool, error) {
|
|
return domain.AudioTask{}, false, nil
|
|
}
|
|
|
|
func (c *Consumer) HeartbeatTask(_ context.Context, _ string) error { return nil }
|
|
|
|
func (c *Consumer) ReapStaleTasks(_ context.Context, _ time.Duration) (int, error) { return 0, nil }
|