Problem Description
Design a QueryBatcher class that batches small key queries into larger ones using an asynchronous function queryMultiple. The constructor takes two parameters: the async function queryMultiple(keys) (which returns an array of values corresponding to the queried keys) and a throttle time t in milliseconds. The getValue(key) method should return a promise that resolves with the value for that key. The very first call of a new “batch cycle” should immediately call queryMultiple with that single key, whereas subsequent calls occurring within t ms should be queued and then processed together (in a separate queryMultiple call after t ms). Every key is unique.
Key Insights
- Use a timer to enforce a throttle window of t milliseconds between queryMultiple calls.
- The first call in a new cycle is handled immediately—its promise is resolved as soon as queryMultiple returns.
- Any subsequent getValue calls within the current window are batched. When the timer expires, batch all queued keys into a single queryMultiple call and resolve their pending promises.
- Maintain a queue (or mapping) pairing each pending key with its promise’s resolve function.
Space and Time Complexity
Time Complexity: O(n) per batch call, where n is the number of keys batched. Space Complexity: O(n) for storing deferred promises and keys in each batch.
Solution
The solution maintains two internal state components:
- A timer (or flag) indicating an active throttle window.
- A pendingBatch list storing information (key and promise resolution functions) for all getValue calls made during the throttle window (after the immediate call).
When getValue(key) is invoked:
- If no timer is active (i.e. we are starting a new batch cycle): • Immediately call queryMultiple with [key] and return its promise. • Start a timer for t ms. If subsequent getValue calls occur during this window, they are added to the pendingBatch.
- If a timer is already running, push the key into the pending batch along with a deferred promise resolver.
When the timer expires:
- If pendingBatch is non-empty, gather all keys from the batch and call queryMultiple with that list.
- Distribute the returned results to their respective promises.
- Clear the pendingBatch and reset the timer so that a new cycle may begin when a later getValue call occurs.
This approach keeps consecutive queryMultiple calls at least t ms apart and guarantees the correct query batching behavior.
Code Solutions
Below are code implementations in Python, JavaScript, C++, and Java. Each snippet is provided with detailed line‐by‐line comments.