Your task is to implement a high-performance ClientManager in Go. This manager is responsible for routing job requests to a critical, external service that enforces extremely strict and non-negotiable operational limits. The primary goal is to maximize throughput and job completion by queueing requests when limits are exceeded, ensuring no job is ever dropped unnecessarily.
The external service imposes two separate, hard limits that must be respected simultaneously:
-
Concurrency Limit: A maximum of 10 concurrent requests may be active at any given time.
-
Rate Limit: A maximum of 100 total requests may be made within any 60-second window.
-
Implement the
ClientManager: Create a structClientManagerthat manages the job queue, concurrency pool, and rate limiter. It must expose a non-blockingSubmitJob(job Job) errormethod. If the internal job queue is full, the submission should apply backpressure and block the caller until space is available in the internal queue. -
The External Service (Mock): You must implement a mock function/method representing the actual API call:
// This function simulates the latency of the external API call. func ExternalServiceCall(job Job) error { // Simulate network latency (e.g., 50ms - 500ms) delay := time.Duration(rand.Intn(450) + 50) * time.Millisecond time.Sleep(delay) // 1% chance of a transient failure if rand.Intn(100) == 0 { return fmt.Errorf("transient service error for job %s", job.ID) } // Simulate successful completion return nil } -
Job Structure: Define a simple
Jobstruct.type Job struct { ID string UserID string // Mandatory for user-level quota tracking // Add other relevant fields if you like, e.g., Payload... } -
Testing and Demonstration: Include a
mainfunction that demonstrates submitting at least 500 jobs concurrently and prints logs showing successful execution, adherence to constraints, and handling of errors.
To better illustrate the requirements, consider these scenarios:
| Scenario | Action | Expected Manager Behavior |
|---|---|---|
| High Concurrency | 20 jobs submitted immediately. | 10 jobs start execution. The remaining 10 jobs are held in the internal queue, starting only as the initial 10 complete. |
| Rate Limit Hit | 100 requests executed in 10 seconds. | The 101st request is immediately queued. It must wait until the 60-second time window is reset (i.e., 50 seconds later) before it can begin processing, regardless of concurrency slots being open. |
| Graceful Shutdown | Call the shutdown method while 5 jobs are running and 15 are queued. | The 5 running jobs finish. The 15 queued jobs are then processed and completed before the manager truly exits. |
The solution must:
-
Concurrency Management: Correctly implement a worker pool or semaphore structure ensuring that no more than 10 goroutines are executing
ExternalServiceCallsimultaneously. -
Request Queuing/Backpressure: Implement an internal mechanism to queue submitted jobs using a First-In, First-Out (FIFO) order when limits are exceeded. Jobs must never be failed or dropped due to temporary backpressure.
-
Rate Limiting Precision: Employ a rate limiting mechanism that strictly enforces the limit of 100 requests per 60 seconds on a rolling basis, serving queued requests immediately when a new window opens.
-
Graceful Shutdown: Implement a mechanism (e.g., a context or a dedicated method) to shut down the
ClientManagergracefully, ensuring all submitted and queued jobs are processed and completed before termination. -
Efficiency and Throughput: The design should prioritize maximum achievable throughput, minimizing unnecessary idling. The manager should process queued jobs immediately whenever both concurrency and rate limit resources become available.
-
Retry Logic: Implement an exponential backoff and retry mechanism for jobs that fail with a transient error (the 1% failure case), with a maximum of 3 retries per job.
-
Dynamic Configuration: Refactor the
ClientManagerinitialization to accept the concurrency limit (N) and rate limit (R requests / T seconds) as parameters, allowing it to be configured dynamically. -
Hierarchical Quota Tracking (System and User Level):
-
Allow the
ClientManagerto be initialized with two quota limits:-
System-Level Quota: A maximum number of total requests allowed for all users combined (e.g., 100,000 per 24 hours).
-
User-Level Quota: A maximum number of requests allowed per unique UserID (e.g., 5,000 per 24 hours).
-
-
The manager must enforce both limits simultaneously. A job must be stopped if either the System-Level or its specific User-Level quota is exceeded.
-
Implement a dedicated method to reset these long-term quotas.
-
These five tests should be implemented in the candidate's solution to prove all core requirements are met:
-
Test Concurrency Limit and FIFO Order:
-
Action: Submit 15 unique jobs immediately. Jobs 1-15 all have the same
UserID. -
Assertion: Jobs 1-10 start concurrently. Jobs 11-15 are queued. Jobs 11-15 must start in order (11, then 12, then 13, etc.) as the initial 10 jobs complete.
-
-
Test Rolling Rate Limit (The Delay Test):
-
Setup: Ensure the
ExternalServiceCallmock is fast (e.g., 5ms latency). -
Action: Submit 101 jobs very quickly (within 1 second).
-
Assertion: Jobs 1-100 execute. Job 101 must be successfully submitted but must not start execution until at least 60 seconds have elapsed since Job 1 started.
-
-
Test Mixed Limit Saturation:
-
Action: Submit 50 jobs. The first 10 immediately fill the Concurrency Limit. As the first 10 finish, submit 60 more jobs (total 110 jobs submitted within 10 seconds).
-
Assertion: 100 jobs execute within the first 60 seconds. Jobs 101-110 are successfully queued and must remain queued until the rate limit window for the first request expires.
-
-
Test Graceful Shutdown and Completion:
-
Action: Submit 50 jobs. After 5 jobs start running, immediately call the
Shutdown()method. -
Assertion: The
Shutdown()call must block until all 50 submitted jobs (running and queued) have completed execution successfully. No jobs should be dropped.
-
-
Test Hierarchical Quota Enforcement (Bonus 3 Required):
-
Setup: Initialize the manager with a System Quota of 50 and a User Quota of 10.
-
Action: Submit 15 jobs for
UserID=Aand 15 jobs forUserID=B. -
Assertion:
-
UserID=Aprocesses 10 jobs and hits the User Quota. -
UserID=Bprocesses 15 jobs. -
The System-Level Quota (50) is reached. Subsequent jobs for any user must be permanently rejected/failed with a "Quota Exceeded" error.
-
-