5
5
use Illuminate \Cache \RetrievesMultipleKeys ;
6
6
use Illuminate \Contracts \Cache \LockProvider ;
7
7
use Illuminate \Contracts \Cache \Store ;
8
- use Illuminate \Support \InteractsWithTime ;
8
+ use Illuminate \Support \Carbon ;
9
+ use MongoDB \BSON \UTCDateTime ;
9
10
use MongoDB \Laravel \Collection ;
10
11
use MongoDB \Laravel \Connection ;
11
12
use MongoDB \Operation \FindOneAndUpdate ;
20
21
21
22
final class MongoStore implements LockProvider, Store
22
23
{
23
- use InteractsWithTime;
24
24
// Provides "many" and "putMany" in a non-optimized way
25
25
use RetrievesMultipleKeys;
26
26
@@ -34,7 +34,7 @@ final class MongoStore implements LockProvider, Store
34
34
* @param string $prefix Prefix for the name of cache items
35
35
* @param Connection|null $lockConnection The MongoDB connection to use for the lock, if different from the cache connection
36
36
* @param string $lockCollectionName Name of the collection where locks are stored
37
- * @param array{int, int} $lockLottery Probability [chance, total] of pruning expired cache items
37
+ * @param array{int, int} $lockLottery Probability [chance, total] of pruning expired cache items. Set to [0, 0] to disable
38
38
* @param int $defaultLockTimeoutInSeconds Time-to-live of the locks in seconds
39
39
*/
40
40
public function __construct (
@@ -62,10 +62,9 @@ public function lock($name, $seconds = 0, $owner = null): MongoLock
62
62
return new MongoLock (
63
63
($ this ->lockConnection ?? $ this ->connection )->getCollection ($ this ->lockCollectionName ),
64
64
$ this ->prefix . $ name ,
65
- $ seconds ,
65
+ $ seconds ?: $ this -> defaultLockTimeoutInSeconds ,
66
66
$ owner ,
67
67
$ this ->lockLottery ,
68
- $ this ->defaultLockTimeoutInSeconds ,
69
68
);
70
69
}
71
70
@@ -95,7 +94,7 @@ public function put($key, $value, $seconds): bool
95
94
[
96
95
'$set ' => [
97
96
'value ' => $ this ->serialize ($ value ),
98
- 'expiration ' => $ this ->currentTime () + $ seconds ,
97
+ 'expires_at ' => $ this ->getUTCDateTime ( $ seconds) ,
99
98
],
100
99
],
101
100
[
@@ -116,6 +115,8 @@ public function put($key, $value, $seconds): bool
116
115
*/
117
116
public function add ($ key , $ value , $ seconds ): bool
118
117
{
118
+ $ isExpired = ['$lte ' => ['$expires_at ' , $ this ->getUTCDateTime ()]];
119
+
119
120
$ result = $ this ->collection ->updateOne (
120
121
[
121
122
'_id ' => $ this ->prefix . $ key ,
@@ -125,16 +126,16 @@ public function add($key, $value, $seconds): bool
125
126
'$set ' => [
126
127
'value ' => [
127
128
'$cond ' => [
128
- 'if ' => [ ' $lte ' => [ ' $expiration ' , $ this -> currentTime ()]] ,
129
+ 'if ' => $ isExpired ,
129
130
'then ' => $ this ->serialize ($ value ),
130
131
'else ' => '$value ' ,
131
132
],
132
133
],
133
- 'expiration ' => [
134
+ 'expires_at ' => [
134
135
'$cond ' => [
135
- 'if ' => [ ' $lte ' => [ ' $expiration ' , $ this -> currentTime ()]] ,
136
- 'then ' => $ this ->currentTime () + $ seconds ,
137
- 'else ' => '$expiration ' ,
136
+ 'if ' => $ isExpired ,
137
+ 'then ' => $ this ->getUTCDateTime ( $ seconds) ,
138
+ 'else ' => '$expires_at ' ,
138
139
],
139
140
],
140
141
],
@@ -156,14 +157,14 @@ public function get($key): mixed
156
157
{
157
158
$ result = $ this ->collection ->findOne (
158
159
['_id ' => $ this ->prefix . $ key ],
159
- ['projection ' => ['value ' => 1 , 'expiration ' => 1 ]],
160
+ ['projection ' => ['value ' => 1 , 'expires_at ' => 1 ]],
160
161
);
161
162
162
163
if (! $ result ) {
163
164
return null ;
164
165
}
165
166
166
- if ($ result ['expiration ' ] <= $ this ->currentTime ()) {
167
+ if ($ result ['expires_at ' ] <= $ this ->getUTCDateTime ()) {
167
168
$ this ->forgetIfExpired ($ key );
168
169
169
170
return null ;
@@ -181,12 +182,9 @@ public function get($key): mixed
181
182
#[Override]
182
183
public function increment ($ key , $ value = 1 ): int |float |false
183
184
{
184
- $ this ->forgetIfExpired ($ key );
185
-
186
185
$ result = $ this ->collection ->findOneAndUpdate (
187
186
[
188
187
'_id ' => $ this ->prefix . $ key ,
189
- 'expiration ' => ['$gte ' => $ this ->currentTime ()],
190
188
],
191
189
[
192
190
'$inc ' => ['value ' => $ value ],
@@ -200,7 +198,7 @@ public function increment($key, $value = 1): int|float|false
200
198
return false ;
201
199
}
202
200
203
- if ($ result ['expiration ' ] <= $ this ->currentTime ()) {
201
+ if ($ result ['expires_at ' ] <= $ this ->getUTCDateTime ()) {
204
202
$ this ->forgetIfExpired ($ key );
205
203
206
204
return false ;
@@ -257,7 +255,7 @@ public function forgetIfExpired($key): bool
257
255
{
258
256
$ result = $ this ->collection ->deleteOne ([
259
257
'_id ' => $ this ->prefix . $ key ,
260
- 'expiration ' => ['$lte ' => $ this ->currentTime ()],
258
+ 'expires_at ' => ['$lte ' => $ this ->getUTCDateTime ()],
261
259
]);
262
260
263
261
return $ result ->getDeletedCount () > 0 ;
@@ -275,6 +273,17 @@ public function getPrefix(): string
275
273
return $ this ->prefix ;
276
274
}
277
275
276
+ /** Creates a TTL index that automatically deletes expired objects. */
277
+ public function createTTLIndex (): void
278
+ {
279
+ $ this ->collection ->createIndex (
280
+ // UTCDateTime field that holds the expiration date
281
+ ['expires_at ' => 1 ],
282
+ // Delay to remove items after expiration
283
+ ['expireAfterSeconds ' => 0 ],
284
+ );
285
+ }
286
+
278
287
private function serialize ($ value ): string |int |float
279
288
{
280
289
// Don't serialize numbers, so they can be incremented
@@ -293,4 +302,9 @@ private function unserialize($value): mixed
293
302
294
303
return unserialize ($ value );
295
304
}
305
+
306
+ private function getUTCDateTime (int $ additionalSeconds = 0 ): UTCDateTime
307
+ {
308
+ return new UTCDateTime (Carbon::now ()->addSeconds ($ additionalSeconds ));
309
+ }
296
310
}
0 commit comments