@@ -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