Releases: ModelsLab/modelq-php
Release list
1.1
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
1.0
Liveness release. Two independent fixes, both merged off the 2026-08-15 outage.
fix: bound the Redis read (#3)
A dropped TCP connection could park a worker forever on a blocking read.
blPop(['ml_tasks'], 1) looked safe, but that 1 is the server's timeout — how long Redis holds the pop before answering. It says nothing about how long the client waits for that answer. phpredis defaults its read timeout to 0 (wait forever) and connect() was called with no timeouts, so when a connection was silently dropped the reply could never arrive and the read never returned.
connect()now passesCONNECT_TIMEOUT(5s) andREAD_TIMEOUT(10s)OPT_TCP_KEEPALIVE(60s) so the kernel probes an idle peer, guarded bydefined()— needs phpredis 5+- the blocking pop uses
BLPOP_TIMEOUT(1s), deliberately held belowREAD_TIMEOUT; invert that ordering and every idle poll aborts the read before Redis replies
Callers injecting their own Redis client are unaffected.
perf: stop reading every task_result payload to prune (#2)
pruneOldTaskResults() scanned the whole task_result:* keyspace and GET the multi-MB value of every key just to read a timestamp — to delete keys Redis already expires on its own. Measured on one production shard over 26 days: 196.7M SCAN + 228.8M GET to issue 2 DELs.
Upgrading
No API changes. The new constants are public const on ModelQ and can be overridden by subclassing if your network needs different bounds.
0.6
Fully drain a task on removeTaskFromQueue (cancel): always zRem queued_requests + sRem processing_tasks so a cancelled/given-up task is neither counted (queue_num/queue_time) nor retried, even if it already left ml_tasks.
0.5
Mirrors modelq python 1.0.11 fixes for production stuck-task and queued_requests leak.
- worker_loop: persist
startedAton the Task object and writestatus='processing'ontask:{id}sostoreFinalTaskStatekeeps them. - requeueStuckProcessingTasks: fall back to
queued_atwhenstarted_atis missing. Covers workers killed in thesAdd processing_tasks→setex task:{id}window. - storeFinalTaskState:
zRem queued_requestson terminal state. Stops unbounded zset growth.
0.4
0.3
0.2
0.1
Full Changelog: https://github.com/ModelsLab/modelq-php/commits/0.1