Skip to content

A concurrent job queue system built in Java w/ Spring Boot, retry logic, configurable worker pools, RESTful API endpoints. Designed as a learning project to explore idiomatic Java concurrency (ExecutorService) and the Producer–Consumer model. Inspired by real job queue systems such as Celery. This is a Java remake of my GoQueue project

Notifications You must be signed in to change notification settings

timan-z/SpringQueue

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

15 Commits
 
 
 
 
 
 
 
 

Repository files navigation

SpringQueue

Author: Timan Zheng
Date: 10/2025
Description: A Concurrent Producer–Consumer Job Queue in Java (Spring Boot)

Disclaimer — this system exists very much as a transitionary project between two other systems I've built (something that is expanded on in the Project Legacy section). This documentation is written under that context, so it may be confusing at points. If you're genuinely curious about this project and its "siblings", I would recommend skimming through my GoQueue documentation first.

Overview

SpringQueue is a concurrent job queue implemented in Java using Spring Boot, built primarily to re-learn Java, Spring Boot, explore idiomatic Java concurrency, and understand how a Producer–Consumer system translates from Go into the Java ecosystem. That is:

  • This project is a one-to-one translation of GoQueue — a prior parallel Producer-Consumer system that relied entirely on Go's concurrency primitives (e.g., goroutines, channels, and mutexes) — with adjustments made to make this project more idiomatically Java.

It features retry logic, configurable worker pools, RESTful API endpoints, and a cosmetic React-TypeScript dashboard for monitoring and control. Like its predecessor GoQueue, this is a learning-focused project, loosely modeled after real-world background job systems such as Celery and Sidekiq, capturing their internal mechanics rather than infrastructure.

  • Really, I started working on this project while I was in the recruitment process for a tech consultancy in Toronto that was Java and Spring Boot-oriented. (The purpose was learn Spring Boot but also to reacquaint myself with Java).

At its core, as stated, SpringQueue is a direct implementation of the Producer–Consumer model:

  • A Producer accepts incoming jobs via a REST API.

  • A managed Worker Pool (Consumers) processes jobs concurrently.

  • A central Queue Service coordinates execution and tracks job state.

Project Legacy

SpringQueue exists as part of a lineage of sorts, relating directly to two other systems that I've built:

  1. GoQueue — this but in Go (its predecessor)
  2. SpringQueuePro this but advanced and production-grade

As aforementioned, I built SpringQueue primarily for the sake of quickly learning Spring Boot by recreating one of my earlier projects (that being GoQueue).

In my GoQueue documentation, I described that project as a conceptual prototype that affirmed a concrete design that I was able to then translate to SpringQueue. And that would ultimately build towards SpringQueuePro, which is — as the name implies — the advanced production-grade evolution of SpringQueue.

  • SpringQueuePro is one of my portfolio flagship projects and a system that I intend to continuously evolve and adjust the architecture of. (Its own projected evolution CloudQueue is something I intend to work on in the upcoming months).

While I struggle to call SpringQueue the successor of GoQueue, I would describe it more a sibling system. Granted, it is 100% a bridge between my GoQueue and SpringQueuePro projects.

  • GoQueue explored concurrency using goroutines, channels, and mutexes.
  • SpringQueue reimplements the same system using idiomatic Java primitives, most notably ExecutorService.
  • SpringQueuePro later evolved this design into a production-grade, distributed system with persistence, Redis, JWT security, metrics, and CI/CD.

SpringQueue exists as the translation and refactor layer — the project where concurrency logic stayed the same, but the language forced better architectural discipline.


Core Concepts

Producer–Consumer Model

SpringQueue follows the classic Producer–Consumer pattern:

HTTP Client → Producer → Executor Queue → Workers

Key differences from GoQueue:

  • Instead of buffered channels, Java uses a managed executor queue.
  • Instead of goroutine loops, work is submitted as Runnable tasks.
  • Backpressure and scheduling are handled by the ExecutorService, not language primitives.

Architecture

Backend Components

1. Queue Service

The QueueService is the central coordination structure.

Responsibilities

  • Tracks all jobs in a thread-safe ConcurrentHashMap
  • Submits tasks to the worker pool
  • Handles retries and lifecycle transitions
  • Serves as the boundary between API and execution

Unlike GoQueue’s explicit channel ownership, Java’s executor abstracts queueing and scheduling.


2. Producer (REST API)

The Producer layer is implemented as a Spring REST Controller.

Key responsibilities

  • Accept job creation requests
  • Validate and enqueue tasks
  • Expose job inspection and management endpoints
  • Act as the system’s entry point

Example endpoints

POST   /api/enqueue
GET    /api/jobs
GET    /api/jobs/{id}
POST   /api/jobs/{id}/retry
DELETE /api/jobs/{id}
POST   /api/clear

3. Workers (Consumers)

Workers are implemented as stateless Runnable tasks executed by a shared ExecutorService.

