Background Tasks & Scheduling

Queue-backed background tasks and cron-style scheduled jobs in Dracory applications.

Background Tasks & Scheduling

Dracory runs background work through two complementary systems: a persistent task queue backed by the taskstore package, and a cron-style scheduler built on github.com/go-co-op/gocron.

Task Queue

Tasks are durable units of work stored in the database and executed by a worker pool. The queue only runs when the task store is enabled (taskStoreUsed = true in internal/config/stores_config.go).

On boot, tasks.RegisterTasks(app) (called from cmd/server/main.go) registers every handler, and a taskstore.TaskQueueRunner is started in cmd/server/background_processes.go with 10 workers, a 2-second poll interval, and a 2-minute unstuck timeout on the default queue.

Defining a Task

  1. Create a package under internal/tasks/<name>/ implementing taskstore.TaskHandlerInterface. Existing examples include hello_world, clean_up, email_otp, and stats.
  2. Add a compile-time safe alias constant in internal/tasks/constants/constants.go.
  3. Register the handler in internal/tasks/register_tasks.go so it is added to the task store at startup.

Enqueueing Tasks

Tasks can be enqueued from controllers or other code paths:

// Via the handler instance
_, err := email_otp.NewEmailOTPTask(app).Enqueue(nonce)

// Or by alias through the task store
_, err := app.GetTaskStore().TaskDefinitionEnqueueByAlias(ctx,
    taskstore.DefaultQueueName, constants.EmailOTPTaskAlias,
    map[string]any{"nonce": nonce})

Running Tasks from the CLI

Any task can be executed synchronously from the command line by alias:

go run ./cmd/server task HelloWorldTask

See the CLI Commands page for the full command list.

Scheduled Jobs

Recurring work is declared in internal/schedules/ using gocron. schedules.StartAsync(ctx, app) is launched as a background goroutine at boot and stops when the server context is cancelled:

func newScheduler(app app.AppInterface) *gocron.Scheduler {
    scheduler := gocron.NewScheduler(time.UTC)

    // Example: run a task every 20 minutes
    scheduler.Every(20).Minutes().Do(func() {
        scheduleCleanUpTask(app)
    })

    return scheduler
}

Each scheduled job lives in its own file (for example schedule_clean_up.go, schedule_blind_index_rebuild.go) and typically enqueues a task rather than doing heavy work inline, so failures are retried by the queue.

Other Background Processes

background_processes.go also starts store housekeeping goroutines when enabled:

  • Cache expiry sweeper (cache store)
  • Session expiry sweeper (session store)