Expand description
§CJA — Cron, Jobs and Axum
ALPHA: CJA is under active development. APIs may change between releases.
A Rust web framework that combines background job processing, cron scheduling, and an HTTP server built on Axum.
§Overview
CJA provides three integrated subsystems:
jobs— PostgreSQL-backed background job queue with automatic retries, exponential backoff, dead letter queue, and graceful shutdowncron— Scheduled task execution with both interval-based and cron expression scheduling, timezone supportserver— HTTP server on Axum with encrypted cookie management, database-backed sessions, and zero-downtime reload via systemfd
All three share a single app_state::AppState and coordinate shutdown via
tokio_util::sync::CancellationToken.
§Getting Started
Add CJA to your project:
cargo add cja§1. Implement AppState
use cja::app_state::AppState;
use cja::server::cookies::CookieKey;
#[derive(Clone)]
struct MyAppState {
db: sqlx::PgPool,
cookie_key: CookieKey,
}
impl AppState for MyAppState {
fn version(&self) -> &str { "1.0.0" }
fn db(&self) -> &sqlx::PgPool { &self.db }
fn cookie_key(&self) -> &CookieKey { &self.cookie_key }
}app_state::AppState is cloned per request — wrap expensive resources in Arc.
§2. Set Up the Database
CJA requires PostgreSQL. Set DATABASE_URL and run migrations on startup:
ⓘ
let pool = sqlx::postgres::PgPoolOptions::new()
.max_connections(5)
.connect(&std::env::var("DATABASE_URL")?)
.await?;
// Run CJA's built-in migrations (jobs, crons, sessions, dead_letter_jobs)
cja::db::run_migrations(&pool).await?;§3. Wire Up main()
ⓘ
use cja::setup::{setup_sentry, setup_tracing};
use cja::tasks::{ShutdownBudget, Supervisor};
fn main() -> cja::Result<()> {
let _sentry_guard = setup_sentry();
tokio::runtime::Builder::new_multi_thread()
.enable_all()
.build()?
.block_on(run())
}
async fn run() -> cja::Result<()> {
// Keep the Eyes handle alive for graceful shutdown
let _eyes_handle = setup_tracing("my-app")?;
let app_state = MyAppState { /* ... */ };
// Registers SIGTERM/SIGINT and owns the shutdown sequence
let mut supervisor = Supervisor::new(ShutdownBudget::from_env())?;
let shutdown = supervisor.shutdown_token();
// HTTP server: stops accepting connections when shutdown starts
supervisor.spawn("server",
cja::server::run_server_until(
routes(app_state.clone()),
shutdown.clone().cancelled_owned(),
)
);
// Job worker: stops claiming jobs, drains the in-flight one, then
// releases its lock so another instance picks it up immediately
supervisor.spawn("jobs",
cja::jobs::worker::job_worker(
app_state.clone(),
Jobs, // your job registry (see jobs module docs)
std::time::Duration::from_secs(60),
cja::jobs::DEFAULT_MAX_RETRIES,
shutdown.clone(),
Default::default(),
)
);
// Returns Ok after a signal, Err if a task died on its own
supervisor.run().await
}See crates/cja.app/ in the repository for a complete working example.
§Configuration
| Variable | Required | Default | Description |
|---|---|---|---|
DATABASE_URL | Yes | — | PostgreSQL connection string |
PORT | No | 3000 | HTTP server port |
COOKIE_KEY | No | Auto-generated | Base64-encoded 64-byte cookie encryption key. Must be set in production for session persistence across restarts. |
SERVER_DISABLED | No | false | Set to "true" to skip starting the HTTP server |
JOBS_DISABLED | No | false | Set to "true" to skip starting the job worker |
CRON_DISABLED | No | false | Set to "true" to skip starting the cron worker |
RUST_LOG | No | info | Tracing filter directive |
CJA_SHUTDOWN_JOB_DRAIN_SECS | No | 2 | How long an in-flight job may run after a shutdown signal before it is dropped and unlocked |
CJA_SHUTDOWN_EXIT_GRACE_SECS | No | 2 | Extra time for tasks to exit after the job drain. Drain + grace must fit the platform’s kill window (Cloud Run 10s fixed, Fly 5s default) |
JSON_LOGS | No | — | If set, outputs JSON logs instead of tree format |
SENTRY_DSN | No | — | Enables Sentry error tracking |
HONEYCOMB_API_KEY | No | — | Enables OpenTelemetry export to Honeycomb |
EYES_ORG_ID | No | — | Eyes tracing org ID (both ORG_ID and APP_ID required) |
EYES_APP_ID | No | — | Eyes tracing app ID |
EYES_URL | No | https://eyes.coreyja.com | Eyes server URL |
§Feature Flags
[features]
default = ["cron", "jobs"]
cron = ["jobs"] # cron depends on jobs
jobs = []
testing = [] # enables mock OAuth for testsDisabling jobs at compile time also disables cron.
§Re-exports
CJA re-exports key dependencies so you don’t need to add them directly:
use cja::eyes_subscriber;
use cja::sqlx;
use cja::uuid;
use cja::color_eyre;
use cja::maud;
use cja::chrono;
use cja::chrono_tz;Re-exports§
pub use eyes_subscriber;pub use sqlx;pub use uuid;pub use color_eyre;pub use maud;pub use chrono;pub use chrono_tz;
Modules§
- app_
state - assets
- Content-hashed assets embedded by
cja-build. - cron
- Scheduled task execution with interval-based and cron expression scheduling.
- db
- Database utilities and migration management.
- eyes_
manifest - Boot-time app manifest emission to Eyes.
- jobs
- Background job processing with
PostgreSQLpersistence. - server
- HTTP server built on Axum with cookies, sessions, and zero-downtime reload.
- setup
- Application setup: Sentry error tracking and tracing/observability initialization.
- tasks
Macros§
- impl_
job_ registry - A macro for implementing a job registry that handles job dispatch.
- include_
assets - Define a named module with this crate’s generated manifest and scoped URL lookup.
Type Aliases§
- Result
- type alias for
Result<T, Report>