Instead of long-lived goroutines:

  • Each task execution is isolated
  • Workers are reused by the thread pool
  • Failures do not terminate threads

This aligns with production Java concurrency practices.


Concurrency Model

SpringQueue intentionally uses idiomatic Java concurrency:

Concept Usage
ExecutorService Managed worker pool
Runnable Unit of work
ConcurrentHashMap Thread-safe job state
@PreDestroy Graceful shutdown

This design provides:

  • Safe concurrent execution
  • Controlled resource usage
  • Predictable lifecycle management

Program Flow & Backpressure

High-Level Runtime Flow

At runtime, SpringQueue operates as a continuously running Producer–Consumer system:

  1. Application boots and initializes:

    • QueueService
    • Fixed ExecutorService worker pool
  2. Workers idle inside the executor.

  3. Producers submit jobs via REST endpoints.

  4. Tasks are queued internally by the executor.

  5. Workers process tasks concurrently.

  6. Failed tasks may be retried or permanently failed.


How Backpressure Works in SpringQueue

Unlike GoQueue, SpringQueue does not rely on blocking semantics at the language level.

Backpressure is provided by:

  • The executor’s internal task queue
  • The fixed size of the worker pool
  • JVM thread scheduling

Key Mechanics

  • Tasks are submitted via executor.submit(...)

  • If workers are busy:

    • Tasks queue internally
  • If the queue grows:

    • Execution slows naturally
  • No busy polling or CPU spinning occurs

This mirrors how real Java services handle load.

In contrast to GoQueue’s implicit channel blocking, SpringQueue relies on managed queuing, which trades transparency for control.


Task Lifecycle

Each task in SpringQueue follows the same conceptual lifecycle as GoQueue.

1. Task Creation (Producer)

  • HTTP request received

  • Task object created with:

    • ID
    • Type
    • Initial status (QUEUED)
    • Retry metadata
  • Stored in ConcurrentHashMap

  • Submitted to ExecutorService


2. Task Queued

  • Task resides in executor queue
  • Waits until a worker thread becomes available
  • No CPU resources consumed

3. Task Claimed

  • Executor assigns task to a worker thread
  • Task marked IN_PROGRESS
  • Processing begins

4. Task Execution

  • Execution simulated via Thread.sleep
  • Behavior varies by task type

5. Completion or Failure

On success

  • Task marked COMPLETED

On failure

  • Retry count incremented

  • If retries remain:

    • Task is re-enqueued as a new execution
  • Otherwise:

    • Task marked FAILED

6. Terminal State

Once COMPLETED or FAILED, tasks are immutable.


Worker Lifecycle (Mermaid Diagram)

stateDiagram-v2
    [*] --> WorkerIdle

    WorkerIdle --> ProcessingTask : executor assigns task
    ProcessingTask --> TaskCompleted : success
    ProcessingTask --> TaskFailed : error

    TaskFailed --> RetryingTask : retries remaining
    RetryingTask --> WorkerIdle : resubmitted

    TaskFailed --> PermanentFailure : retries exhausted
    PermanentFailure --> WorkerIdle

    TaskCompleted --> WorkerIdle
Loading

Key Observations

  • Workers are stateless
  • Threads are reused
  • Failures do not kill workers
  • Retry logic is task-scoped

Features

  • Concurrent job processing via ExecutorService
  • Producer–Consumer architecture
  • Configurable retry logic
  • Multiple simulated job types
  • Thread-safe in-memory state
  • Graceful shutdown with @PreDestroy
  • RESTful API
  • Cosmetic React-TypeScript dashboard

Skills & Concepts Demonstrated

  • Java Concurrency – ExecutorService, Runnable, thread safety
  • Spring Boot – REST controllers, DI, lifecycle hooks
  • Producer–Consumer Pattern
  • Task Lifecycle Management
  • Retry Strategies
  • Full-Stack Integration

Design Goals

  • Re-learn Java through concurrency
  • Learn Spring Boot (e.g., making a REST API w/ it)
  • Translate Go concurrency into Java idioms
  • Build a clean stepping stone toward SpringQueuePro
  • Emphasize correctness over features

Limitations

This is intentionally not production-grade:

  • No persistence
  • No distribution
  • No authentication
  • No metrics

All of these are addressed in SpringQueuePro.


Why This Project Matters

From an engineering perspective, SpringQueue demonstrates:

  • Cross-language concurrency translation
  • Idiomatic Java design choices
  • Clean architectural evolution

In a way, SpringQueue represents the moment the design “locked in” — the point where GoQueue proved the concept, and SpringQueue proved it could survive in a production-style language and framework.


About

A concurrent job queue system built in Java w/ Spring Boot, retry logic, configurable worker pools, RESTful API endpoints. Designed as a learning project to explore idiomatic Java concurrency (ExecutorService) and the Producer–Consumer model. Inspired by real job queue systems such as Celery. This is a Java remake of my GoQueue project

Topics

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published