3
3
4
4
final class Locker
5
5
{
6
+ /**
7
+ * The lock collection.
8
+ *
9
+ * @var \MongoCollection
10
+ */
11
+ private $ collection ;
12
+
13
+ /**
14
+ * The duration in microseconds to wait inbetween lock attempts.
15
+ *
16
+ * @var int
17
+ */
18
+ private $ pollDuration ;
19
+
20
+ /**
21
+ * How long to wait for a lock before throwing an exception.
22
+ *
23
+ * @var int
24
+ */
25
+ private $ timeoutDuration ;
26
+
6
27
/**
7
28
* Lock docs look like:
8
29
* [
@@ -20,34 +41,42 @@ final class Locker
20
41
*/
21
42
22
43
/**
23
- * Get a read lock .
44
+ * Initialize the locker .
24
45
*
25
46
* @param \MongoCollection $collection the lock collection
47
+ * @param int $pollDuration duration in microseconds to wait inbetween lock attempts
48
+ * @param int $timeoutDuration duration in seconds to wait for a lock before throwing an exception
49
+ */
50
+ public function __construct (\MongoCollection $ collection , $ pollDuration = 100000 , $ timeoutDuration = PHP_INT_MAX )
51
+ {
52
+ if (!is_int ($ pollDuration ) || $ pollDuration < 0 ) {
53
+ throw new \InvalidArgumentException ('$pollDuration must be an int >= 0 ' );
54
+ }
55
+
56
+ if (!is_int ($ timeoutDuration ) || $ timeoutDuration < 0 ) {
57
+ throw new \InvalidArgumentException ('$timeoutDuration must be an int >= 0 ' );
58
+ }
59
+
60
+ $ this ->collection = $ collection ;
61
+ $ this ->pollDuration = $ pollDuration ;
62
+ $ this ->timeoutDuration = $ timeoutDuration ;
63
+ }
64
+
65
+ /**
66
+ * Get a read lock.
67
+ *
26
68
* @param mixed $id an id for the lock that used with the other *Lock()/*Unlock() methods.
27
69
* Any type suitable for a mongo _id
28
70
* @param \MongoDate $staleTimestamp time the read is considered stale and can be cleared.
29
71
* (to possibly write lock if no more readers)
30
- * @param int $pollDuration duration in microseconds to wait inbetween lock attempts
31
- * @param int $timeoutTimestamp a unix timestamp to stop waiting and throw an exception
32
72
*
33
73
* @throws \Exception
34
74
*
35
75
* @return \MongoId a reader id to be given to readUnlock()
36
76
*/
37
- public static function readLock (
38
- \MongoCollection $ collection ,
39
- $ id ,
40
- \MongoDate $ staleTimestamp ,
41
- $ pollDuration = 100000 ,
42
- $ timeoutTimestamp = PHP_INT_MAX
43
- ) {
44
- if (!is_int ($ pollDuration ) || $ pollDuration < 0 ) {
45
- throw new \InvalidArgumentException ('$pollDuration must be an int >= 0 ' );
46
- }
47
-
48
- if (!is_int ($ timeoutTimestamp )) {
49
- throw new \InvalidArgumentException ('$timeoutTimestamp must be an int ' );
50
- }
77
+ public function readLock ($ id , \MongoDate $ staleTimestamp )
78
+ {
79
+ $ timeoutTimestamp = (int )min (time () + $ this ->timeoutDuration , PHP_INT_MAX );
51
80
52
81
while (time () < $ timeoutTimestamp ) {
53
82
$ readerId = new \MongoId ();
@@ -57,7 +86,7 @@ public static function readLock(
57
86
'$set ' => ['writeStaleTs ' => null ],
58
87
];
59
88
try {
60
- if ($ collection ->update ($ query , $ update , ['upsert ' => true ])['n ' ] === 1 ) {
89
+ if ($ this -> collection ->update ($ query , $ update , ['upsert ' => true ])['n ' ] === 1 ) {
61
90
return $ readerId ;
62
91
}
63
92
} catch (\MongoException $ e ) {
@@ -66,11 +95,11 @@ public static function readLock(
66
95
}
67
96
}
68
97
69
- if (self :: clearStuckWrite ($ collection , $ id )) {
98
+ if ($ this -> clearStuckWrite ($ id )) {
70
99
continue ;
71
100
}
72
101
73
- usleep ($ pollDuration );
102
+ usleep ($ this -> pollDuration );
74
103
}
75
104
76
105
throw new \Exception ('timed out waiting for lock ' );
@@ -79,44 +108,30 @@ public static function readLock(
79
108
/**
80
109
* Release a read lock.
81
110
*
82
- * @param \MongoCollection $collection the lock collection
83
111
* @param mixed $id an id for the lock that used with the other *Lock()/*Unlock() methods.
84
112
* Any type suitable for a mongo _id
85
113
* @param \MongoId $readerId reader id returned from readLock()
86
114
*/
87
- public static function readUnlock (\ MongoCollection $ collection , $ id , \MongoId $ readerId )
115
+ public function readUnlock ($ id , \MongoId $ readerId )
88
116
{
89
- $ collection ->update (['_id ' => $ id ], ['$pull ' => ['readers ' => ['id ' => $ readerId ]]]);
90
- $ collection ->remove (['_id ' => $ id , 'writing ' => false , 'readers ' => ['$size ' => 0 ]]);
117
+ $ this -> collection ->update (['_id ' => $ id ], ['$pull ' => ['readers ' => ['id ' => $ readerId ]]]);
118
+ $ this -> collection ->remove (['_id ' => $ id , 'writing ' => false , 'readers ' => ['$size ' => 0 ]]);
91
119
}
92
120
93
121
/**
94
122
* Get a write lock.
95
123
*
96
- * @param \MongoCollection $collection the lock collection
97
124
* @param mixed $id an id for the lock that used with the other *Lock()/*Unlock() methods.
98
125
* Any type suitable for a mongo _id
99
126
* @param \MongoDate $staleTimestamp time the write is considered stale and can be cleared.
100
127
* (to possibly read/write lock)
101
- * @param int $pollDuration duration in microseconds to wait inbetween lock attempts
102
128
* @param int $timeoutTimestamp a unix timestamp to stop waiting and throw an exception
103
129
*
104
130
* @throws \Exception
105
131
*/
106
- public static function writeLock (
107
- \MongoCollection $ collection ,
108
- $ id ,
109
- \MongoDate $ staleTimestamp ,
110
- $ pollDuration = 100000 ,
111
- $ timeoutTimestamp = PHP_INT_MAX
112
- ) {
113
- if (!is_int ($ pollDuration ) || $ pollDuration < 0 ) {
114
- throw new \InvalidArgumentException ('$pollDuration must be an int >= 0 ' );
115
- }
116
-
117
- if (!is_int ($ timeoutTimestamp )) {
118
- throw new \InvalidArgumentException ('$timeoutTimestamp must be an int ' );
119
- }
132
+ public function writeLock ($ id , \MongoDate $ staleTimestamp )
133
+ {
134
+ $ timeoutTimestamp = (int )min (time () + $ this ->timeoutDuration , PHP_INT_MAX );
120
135
121
136
while (time () < $ timeoutTimestamp ) {
122
137
$ query = ['_id ' => $ id , 'writing ' => false , 'readers ' => ['$size ' => 0 ]];
@@ -128,7 +143,7 @@ public static function writeLock(
128
143
'readers ' => [],
129
144
];
130
145
try {
131
- if ($ collection ->update ($ query , $ update , ['upsert ' => true ])['n ' ] === 1 ) {
146
+ if ($ this -> collection ->update ($ query , $ update , ['upsert ' => true ])['n ' ] === 1 ) {
132
147
return ;
133
148
}
134
149
} catch (\MongoException $ e ) {
@@ -137,13 +152,13 @@ public static function writeLock(
137
152
}
138
153
}
139
154
140
- if (self :: clearStuckWrite ($ collection , $ id ) || self :: clearStuckRead ($ collection , $ id )) {
155
+ if ($ this -> clearStuckWrite ($ id ) || $ this -> clearStuckRead ($ id )) {
141
156
continue ;
142
157
}
143
158
144
- $ collection ->update (['_id ' => $ id ], ['$set ' => ['writePending ' => true ]]);
159
+ $ this -> collection ->update (['_id ' => $ id ], ['$set ' => ['writePending ' => true ]]);
145
160
146
- usleep ($ pollDuration );
161
+ usleep ($ this -> pollDuration );
147
162
}
148
163
149
164
throw new \Exception ('timed out waiting for lock ' );
@@ -152,27 +167,26 @@ public static function writeLock(
152
167
/**
153
168
* Release a write lock.
154
169
*
155
- * @param \MongoCollection $collection the lock collection
156
170
* @param mixed $id an id for the lock that used with the other *Lock()/*Unlock() methods.
157
171
* Any type suitable for a mongo _id
158
172
*/
159
- public static function writeUnlock (\ MongoCollection $ collection , $ id )
173
+ public function writeUnlock ($ id )
160
174
{
161
- $ collection ->remove (['_id ' => $ id ]);
175
+ $ this -> collection ->remove (['_id ' => $ id ]);
162
176
}
163
177
164
- private static function clearStuckWrite (\ MongoCollection $ collection , $ id )
178
+ private function clearStuckWrite ($ id )
165
179
{
166
- return $ collection ->remove (
180
+ return $ this -> collection ->remove (
167
181
['_id ' => $ id , 'writing ' => true , 'writeStaleTs ' => ['$lte ' => new \MongoDate ()]]
168
182
)['n ' ] === 1 ;
169
183
}
170
184
171
- private static function clearStuckRead (\ MongoCollection $ collection , $ id )
185
+ private function clearStuckRead ($ id )
172
186
{
173
187
$ now = new \MongoDate ();
174
188
$ query = ['_id ' => $ id , 'writing ' => false , 'readers.staleTs ' => ['$lte ' => $ now ]];
175
189
$ update = ['$pull ' => ['readers ' => ['staleTs ' => ['$lte ' => $ now ]]]];
176
- return $ collection ->update ($ query , $ update )['n ' ] === 1 ;
190
+ return $ this -> collection ->update ($ query , $ update )['n ' ] === 1 ;
177
191
}
178
192
}
0 commit comments