1
1
<?php
2
2
3
- namespace Cyppe \ LaravelBatchJobsRedisDriver \Repositories ;
3
+ namespace App \Repositories ;
4
4
5
5
use Carbon \CarbonImmutable ;
6
6
use Closure ;
7
7
use DateTimeInterface ;
8
8
use Illuminate \Bus \Batch ;
9
9
use Illuminate \Bus \BatchFactory ;
10
10
use Illuminate \Bus \BatchRepository ;
11
- use Illuminate \Bus \DatabaseBatchRepository ;
12
11
use Illuminate \Bus \PendingBatch ;
13
12
use Illuminate \Bus \PrunableBatchRepository ;
14
13
use Illuminate \Bus \UpdatedBatchJobCounts ;
@@ -24,47 +23,55 @@ class RedisBatchRepository extends DatabaseBatchRepository implements BatchRepos
24
23
public function __construct ( BatchFactory $ factory )
25
24
{
26
25
$ this ->lockTimeout = 120 ;
27
- $ this ->factory = $ factory ;
26
+ $ this ->factory = $ factory ;
28
27
}
29
28
30
- public function get ( $ limit = 50 , $ before = null )
29
+ public function get ( $ limit = 50 , $ before = null ): array
31
30
{
32
31
if ( !Redis::exists ( 'batches_list ' ) ) {
33
- // Handle the case where the batches_list does not exist
34
32
return [];
35
33
}
36
34
37
- $ allBatchIds = Redis::lrange ( 'batches_list ' , 0 , -1 );
38
- $ batches = [];
35
+ $ totalBatches = Redis::llen ( 'batches_list ' );
39
36
40
- foreach ( $ allBatchIds as $ batchId ) {
41
- $ data = Redis::get ( "batch: $ batchId " );
42
- if ( $ data !== false ) {
43
- $ batchData = unserialize ( $ data );
44
- $ batches [$ batchId ] = $ batchData ;
37
+ // Determine the starting index
38
+ $ startIndex = 0 ;
39
+ if ( $ before !== null ) {
40
+ // Fetch a range of batch IDs surrounding the 'before' value
41
+ // Adjust the range size as needed for efficiency
42
+ $ rangeSize = 100 ;
43
+ $ allBatchIds = Redis::lrange ( 'batches_list ' , 0 , $ rangeSize - 1 );
44
+ $ beforeIndex = array_search ( $ before , $ allBatchIds );
45
+
46
+ if ( $ beforeIndex !== false ) {
47
+ $ startIndex = $ beforeIndex + 1 ;
48
+ } else {
49
+ // TODO:
50
+ // Handle case where 'before' is not found in the initial range
51
+ // Consider fetching additional ranges or handling this scenario differently
45
52
}
46
53
}
47
54
48
- // Sort batches by 'created_at' in descending order
49
- uasort ( $ batches , function ( $ a , $ b ) {
50
- return $ b ['created_at ' ] <=> $ a ['created_at ' ];
51
- } );
55
+ // Calculate the range to fetch
56
+ $ rangeEnd = min ( $ startIndex + $ limit - 1 , $ totalBatches - 1 );
52
57
53
- // If 'before' is specified, find the start position
54
- $ startIndex = 0 ;
55
- if ( $ before !== null ) {
56
- $ startIndex = array_search ( $ before , array_keys ( $ batches ) );
57
- $ startIndex = $ startIndex === false ? 0 : $ startIndex + 1 ;
58
- }
58
+ // Fetch only the required batch IDs
59
+ $ batchIds = Redis::lrange ( 'batches_list ' , $ startIndex , $ rangeEnd );
59
60
60
- // Slice the array to apply limit and offset
61
- $ batches = array_slice ( $ batches , $ startIndex , $ limit , true );
61
+ // Use Redis pipeline to bulk fetch batch data
62
+ $ responses = Redis::pipeline ( function ( $ pipe ) use ( $ batchIds ) {
63
+ foreach ( $ batchIds as $ batchId ) {
64
+ $ pipe ->get ( "batch: $ batchId " );
65
+ }
66
+ } );
62
67
63
- return array_map ( function ( $ batchId ) {
64
- return $ this -> find ( $ batchId );
65
- }, array_keys ( $ batches ) );
66
- }
68
+ // Filter, unserialize, and map to Batch objects
69
+ $ batches = array_map ( function ( $ response ) {
70
+ return $ response ? $ this -> toBatch ( json_decode ( $ response , true ) ) : null ;
71
+ }, $ responses );
67
72
73
+ return array_filter ( $ batches );
74
+ }
68
75
69
76
public function find ( string $ batchId )
70
77
{
@@ -75,7 +82,7 @@ public function find( string $batchId )
75
82
return null ;
76
83
}
77
84
78
- $ batchData = unserialize ( $ data );
85
+ $ batchData = json_decode ( $ data, true );
79
86
return $ this ->toBatch ( $ batchData );
80
87
}
81
88
@@ -96,7 +103,7 @@ public function store( PendingBatch $batch )
96
103
'finished_at ' => null ,
97
104
];
98
105
99
- Redis::set ( "batch: $ id " , serialize ( $ batchData ) );
106
+ Redis::set ( "batch: $ id " , json_encode ( $ batchData ) );
100
107
Redis::rpush ( 'batches_list ' , $ id ); // Add the batch ID to the list
101
108
102
109
return $ this ->find ( $ id );
@@ -110,15 +117,14 @@ public function incrementTotalJobs( string $batchId, int $amount )
110
117
Log::error ( "Batch not found for incrementTotalJobs: " . $ batchId );
111
118
return new UpdatedBatchJobCounts ( 0 , 0 );
112
119
}
113
- $ batchData = unserialize ( $ data );
114
- $ batchData ['total_jobs ' ] += $ amount ;
120
+ $ batchData = json_decode ( $ data, true );
121
+ $ batchData ['total_jobs ' ] += $ amount ;
115
122
$ batchData ['pending_jobs ' ] += $ amount ;
116
- Redis::set ( "batch: $ batchId " , serialize ( $ batchData ) );
123
+ Redis::set ( "batch: $ batchId " , json_encode ( $ batchData ) );
117
124
return new UpdatedBatchJobCounts ( $ batchData ['pending_jobs ' ], $ batchData ['failed_jobs ' ] );
118
- }, 100 , 200 );
125
+ }, 100 , 200 );
119
126
}
120
127
121
-
122
128
protected function acquireLock ( string $ key ): bool
123
129
{
124
130
$ isAcquired = Redis::set ( $ key , true , 'EX ' , $ this ->lockTimeout , 'NX ' );
@@ -132,10 +138,10 @@ protected function executeWithLock( string $lockKey, Closure $callback, $retryCo
132
138
if ( $ this ->acquireLock ( $ lockKey ) ) {
133
139
try {
134
140
if ( $ attempts > 2 ) {
135
- // Log::info( "Finally got lock. Attempt: " . $attempts );
141
+ // Log::info( "Finally got lock. Attempt: " . $attempts );
136
142
}
137
143
return $ callback ();
138
- } catch ( \Exception $ e ) {
144
+ } catch ( \Throwable $ e ) {
139
145
Log::error ( "Error in executeWithLock: " . $ e ->getMessage () );
140
146
throw $ e ;
141
147
} finally {
@@ -167,15 +173,14 @@ public function incrementFailedJobs( string $batchId, string $jobId )
167
173
Log::error ( "Batch not found for incrementFailedJobs: " . $ batchId );
168
174
return new UpdatedBatchJobCounts ( 0 , 0 );
169
175
}
170
- $ batchData = unserialize ( $ data );
176
+ $ batchData = json_decode ( $ data, true );
171
177
$ batchData ['failed_jobs ' ]++;
172
178
$ batchData ['failed_job_ids ' ][] = $ jobId ;
173
- Redis::set ( "batch: $ batchId " , serialize ( $ batchData ) );
179
+ Redis::set ( "batch: $ batchId " , json_encode ( $ batchData ) );
174
180
return new UpdatedBatchJobCounts ( $ batchData ['pending_jobs ' ], $ batchData ['failed_jobs ' ] );
175
- }, 100 , 200 );
181
+ }, 100 , 200 );
176
182
}
177
183
178
-
179
184
public function decrementPendingJobs ( string $ batchId , string $ jobId )
180
185
{
181
186
return $ this ->executeWithLock ( "lock:batch: $ batchId " , function () use ( $ batchId , $ jobId ) {
@@ -184,11 +189,11 @@ public function decrementPendingJobs( string $batchId, string $jobId )
184
189
Log::error ( "Batch not found for decrementPendingJobs: " . $ batchId );
185
190
return new UpdatedBatchJobCounts ( 0 , 0 );
186
191
}
187
- $ batchData = unserialize ( $ data );
192
+ $ batchData = json_decode ( $ data, true );
188
193
$ batchData ['pending_jobs ' ]--;
189
- Redis::set ( "batch: $ batchId " , serialize ( $ batchData ) );
194
+ Redis::set ( "batch: $ batchId " , json_encode ( $ batchData ) );
190
195
return new UpdatedBatchJobCounts ( $ batchData ['pending_jobs ' ], $ batchData ['failed_jobs ' ] );
191
- }, 100 , 200 );
196
+ }, 100 , 200 );
192
197
}
193
198
194
199
@@ -202,13 +207,13 @@ public function markAsFinished( string $batchId )
202
207
return ;
203
208
}
204
209
205
- $ batchData = unserialize ( $ data );
210
+ $ batchData = json_decode ( $ data, true );
206
211
// Convert finished_at to a Unix timestamp before storing
207
212
$ batchData ['finished_at ' ] = CarbonImmutable::now ()->getTimestamp ();
208
- Redis::set ( "batch: $ batchId " , serialize ( $ batchData ) );
213
+ Redis::set ( "batch: $ batchId " , json_encode ( $ batchData ) );
209
214
210
- // Log::debug( "Batch marked as finished: " . $batchId . " with finished_at: " . $batchData['finished_at'] );
211
- }, 100 , 200 );
215
+ Log::debug ( "Batch marked as finished: " . $ batchId . " with finished_at: " . $ batchData ['finished_at ' ] );
216
+ }, 100 , 200 );
212
217
}
213
218
214
219
@@ -257,21 +262,18 @@ public function cancel( string $batchId )
257
262
if ( $ data === false ) {
258
263
return ;
259
264
}
260
- $ batchData = unserialize ( $ data );
265
+ $ batchData = json_decode ( $ data, true );
261
266
// Convert cancelled_at to a Unix timestamp before storing
262
267
$ batchData ['cancelled_at ' ] = CarbonImmutable::now ()->getTimestamp ();
263
- Redis::set ( "batch: $ batchId " , serialize ( $ batchData ) );
264
- }, 100 , 200 ); // Retry 100 times with 200 milliseconds between retries
268
+ Redis::set ( "batch: $ batchId " , json_encode ( $ batchData ) );
269
+ }, 100 , 200 ); // Retry 100 times with 200 milliseconds between retries
265
270
}
266
271
267
-
268
272
public function transaction ( Closure $ callback )
269
273
{
270
274
return $ callback ();
271
275
}
272
276
273
-
274
- // Work in progress
275
277
public function prune ( DateTimeInterface $ before )
276
278
{
277
279
return $ this ->pruneBatches ( $ before , true );
@@ -289,7 +291,7 @@ public function pruneCancelled( DateTimeInterface $before )
289
291
290
292
protected function pruneBatches ( DateTimeInterface $ before , $ isFinished = null , $ isCancelled = false )
291
293
{
292
- $ batchIds = Redis::lrange ( 'batches_list ' , 0 , -1 );
294
+ $ batchIds = Redis::lrange ( 'batches_list ' , 0 , -1 );
293
295
$ totalDeleted = 0 ;
294
296
295
297
foreach ( $ batchIds as $ batchId ) {
@@ -300,12 +302,12 @@ protected function pruneBatches( DateTimeInterface $before, $isFinished = null,
300
302
continue ;
301
303
}
302
304
303
- $ batchData = unserialize ( $ data );
305
+ $ batchData = json_decode ( $ data, true );
304
306
305
307
$ shouldBeDeleted = false ;
306
308
307
- $ createdAt = CarbonImmutable::createFromTimestamp ( $ batchData ['created_at ' ] );
308
- $ finishedAt = isset ( $ batchData ['finished_at ' ] ) ? CarbonImmutable::createFromTimestamp ( $ batchData ['finished_at ' ] ) : null ;
309
+ $ createdAt = CarbonImmutable::createFromTimestamp ( $ batchData ['created_at ' ] );
310
+ $ finishedAt = isset ( $ batchData ['finished_at ' ] ) ? CarbonImmutable::createFromTimestamp ( $ batchData ['finished_at ' ] ) : null ;
309
311
$ cancelledAt = isset ( $ batchData ['cancelled_at ' ] ) ? CarbonImmutable::createFromTimestamp ( $ batchData ['cancelled_at ' ] ) : null ;
310
312
311
313
if ( $ isFinished === true && $ finishedAt && $ finishedAt < $ before ) {
@@ -316,7 +318,6 @@ protected function pruneBatches( DateTimeInterface $before, $isFinished = null,
316
318
$ shouldBeDeleted = true ;
317
319
}
318
320
319
-
320
321
if ( $ shouldBeDeleted ) {
321
322
Redis::del ( "batch: $ batchId " );
322
323
Redis::lrem ( 'batches_list ' , 0 , $ batchId );
@@ -326,6 +327,4 @@ protected function pruneBatches( DateTimeInterface $before, $isFinished = null,
326
327
327
328
return $ totalDeleted ;
328
329
}
329
-
330
-
331
- }
330
+ }
0 commit comments