@@ -36,6 +36,7 @@ class Connection
36
36
37
37
private $ connection ;
38
38
private $ stream ;
39
+ private $ queue ;
39
40
private $ group ;
40
41
private $ consumer ;
41
42
private $ autoSetup ;
@@ -59,6 +60,7 @@ public function __construct(array $configuration, array $connectionCredentials =
59
60
$ this ->stream = $ configuration ['stream ' ] ?? self ::DEFAULT_OPTIONS ['stream ' ];
60
61
$ this ->group = $ configuration ['group ' ] ?? self ::DEFAULT_OPTIONS ['group ' ];
61
62
$ this ->consumer = $ configuration ['consumer ' ] ?? self ::DEFAULT_OPTIONS ['consumer ' ];
63
+ $ this ->queue = $ this ->stream .'__queue ' ;
62
64
$ this ->autoSetup = $ configuration ['auto_setup ' ] ?? self ::DEFAULT_OPTIONS ['auto_setup ' ];
63
65
$ this ->maxEntries = $ configuration ['stream_max_entries ' ] ?? self ::DEFAULT_OPTIONS ['stream_max_entries ' ];
64
66
}
@@ -112,6 +114,24 @@ public function get(): ?array
112
114
$ this ->setup ();
113
115
}
114
116
117
+ $ queuedMessageCount = $ this ->connection ->zcount ($ this ->queue , 0 , $ this ->getCurrentTimeInMilliseconds ());
118
+
119
+ if ($ queuedMessageCount ) {
120
+ for ($ i = 0 ; $ i < $ queuedMessageCount ; ++$ i ) {
121
+ foreach ($ this ->connection ->zpopmin ($ this ->queue , 1 ) as $ queuedMessage => $ time ) {
122
+ $ queuedMessage = json_decode ($ queuedMessage , true );
123
+ // if a futured placed message is actually popped because of a race condition with
124
+ // another running message consumer, the message is readded to the queue by add function
125
+ // else its just added stream and will be available for all stream consumers
126
+ $ this ->add (
127
+ $ queuedMessage ['body ' ],
128
+ $ queuedMessage ['headers ' ],
129
+ $ time - $ this ->getCurrentTimeInMilliseconds ()
130
+ );
131
+ }
132
+ }
133
+ }
134
+
115
135
$ messageId = '> ' ; // will receive new messages
116
136
117
137
if ($ this ->couldHavePendingMessages ) {
@@ -191,24 +211,37 @@ public function reject(string $id): void
191
211
}
192
212
}
193
213
194
- public function add (string $ body , array $ headers ): void
214
+ /**
215
+ * @param int $delay The delay in milliseconds
216
+ */
217
+ public function add (string $ body , array $ headers , int $ delay = 0 ): void
195
218
{
196
219
if ($ this ->autoSetup ) {
197
220
$ this ->setup ();
198
221
}
199
222
200
223
try {
201
- if ($ this ->maxEntries ) {
202
- $ added = $ this ->connection ->xadd ($ this ->stream , '* ' , ['message ' => json_encode (
203
- ['body ' => $ body , 'headers ' => $ headers ]
204
- )], $ this ->maxEntries , true );
224
+ $ message = json_encode ([
225
+ 'body ' => $ body ,
226
+ 'headers ' => $ headers ,
227
+ 'uniqid ' => uniqid ('' , true ), // Entry need to be unique in the sorted set
228
+ ]);
229
+
230
+ if ($ delay > 0 ) { // the delay could be smaller 0 in a queued message
231
+ $ score = (int ) ($ this ->getCurrentTimeInMilliseconds () + $ delay );
232
+ $ added = $ this ->connection ->zadd ($ this ->queue , ['NX ' ], $ score , $ message );
205
233
} else {
206
- $ added = $ this ->connection ->xadd ($ this ->stream , '* ' , ['message ' => json_encode (
207
- ['body ' => $ body , 'headers ' => $ headers ]
208
- )]);
234
+ if ($ this ->maxEntries ) {
235
+ $ added = $ this ->connection ->xadd ($ this ->stream , '* ' , ['message ' => $ message ], $ this ->maxEntries , true );
236
+ } else {
237
+ $ added = $ this ->connection ->xadd ($ this ->stream , '* ' , ['message ' => $ message ]);
238
+ }
209
239
}
210
240
} catch (\RedisException $ e ) {
211
- throw new TransportException ($ e ->getMessage (), 0 , $ e );
241
+ if ($ error = $ this ->connection ->getLastError () ?: null ) {
242
+ $ this ->connection ->clearLastError ();
243
+ }
244
+ throw new TransportException ($ error ?? $ e ->getMessage (), 0 , $ e );
212
245
}
213
246
214
247
if (!$ added ) {
@@ -234,4 +267,18 @@ public function setup(): void
234
267
235
268
$ this ->autoSetup = false ;
236
269
}
270
+
271
+ private function getCurrentTimeInMilliseconds (): int
272
+ {
273
+ return (int ) (microtime (true ) * 1000 );
274
+ }
275
+
276
+ /**
277
+ * @internal
278
+ */
279
+ public function cleanup ()
280
+ {
281
+ $ this ->connection ->del ($ this ->stream );
282
+ $ this ->connection ->del ($ this ->queue );
283
+ }
237
284
}
0 commit comments