mini-lambda-rust is a small serverless-style job execution platform built with Rust, Tokio, Axum, and SQLite. It exposes an HTTP API for submitting jobs, persists metadata in SQLite, processes work asynchronously with a Tokio worker pool, and tracks retries, failures, and timeouts with structured logs.
The project is intentionally scoped as a local MVP, but the structure is production-oriented:
- Axum HTTP API
- Tokio async runtime
- SQLx + SQLite persistence
- In-memory Tokio
mpscqueue - Configurable worker pool
- Structured tracing logs
- JSON error responses
- Graceful shutdown with queue draining
- Basic integration tests
POST /jobssubmits a new job and queues it for background execution.GET /jobs/:idfetches one job with current metadata and status.GET /jobslists jobs ordered by creation time descending.POST /jobs/:id/retryrequeues failed or timed-out jobs.DELETE /jobs/:iddeletes a job record.GET /healthchecks API and database availability.- Failed jobs can be retried automatically up to
max_retries. - Timed-out jobs are marked
timeoutand can be retried manually.
The codebase is split into small modules:
src/main.rs: binary entrypoint, tracing setup, HTTP server, graceful shutdownsrc/lib.rs: runtime bootstrapsrc/api/: Axum router and handlerssrc/models/: API and domain modelssrc/db/: SQLite connection, schema initialization, and job queriessrc/scheduler/: job submission and retry orchestrationsrc/worker/: worker pool and simulated job executionsrc/state/: shared application statesrc/error/: centralized error handling and JSON responses
Runtime flow:
- A client submits a job to
POST /jobs. - The API validates the request, stores the job in SQLite, and pushes the job ID onto a Tokio
mpscqueue. - A worker picks up the job, marks it
running, and simulates async execution. - The worker updates the database to
success,failed, ortimeout. - If a job fails and
retry_count < max_retries, it is requeued automatically.
Each job stores:
idpayloadstatuscreated_atstarted_atfinished_atretry_countmax_retriestimeout_secondsresulterror_message
Statuses:
pendingrunningsuccessfailedtimeout
The worker uses a small deterministic simulation layer so the service is easy to demo locally.
If the submitted payload is a JSON object, these optional keys are recognized:
duration_ms: simulated processing duration, defaults to750should_fail: iftrue, the worker returns an errorresult: custom success messageerror_message: custom failure message
Example payloads:
{
"task": "resize-image",
"duration_ms": 250,
"result": "image resized successfully"
}{
"task": "failing-job",
"duration_ms": 50,
"should_fail": true,
"error_message": "simulated downstream failure"
}To trigger a timeout, submit a duration_ms larger than the configured timeout_seconds.
Environment variables:
SERVER_ADDR: HTTP bind address, default127.0.0.1:3000DATABASE_URL: SQLite connection string, defaultsqlite://mini_lambda.dbWORKER_COUNT: number of workers, default4DEFAULT_TIMEOUT_SECONDS: fallback timeout for jobs, default30DEFAULT_MAX_RETRIES: fallback retry count, default3
The application initializes the SQLite schema automatically at startup with CREATE TABLE IF NOT EXISTS statements. No external migration command is required for the MVP.
The jobs table includes indexes on:
created_atstatus
- Rust stable
- SQLite support through SQLx
cargo runOr with custom settings:
SERVER_ADDR=127.0.0.1:4000 \
DATABASE_URL=sqlite://mini_lambda.db \
WORKER_COUNT=4 \
DEFAULT_TIMEOUT_SECONDS=10 \
DEFAULT_MAX_RETRIES=2 \
cargo runcargo testcurl http://127.0.0.1:3000/healthcurl -X POST http://127.0.0.1:3000/jobs \
-H "Content-Type: application/json" \
-d '{
"payload": {
"task": "send-email",
"duration_ms": 200,
"result": "email delivered"
},
"timeout_seconds": 5,
"max_retries": 1
}'Example response:
{
"job_id": "8ee65756-65ca-48db-bf59-f1d8271f18ce",
"status": "pending"
}curl http://127.0.0.1:3000/jobs/8ee65756-65ca-48db-bf59-f1d8271f18ceExample response:
{
"id": "8ee65756-65ca-48db-bf59-f1d8271f18ce",
"payload": {
"task": "send-email",
"duration_ms": 200,
"result": "email delivered"
},
"status": "success",
"created_at": "2026-03-13T04:00:00.000000Z",
"started_at": "2026-03-13T04:00:00.010000Z",
"finished_at": "2026-03-13T04:00:00.210000Z",
"retry_count": 0,
"max_retries": 1,
"timeout_seconds": 5,
"result": "email delivered",
"error_message": null
}curl http://127.0.0.1:3000/jobscurl -X POST http://127.0.0.1:3000/jobs \
-H "Content-Type: application/json" \
-d '{
"payload": {
"task": "failing-job",
"duration_ms": 50,
"should_fail": true,
"error_message": "simulated downstream failure"
},
"timeout_seconds": 5,
"max_retries": 2
}'curl -X POST http://127.0.0.1:3000/jobs/8ee65756-65ca-48db-bf59-f1d8271f18ce/retrycurl -X DELETE http://127.0.0.1:3000/jobs/8ee65756-65ca-48db-bf59-f1d8271f18ceThe service logs key lifecycle events with tracing:
- job submitted
- job started
- job succeeded
- job failed
- job timed out
- job retried
Set RUST_LOG to adjust verbosity:
RUST_LOG=mini_lambda_rust=debug cargo runErrors are returned as JSON:
{
"error": {
"code": "validation_error",
"message": "payload cannot be null"
}
}- durable queueing so pending work survives process restarts
- per-job execution history instead of single-attempt timestamps
- authentication and rate limiting
- richer job payload contracts
- metrics and OpenTelemetry export
- SQLx migrations directory for schema evolution
- job cancellation support
- dead-letter queue for repeated failures