@@ -141,33 +141,29 @@ public function get(): ?array
141
141
if ($this->autoSetup) {
142
142
$this->setup();
143
143
}
144
+ $now = microtime();
145
+ $now = substr($now, 11).substr($now, 2, 3);
144
146
145
- try {
146
- $queuedMessageCount = $this->connection->zcount($this->queue, 0, $this->getCurrentTimeInMilliseconds());
147
- } catch (\RedisException $e) {
148
- throw new TransportException($e->getMessage(), 0, $e);
149
- }
147
+ $queuedMessageCount = $this->rawCommand('ZCOUNT', 0, $now);
150
148
151
- if ($queuedMessageCount) {
152
- for ($i = 0; $i < $queuedMessageCount; ++$i) {
153
- try {
154
- $queuedMessages = $this->connection->zpopmin($this->queue, 1);
155
- } catch (\RedisException $e) {
156
- throw new TransportException($e->getMessage(), 0, $e);
157
- }
149
+ while ($queuedMessageCount--) {
150
+ if (![$queuedMessage, $expiry] = $this->rawCommand('ZPOPMIN', 1)) {
151
+ break;
152
+ }
153
+
154
+ if (\strlen($expiry) === \strlen($now) ? $expiry > $now : \strlen($expiry) < \strlen($now)) {
155
+ // if a future-placed message is popped because of a race condition with
156
+ // another running consumer, the message is readded to the queue
158
157
159
- foreach ($queuedMessages as $queuedMessage => $time) {
160
- $queuedMessage = json_decode($queuedMessage, true);
161
- // if a futured placed message is actually popped because of a race condition with
162
- // another running message consumer, the message is readded to the queue by add function
163
- // else its just added stream and will be available for all stream consumers
164
- $this->add(
165
- $queuedMessage['body'],
166
- $queuedMessage['headers'],
167
- $time - $this->getCurrentTimeInMilliseconds()
168
- );
158
+ if (!$this->rawCommand('ZADD', 'NX', $expiry, $queuedMessage)) {
159
+ throw new TransportException('Could not add a message to the redis stream.');
169
160
}
161
+
162
+ break;
170
163
}
164
+
165
+ $queuedMessage = json_decode($queuedMessage, true);
166
+ $this->add($queuedMessage['body'], $queuedMessage['headers'], 0);
171
167
}
172
168
173
169
$messageId = '>'; // will receive new messages
@@ -255,7 +251,7 @@ public function add(string $body, array $headers, int $delayInMs = 0): void
255
251
}
256
252
257
253
try {
258
- if ($delayInMs > 0) { // the delay could be smaller 0 in a queued message
254
+ if ($delayInMs > 0) { // the delay is <= 0 for queued messages
259
255
$message = json_encode([
260
256
'body' => $body,
261
257
'headers' => $headers,
@@ -267,8 +263,18 @@ public function add(string $body, array $headers, int $delayInMs = 0): void
267
263
throw new TransportException(json_last_error_msg());
268
264
}
269
265
270
- $score = $this->getCurrentTimeInMilliseconds() + $delayInMs;
271
- $added = $this->connection->zadd($this->queue, ['NX'], $score, $message);
266
+ $now = explode(' ', microtime(), 2);
267
+ $now[0] = str_pad($delayInMs + substr($now[0], 2, 3), 3, '0', \STR_PAD_LEFT);
268
+ if (3 < \strlen($now[0])) {
269
+ $now[1] += substr($now[0], 0, -3);
270
+ $now[0] = substr($now[0], -3);
271
+
272
+ if (\is_float($now[1])) {
273
+ throw new TransportException("Message delay is too big: {$delayInMs}ms.");
274
+ }
275
+ }
276
+
277
+ $added = $this->rawCommand('ZADD', 'NX', $now[1].$now[0], $message);
272
278
} else {
273
279
$message = json_encode([
274
280
'body' => $body,
@@ -316,14 +322,30 @@ public function setup(): void
316
322
$this->autoSetup = false;
317
323
}
318
324
319
- private function getCurrentTimeInMilliseconds(): int
320
- {
321
- return (int) (microtime(true) * 1000);
322
- }
323
-
324
325
public function cleanup(): void
325
326
{
326
327
$this->connection->del($this->stream);
327
328
$this->connection->del($this->queue);
328
329
}
330
+
331
+ /**
332
+ * @return mixed
333
+ */
334
+ private function rawCommand(string $command, ...$arguments)
335
+ {
336
+ try {
337
+ $result = $this->connection->rawCommand($command, $this->queue, ...$arguments);
338
+ } catch (\RedisException $e) {
339
+ throw new TransportException($e->getMessage(), 0, $e);
340
+ }
341
+
342
+ if (false === $result) {
343
+ if ($error = $this->connection->getLastError() ?: null) {
344
+ $this->connection->clearLastError();
345
+ }
346
+ throw new TransportException($error ?? sprintf('Could not run "%s" on Redis queue.', $command));
347
+ }
348
+
349
+ return $result;
350
+ }
329
351
}
0 commit comments