Architecture of the S3-Based Distributed Work Queue in OLMocr
The S3-based distributed work queue system in the allenai/olmocr repository implements a lightweight, serverless coordination mechanism using only Amazon S3 object storage, enabling fault-tolerant parallel processing without external databases or message brokers.
The olmocr project uses a custom S3-based distributed work queue to coordinate OCR workloads across multiple workers. Unlike traditional job queues that require Redis, RabbitMQ, or PostgreSQL, this architecture stores all state—pending work, active locks, and completion markers—as objects in a shared S3 bucket. By leveraging atomic S3 operations and object metadata, the system provides strong consistency guarantees while eliminating infrastructure dependencies.
Core Components of the Work Queue System
The Backend Abstraction Layer
Located in olmocr/work_queue.py, the abstract Backend class defines the storage-agnostic API required by the queue. The concrete S3Backend implementation uses boto3 to map queue operations to native S3 calls, handling object creation, deletion, and metadata queries through simple HTTP operations.
The WorkQueue Orchestrator
The WorkQueue class manages the lifecycle of distributed tasks. It handles queue population via populate_queue(), distributes work through get_work(), and tracks completion using mark_done(). This orchestrator maintains an in-memory queue of WorkItem objects while delegating persistence to the backend.
S3 Data Layout and Object Structure
Within a configured workspace_path (e.g., s3://bucket/olmocr-workspace), the system organizes state into three specific locations:
workspace_path/
├── work_index_list.csv.zstd # Compressed CSV mapping hash → file paths
├── worker_locks/ # Active worker locks: worker_<hash>.lock
└── done_flags/ # Completion markers: done_<hash>.flag
The directory constants are defined in work_queue.py:
WORKER_LOCKS_DIR = "worker_locks"
DONE_FLAGS_DIR = "done_flags"
How the Distributed Queue Works
Queue Population and Work Group Indexing
The populate_queue() method in WorkQueue processes input file lists by grouping them into batches (configurable via items_per_group). It computes a deterministic SHA-1 hash for each group using _compute_workgroup_hash(), then appends these entries to work_index_list.csv.zstd. The helper functions in olmocr/s3_utils.py handle zstd compression and comma-escaping for paths containing literal commas.
Worker Coordination and Lock Acquisition
When a worker calls get_work(), the system executes a distributed consensus protocol:
- Completion Check: Queries
backend.is_completed()(a HEAD request for the corresponding file indone_flags/) - Lock Verification: Checks
backend.is_worker_lock_taken()by examining theLastModifiedtimestamp ofworker_<hash>.lockagainst a 30-minute timeout - Lock Acquisition: Creates an empty object via
backend.create_worker_lock()if the existing lock is stale or absent
Marking Completion and Releasing Locks
After processing a work group, the worker invokes mark_done(), which calls backend.create_done_flag() to create an empty object in done_flags/. The worker then releases its claim via backend.delete_worker_lock(). This atomic write serves as the immutable source of truth for work completion across all nodes.
Distributed Guarantees and Fault Tolerance
The architecture provides several critical guarantees for distributed systems:
- Mutual Exclusion: Only one worker can hold a valid lock for a work group due to S3's atomic PUT semantics
- Fault Tolerance: Crashed workers leave stale locks that are automatically reclaimed after the timeout period based on
LastModifiedmetadata - Idempotency: Completion markers are idempotent; duplicate flag creation is safe and prevents double-processing
- Scalability: No central coordinator bottleneck exists; workers communicate only through S3 object operations
Implementation Example
Here is a complete example showing how to initialize the queue and run a worker loop:
import boto3
from olmocr.work_queue import S3Backend, WorkQueue
# Initialize the S3 backend
s3_client = boto3.client("s3")
backend = S3Backend(s3_client, "s3://my-bucket/olmocr-workspace")
queue = WorkQueue(backend)
# Populate the queue (run once before workers start)
input_paths = [
"s3://my-bucket/input/doc1.pdf",
"s3://my-bucket/input/doc2.pdf",
# ...
]
await queue.populate_queue(input_paths, items_per_group=5)
# Worker processing loop
async def worker_loop():
await queue.initialize_queue()
while True:
item = await queue.get_work()
if item is None:
break # No more work available
# Process files in item.work_paths
for pdf_path in item.work_paths:
# Perform OCR processing here
pass
# Mark completion and release lock
await queue.mark_done(item)
# Run multiple workers across processes or machines
Summary
- The S3-based distributed work queue in
olmocruses object storage as the sole coordination mechanism, requiring no external databases - State persists in three S3 locations: a zstd-compressed CSV index, a
worker_locks/directory for active claims, and adone_flags/directory for completion tracking - The
S3Backendclass inolmocr/work_queue.pyimplements distributed locking via atomic empty-object creation with timestamp-based stale detection - Workers acquire exclusive access using
create_worker_lock()and finalize work viacreate_done_flag(), ensuring exactly-once processing semantics - Failed workers are handled automatically through lock timeouts, making the system resilient to individual node failures
Frequently Asked Questions
How does the system handle worker crashes?
If a worker crashes mid-processing, its lock object remains in the worker_locks/ directory indefinitely. Other workers detect this stale lock by comparing the object's LastModified timestamp against the configured timeout (default 30 minutes). Once expired, a new worker can safely acquire the lock via create_worker_lock() and process the work group.
What prevents two workers from processing the same work group?
The S3Backend.is_worker_lock_taken() method checks for the existence and freshness of lock objects before granting work. Since S3 PUT operations are atomic, only one worker can successfully create worker_<hash>.lock. Subsequent attempts fail until the lock is deleted via delete_worker_lock() or becomes stale after the timeout period.
Can this architecture work with S3-compatible storage like MinIO or Wasabi?
Yes. The S3Backend class uses standard boto3 operations. As long as the storage provider supports atomic PUT operations and object metadata queries (HEAD requests), the distributed queue functions correctly. The system has been tested against Amazon S3 and compatible object stores.
How are work groups defined and hashed?
The WorkQueue.populate_queue() method groups input paths into batches of size items_per_group. It computes a SHA-1 hash of the sorted paths via _compute_workgroup_hash() to create a deterministic, unique identifier for each batch. This hash appears in the CSV index and forms the basis for lock filenames (worker_<hash>.lock) and completion flags (done_<hash>.flag).
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →