Skip to content

Commit 562e2ff

Browse files
author
Jonathan Gaillard
committed
Merge pull request #6 from nubs/master
Convert Locker to be instantiated.
2 parents cda65aa + 6bb24ad commit 562e2ff

File tree

2 files changed

+162
-182
lines changed

2 files changed

+162
-182
lines changed

src/Locker.php

Lines changed: 65 additions & 51 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,27 @@
33

44
final class Locker
55
{
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+
627
/**
728
* Lock docs look like:
829
* [
@@ -20,34 +41,42 @@ final class Locker
2041
*/
2142

2243
/**
23-
* Get a read lock.
44+
* Initialize the locker.
2445
*
2546
* @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+
*
2668
* @param mixed $id an id for the lock that used with the other *Lock()/*Unlock() methods.
2769
* Any type suitable for a mongo _id
2870
* @param \MongoDate $staleTimestamp time the read is considered stale and can be cleared.
2971
* (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
3272
*
3373
* @throws \Exception
3474
*
3575
* @return \MongoId a reader id to be given to readUnlock()
3676
*/
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);
5180

5281
while (time() < $timeoutTimestamp) {
5382
$readerId = new \MongoId();
@@ -57,7 +86,7 @@ public static function readLock(
5786
'$set' => ['writeStaleTs' => null],
5887
];
5988
try {
60-
if ($collection->update($query, $update, ['upsert' => true])['n'] === 1) {
89+
if ($this->collection->update($query, $update, ['upsert' => true])['n'] === 1) {
6190
return $readerId;
6291
}
6392
} catch (\MongoException $e) {
@@ -66,11 +95,11 @@ public static function readLock(
6695
}
6796
}
6897

69-
if (self::clearStuckWrite($collection, $id)) {
98+
if ($this->clearStuckWrite($id)) {
7099
continue;
71100
}
72101

73-
usleep($pollDuration);
102+
usleep($this->pollDuration);
74103
}
75104

76105
throw new \Exception('timed out waiting for lock');
@@ -79,44 +108,30 @@ public static function readLock(
79108
/**
80109
* Release a read lock.
81110
*
82-
* @param \MongoCollection $collection the lock collection
83111
* @param mixed $id an id for the lock that used with the other *Lock()/*Unlock() methods.
84112
* Any type suitable for a mongo _id
85113
* @param \MongoId $readerId reader id returned from readLock()
86114
*/
87-
public static function readUnlock(\MongoCollection $collection, $id, \MongoId $readerId)
115+
public function readUnlock($id, \MongoId $readerId)
88116
{
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]]);
91119
}
92120

93121
/**
94122
* Get a write lock.
95123
*
96-
* @param \MongoCollection $collection the lock collection
97124
* @param mixed $id an id for the lock that used with the other *Lock()/*Unlock() methods.
98125
* Any type suitable for a mongo _id
99126
* @param \MongoDate $staleTimestamp time the write is considered stale and can be cleared.
100127
* (to possibly read/write lock)
101-
* @param int $pollDuration duration in microseconds to wait inbetween lock attempts
102128
* @param int $timeoutTimestamp a unix timestamp to stop waiting and throw an exception
103129
*
104130
* @throws \Exception
105131
*/
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);
120135

121136
while (time() < $timeoutTimestamp) {
122137
$query = ['_id' => $id, 'writing' => false, 'readers' => ['$size' => 0]];
@@ -128,7 +143,7 @@ public static function writeLock(
128143
'readers' => [],
129144
];
130145
try {
131-
if ($collection->update($query, $update, ['upsert' => true])['n'] === 1) {
146+
if ($this->collection->update($query, $update, ['upsert' => true])['n'] === 1) {
132147
return;
133148
}
134149
} catch (\MongoException $e) {
@@ -137,13 +152,13 @@ public static function writeLock(
137152
}
138153
}
139154

140-
if (self::clearStuckWrite($collection, $id) || self::clearStuckRead($collection, $id)) {
155+
if ($this->clearStuckWrite($id) || $this->clearStuckRead($id)) {
141156
continue;
142157
}
143158

144-
$collection->update(['_id' => $id], ['$set' => ['writePending' => true]]);
159+
$this->collection->update(['_id' => $id], ['$set' => ['writePending' => true]]);
145160

146-
usleep($pollDuration);
161+
usleep($this->pollDuration);
147162
}
148163

149164
throw new \Exception('timed out waiting for lock');
@@ -152,27 +167,26 @@ public static function writeLock(
152167
/**
153168
* Release a write lock.
154169
*
155-
* @param \MongoCollection $collection the lock collection
156170
* @param mixed $id an id for the lock that used with the other *Lock()/*Unlock() methods.
157171
* Any type suitable for a mongo _id
158172
*/
159-
public static function writeUnlock(\MongoCollection $collection, $id)
173+
public function writeUnlock($id)
160174
{
161-
$collection->remove(['_id' => $id]);
175+
$this->collection->remove(['_id' => $id]);
162176
}
163177

164-
private static function clearStuckWrite(\MongoCollection $collection, $id)
178+
private function clearStuckWrite($id)
165179
{
166-
return $collection->remove(
180+
return $this->collection->remove(
167181
['_id' => $id, 'writing' => true, 'writeStaleTs' => ['$lte' => new \MongoDate()]]
168182
)['n'] === 1;
169183
}
170184

171-
private static function clearStuckRead(\MongoCollection $collection, $id)
185+
private function clearStuckRead($id)
172186
{
173187
$now = new \MongoDate();
174188
$query = ['_id' => $id, 'writing' => false, 'readers.staleTs' => ['$lte' => $now]];
175189
$update = ['$pull' => ['readers' => ['staleTs' => ['$lte' => $now]]]];
176-
return $collection->update($query, $update)['n'] === 1;
190+
return $this->collection->update($query, $update)['n'] === 1;
177191
}
178192
}

0 commit comments

Comments
 (0)