Parallelism & Concurrency

JoobQ provides robust parallelism and concurrency mechanisms to maximize the efficiency of job processing. Understanding and configuring parallelism and concurrency in JoobQ helps you make the most out of available resources, reducing job execution time and increasing system throughput. This section explains how parallelism and concurrency work in JoobQ and how to effectively manage them.

Overview of Parallelism and Concurrency

Parallelism refers to executing multiple tasks simultaneously across multiple processors or cores, while concurrency refers to managing multiple tasks at the same time, even if they aren't necessarily executing simultaneously. JoobQ uses both approaches to improve job throughput by allowing multiple workers to process different jobs concurrently, taking advantage of multicore systems.

JoobQ provides these mechanisms through the use of workers and queues. Each queue can have multiple workers assigned, and each worker runs in its own fiber, enabling concurrent job execution.

Configuring Parallelism with Workers

The key to leveraging parallelism in JoobQ lies in configuring the number of workers assigned to each queue. More workers mean more jobs can be processed in parallel, leading to increased throughput.

Assigning Workers to a Queue

When creating a queue in JoobQ, you can specify the number of workers that should process jobs from that queue.

queue = JoobQ::Queue(ExampleJob).new("concurrent_queue", total_workers: 5)
  • total_workers: Defines the number of worker threads that will concurrently fetch and execute jobs from the queue.

In the above example, 5 workers are created for the concurrent_queue, allowing 5 jobs to be processed concurrently.

Worker Management

Each worker is responsible for fetching jobs from the queue, executing them, and handling retries or failures. Workers run in separate fibers, enabling efficient concurrency within the application.

Worker Lifecycle

  • Start Workers: Workers are started when the queue is started by calling the start method.

    queue.start

    This method will initialize all the workers and begin processing jobs concurrently.

  • Stop Workers: Workers can be stopped gracefully by calling the stop! method, which signals all active workers to terminate.

    queue.stop!

Example Usage of Concurrency and Parallelism

Below is an example of configuring a queue with multiple workers to achieve concurrency:

require "joobq"

# Define a job
struct ExampleJob
  include JoobQ::Job
  property x : Int32

  def initialize(@x : Int32)
  end

  def perform
    puts "Performing job with x = #{x}"
  end
end

# Configure the queue with 5 workers for concurrency
queue = JoobQ::Queue(ExampleJob).new("concurrent_queue", total_workers: 5)

# Add jobs to the queue
20.times do |i|
  queue.add(ExampleJob.new(x: i).to_json)
end

# Start the queue, enabling concurrent job processing
queue.start

In this example:

  • total_workers: 5 indicates that there are 5 workers processing jobs from the queue concurrently, which allows multiple jobs to be executed in parallel.

  • queue.add adds 20 jobs to the queue, which will be distributed among the available workers for concurrent execution.

Balancing Parallelism and Resource Usage

While increasing the number of workers can improve job throughput, it is important to balance parallelism with resource usage to avoid overwhelming the system.

Key Considerations

  • CPU and Memory: More workers mean higher CPU and memory usage. Ensure your system has enough resources to handle the number of workers you configure.

  • Job Complexity: Simple jobs may benefit from a higher number of workers, while complex, CPU-intensive jobs may require fewer workers to avoid overloading the system.

  • I/O Bound Jobs: If jobs are I/O bound (e.g., making API calls or reading from a database), adding more workers can help improve throughput, as the workers can continue processing other jobs while waiting for I/O to complete.

Monitoring Worker Performance

Monitoring worker activity is crucial for understanding how well your concurrency settings are working. JoobQ provides metrics that help you track worker performance, such as:

  • Active Workers: Number of workers actively processing jobs at a given time.

  • Job Throughput: Number of jobs completed per second by all workers.

  • Worker Utilization: Indicates how effectively each worker is being utilized. High utilization suggests good resource usage, while low utilization may indicate too many idle workers.

You can retrieve these metrics via the JoobQ metrics API or by calling the queue.info method:

queue_info = queue.info
puts queue_info.to_json

The output will include details about the number of active workers, job completion rates, and other relevant metrics.

Best Practices for Parallelism and Concurrency

  • Start with a Conservative Number of Workers: Begin with a modest number of workers and gradually increase until you find the optimal balance between throughput and resource consumption.

  • Monitor System Resources: Use monitoring tools to keep an eye on CPU and memory usage to ensure that adding more workers does not degrade overall system performance.

  • Use Metrics to Tune Settings: Leverage the metrics provided by JoobQ to understand worker activity and tune the number of workers accordingly.

  • Different Queues for Different Loads: If your system processes different types of jobs (e.g., CPU-intensive vs. I/O-bound), consider creating separate queues with different concurrency settings tailored to the job types.


Parallelism and concurrency are crucial for maximizing the efficiency of job processing in JoobQ. By configuring the appropriate number of workers and understanding how to monitor and balance resource usage, you can ensure that your system processes jobs effectively, without overwhelming system resources.

Last updated