Build a Priority Task Queue
Difficulty: Medium · Category: Task Scheduler · Languages: Java 17, Python 3.11, Go 1.22, Node.js 20, TypeScript 5.x
Priority Task Queue
Background
Task queues are fundamental building blocks of distributed systems. They decouple producers from consumers, smooth out traffic spikes, and enable reliable background processing. A priority-based task queue ensures that the most important work is processed first — critical for systems where not all tasks are equal (e.g., payment processing before analytics).
Adding retry semantics and a dead-letter queue makes the system resilient: transient failures are automatically retried, and permanently failing tasks are quarantined for manual investigation instead of being lost or retried forever.
Your Task
Implement the TaskQueueContract interface. Your implementation must:
- Persist all state — queue entries and task metadata must survive restarts.
- Dequeue tasks in descending priority order (highest first).
- Be thread-safe: concurrent dequeue calls must never return the same task to two consumers.
- Track the full task lifecycle: PENDING → PROCESSING → COMPLETED, or PENDING → PROCESSING → FAILED → (retry) → PENDING → ...
- Move tasks that exceed the configured max retries to a dead-letter queue.
Recommended infrastructure:
redis— fast key-value store with atomic operations ideal for queue semantics.
How It Works
The platform injects your chosen infrastructure SDK(s) via constructor. For example, if you select Redis:
public class MyTaskQueue implements TaskQueueContract {
private final RedisClient redis;
public MyTaskQueue(RedisClient redis) {
this.redis = redis;
}
// ... implement methods using redis.zadd(), redis.get(), etc.
}
See the SDK Docs tab for the full API reference of your chosen system.
Interface
public interface TaskQueueContract {
String enqueue(String queueName, String payload, int priority);
String dequeue(String queueName);
String getPayload(String taskId);
boolean acknowledge(String taskId);
boolean fail(String taskId, String errorMessage);
boolean retry(String taskId);
String getStatus(String taskId);
List<String> getDeadLetterQueue(String queueName);
int getQueueSize(String queueName);
void configureMaxRetries(String queueName, int maxRetries);
}
Method Details
| Method | Description |
|---|---|
enqueue | Adds a task with a payload and priority. Returns a unique task ID. |
dequeue | Atomically removes and returns the highest-priority task. Status → PROCESSING. Returns null if empty. |
getPayload | Returns the original payload for a task ID. |
acknowledge | Marks a PROCESSING task as COMPLETED. |
fail | Marks a PROCESSING task as FAILED with an error message. |
retry | Re-enqueues a FAILED task. If max retries exceeded, moves to DLQ. |
getStatus | Returns PENDING, PROCESSING, COMPLETED, FAILED, or DEAD_LETTER. |
getDeadLetterQueue | Lists task IDs in the DLQ for a queue. |
getQueueSize | Returns the number of PENDING tasks. |
configureMaxRetries | Sets the max retry count for a queue (default 3). |
Constraints
prioritymay be any integer (including negative)queueNameandpayloadare non-null, non-empty stringsmaxRetries≥ 0- Performance target: ≥ 8 000 ops/sec with p99 latency ≤ 5 ms
Examples
configureMaxRetries("jobs", 2)
t1 = enqueue("jobs", "send-email", 1)
t2 = enqueue("jobs", "process-payment", 10)
t3 = enqueue("jobs", "resize-image", 5)
dequeue("jobs") → t2 (priority 10)
dequeue("jobs") → t3 (priority 5)
dequeue("jobs") → t1 (priority 1)
dequeue("jobs") → null (empty)
acknowledge(t2) → true // COMPLETED
fail(t3, "timeout") → true // FAILED
retry(t3) → true // back to PENDING (retry 1/2)
// fail + retry again → retry 2/2
// fail + retry again → moved to DLQ
getDeadLetterQueue("jobs") → [t3]
Hints
- Use a sorted data structure with priority as the score for the queue; atomically pop the highest-priority entry on dequeue.
- Store task metadata (payload, status, retry count, queue name) in a key-value structure keyed by task ID.
- Use atomic operations for the dequeue to guarantee no two consumers receive the same task.
- The dead-letter queue can be a simple list structure per queue.
- Generate unique task IDs using
UUID.randomUUID().
Infrastructure your code runs against
- Redis 7.x
How your solution is graded
Your score combines functional correctness (45%), performance (35%) and code quality (20%).
- Target throughput: 8,000 operations/second
- Target p99 latency: 5 ms
- Load test concurrency: 8 concurrent clients
- Load test duration: 10 seconds
- Maximum error rate: 0.1%