Two performance changes since 1.0, both aimed at the same cost: producers run in us-east4 while the ModelQ Redis is in Mumbai, ~271ms away, so every reply this library waits for before sending the next command lands on a caller's request path.
One round trip per enqueue (#5)
enqueue() issued five sequential writes — rPush ml_tasks, zAdd queued_requests, setex task:, zAdd task_history, setex task_history: — waiting for each reply in turn. Measured against 5,434 production requests joined between Redis and MongoDB, that gap is a flat 1.355–1.41s regardless of payload size: exactly five round trips.
They now go out as one pipeline. Redis still executes them in order; only the waiting is removed.
Ordering is corrected at the same time: the task's own state is written before the id is advertised on ml_tasks, so a worker can no longer pop a task whose task:{id} key does not exist yet.
The result poll backs off (#5)
Task::getResult() woke every 100ms and issued two GETs per wake. On a 30s wait against a Redis 271ms away that is ~100 round trips to learn nothing, with the caller's worker pinned for all of it.
The pair of GETs is now one pipelined read, and the interval starts at 100ms and doubles to a 1s ceiling.
| wait | polls before | polls after |
|---|---|---|
| 3s | 30 | 6 |
| 30s | 300 | 9 |
Backoff rather than a flat 1s keeps the fast endpoints fast — realtime_turbo runs in 1.18s at p50 and 100% of its tasks finish inside 10s, so a flat 1s poll would add up to a second to a one-second job.
Task history stops storing the payload a third time (#4)
Compatibility
No API breaks. Wire format unchanged — same five keys, same values, same scores. isCancelled() semantics preserved exactly, including a stored "0" still counting as cancelled.
Task::POLL_MIN_INTERVAL_US and Task::POLL_MAX_INTERVAL_US are public if you want to pin the cadence flat.
The now-unused private addToTaskHistory() was removed.
Full Changelog: 1.0...1.1