Job Scheduler
Problem
A background-processing service has a fixed number of workers. Jobs become eligible at a scheduled time, but a worker must claim an occurrence before running it. Recurring jobs use fixed-delay scheduling, so their next occurrence depends on when the current one completes rather than when it was originally due.
Design JobScheduler to store one-time and recurring jobs and coordinate claims from worker threads sharing one instance. Time is represented by caller-supplied logical ticks.
Requirements
- Register timed work.
submitOncecreates one occurrence due atrunAt;submitRecurringcreates its first occurrence due atfirstRunAtand retainsintervalTicks. - Claim only due work.
claimDue(now)returns and activates any scheduled job whose due tick is at mostnow, or returns the empty string when none is immediately claimable. - Protect claims and capacity. Each job has at most one active occurrence, no occurrence can be claimed twice, and the total number of active claims never exceeds
workerCount; a claim at full capacity returns the empty string. - Complete an occurrence.
completereleases the active claim, ends a one-time job, and schedules a recurring job's next occurrence atcompletedAt + intervalTicks. - Coordinate threads. Every public call is linearizable when threads share one scheduler. Calls may use ordinary internal synchronization, but they do not wait for time, capacity, work, or completion;
claimDuereturns immediately when it cannot claim.
API
| Signature | Returns | Behavior |
|---|---|---|
JobScheduler(workerCount: integer) | Not applicable | Creates an empty scheduler with the given active-claim capacity. |
submitOnce(jobId: string, runAt: integer) | void | Registers one-time work. |
submitRecurring(jobId: string, firstRunAt: integer, intervalTicks: integer) | void | Registers fixed-delay recurring work. |
claimDue(now: integer) | string | Attempts an immediate claim and returns a job ID or the empty string. |
complete(jobId: string, completedAt: integer) | void | Completes that job's active occurrence. |
Examples
With JobScheduler(1):
| Step | Operation | Result |
|---|---|---|
| 1 | submitRecurring("pulse", 5, 4) | void |
| 2 | claimDue(4) | "" |
| 3 | claimDue(5) | "pulse" |
| 4 | claimDue(100) | "" |
| 5 | complete("pulse", 9) | void |
| 6 | claimDue(12) | "" |
| 7 | claimDue(13) | "pulse" |
With two due jobs, capacity 2, and three concurrent claimDue(0) calls, exactly two calls return distinct job IDs and the other returns "".
Constraints
1 <= workerCount <= 64.- Each
jobIdis non-empty, has at most 64 characters, and is submitted exactly once. - Tick arguments are exact nonnegative 64-bit integers no greater than
1,000,000,000,000. 1 <= intervalTicks <= 1,000,000,000,000, and each computed next due tick is at most2,000,000,000,000.completeis called only for an active occurrence, withcompletedAtat least the tick used to claim it.- At most
100,000jobs and200,000public method calls occur in one testcase. - Inputs satisfy these constraints; behavior outside them is not judged.
Notes
Calls in one thread take effect in program order. Across threads, any linearizable interleaving is valid. When several jobs are due, any one may be returned; no priority, fairness, wake-up, cancellation, or shutdown behavior is required.