Skip to content

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

1 Commit
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

TaskForge

A distributed job scheduling and execution engine — submit background jobs over REST, and TaskForge handles queuing, scheduled/delayed execution, automatic retries with exponential backoff, dead-lettering, and idempotent resubmission.

Think of it as a small, self-hosted version of what sits behind "send this email in 10 minutes," "retry this payment webhook 5 times," or "process this file asynchronously" at companies like DoorDash, Stripe, or Uber.

Why this exists

Most "todo API" portfolio projects don't demonstrate distributed-systems thinking. TaskForge is built around three problems every backend engineer runs into at scale:

Problem How TaskForge solves it
A client retries a request after a timeout — do you process the job twice? Idempotency keys: resubmitting the same key returns the existing job instead of creating a duplicate
A job fails — do you lose it, or hammer the downstream service instantly? Exponential backoff retries (2s → 4s → 8s → 16s...) via a SCHEDULED state, capped by maxAttempts
A job keeps failing — does it retry forever? Dead-letter queue: after max attempts, the job is marked DEAD_LETTERED and published to a DLQ topic for inspection
Two worker instances read the same message during a Kafka rebalance Distributed lock via Redis (SET NX PX) so only one worker executes a given job

Architecture

Client --REST--> [Job API] --save--> [Postgres]
                     |
                     v
              [Kafka: taskforge.jobs] --> [Worker(s)] --Redis lock--> execute()
                     ^                          |
                     |                     on failure
              [Scheduler: promotes due      (retry or)
               SCHEDULED jobs every 5s]         v
                                        [Kafka: taskforge.jobs.dlq]
  • API layer (Spring Boot REST) — accepts job submissions, persists to Postgres, publishes job IDs to Kafka.
  • Queue (Kafka) — decouples submission from execution; keyed by job ID so retries preserve per-job ordering.
  • Workers (Kafka consumer group) — pull jobs, acquire a short-lived Redis lock, execute, and update status.
  • Scheduler — a lightweight @Scheduled poller that promotes SCHEDULED jobs (both delayed jobs and retry backoffs) back onto the queue when they're due.

Tech stack

Java 17 · Spring Boot 3 · Spring Kafka · Spring Data JPA · PostgreSQL · Redis · Docker Compose

Running locally

Requires Docker and Docker Compose.

git clone <your-repo-url>
cd taskforge
docker-compose up --build

This starts Postgres, Redis, Zookeeper, Kafka, and the TaskForge app on localhost:8080.

API

Submit a job

curl -X POST http://localhost:8080/api/jobs \
  -H "Content-Type: application/json" \
  -d '{
        "idempotencyKey": "order-42-confirmation-email",
        "type": "flaky-send-email",
        "payload": "{\"to\":\"user@example.com\"}",
        "priority": 5,
        "maxAttempts": 5
      }'

Check job status

curl http://localhost:8080/api/jobs/{id}

List jobs by status

curl "http://localhost:8080/api/jobs?status=DEAD_LETTERED"

Job types prefixed with flaky- intentionally fail ~40% of the time (see JobConsumer.execute) so you can watch the retry → backoff → DLQ flow without wiring up a real downstream integration. Swap this method out for actual work (send an email, call a webhook, generate a report) when adapting this for a real use case.

Deployment guide (free-tier friendly)

TaskForge needs Postgres, Redis, and Kafka, so a pure serverless deploy isn't the easiest path — the cleanest free/cheap options:

  1. Render — deploy the taskforge service as a Docker Web Service. Add a Render Postgres instance (free tier) and a Render Redis/Key-Value instance.
  2. Kafka: self-hosting Kafka on a free tier is painful. Easiest path is Confluent Cloud (free trial credits) — just point KAFKA_BOOTSTRAP_SERVERS (and add SASL config to application.yml) at your Confluent cluster instead of the local broker.
  3. Alternatively, for a resume demo, run the whole docker-compose.yml stack on a small VM (e.g. an AWS EC2 free-tier instance or an Oracle Cloud free VM) — simplest to set up end-to-end since it mirrors local dev exactly.

Set these environment variables wherever you deploy: DB_URL, DB_USER, DB_PASSWORD, REDIS_HOST, REDIS_PORT, KAFKA_BOOTSTRAP_SERVERS.

License

MIT — free to use as a reference or starting point.

About

Distributed job scheduling engine with idempotent submission, exponential backoff retries, and dead-letter queues. Built with Java, Spring Boot, Kafka, Redis, and PostgreSQL.

Topics

Resources

Stars

1 star

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages