Skip to content

Commit a5d08a2

Browse files
author
Jonathan Gaillard
committed
Turn $staleTimestamp into $staleDuration for readLock()/writeLock().
1 parent 562e2ff commit a5d08a2

File tree

2 files changed

+117
-54
lines changed

2 files changed

+117
-54
lines changed

src/Locker.php

Lines changed: 14 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -67,16 +67,21 @@ public function __construct(\MongoCollection $collection, $pollDuration = 100000
6767
*
6868
* @param mixed $id an id for the lock that used with the other *Lock()/*Unlock() methods.
6969
* Any type suitable for a mongo _id
70-
* @param \MongoDate $staleTimestamp time the read is considered stale and can be cleared.
70+
* @param int $staleDuration duration in seconds before the read is considered stale and can be cleared.
7171
* (to possibly write lock if no more readers)
7272
*
7373
* @throws \Exception
7474
*
7575
* @return \MongoId a reader id to be given to readUnlock()
7676
*/
77-
public function readLock($id, \MongoDate $staleTimestamp)
77+
public function readLock($id, $staleDuration)
7878
{
79+
if (!is_int($staleDuration) || $staleDuration < 0) {
80+
throw new \InvalidArgumentException('$staleDuration must be an int >= 0');
81+
}
82+
7983
$timeoutTimestamp = (int)min(time() + $this->timeoutDuration, PHP_INT_MAX);
84+
$staleTimestamp = new \MongoDate((int)min(time() + $staleDuration, PHP_INT_MAX));
8085

8186
while (time() < $timeoutTimestamp) {
8287
$readerId = new \MongoId();
@@ -123,15 +128,20 @@ public function readUnlock($id, \MongoId $readerId)
123128
*
124129
* @param mixed $id an id for the lock that used with the other *Lock()/*Unlock() methods.
125130
* Any type suitable for a mongo _id
126-
* @param \MongoDate $staleTimestamp time the write is considered stale and can be cleared.
131+
* @param int $staleDuration duration in seconds before the write is considered stale and can be cleared.
127132
* (to possibly read/write lock)
128133
* @param int $timeoutTimestamp a unix timestamp to stop waiting and throw an exception
129134
*
130135
* @throws \Exception
131136
*/
132-
public function writeLock($id, \MongoDate $staleTimestamp)
137+
public function writeLock($id, $staleDuration)
133138
{
139+
if (!is_int($staleDuration) || $staleDuration < 0) {
140+
throw new \InvalidArgumentException('$staleDuration must be an int >= 0');
141+
}
142+
134143
$timeoutTimestamp = (int)min(time() + $this->timeoutDuration, PHP_INT_MAX);
144+
$staleTimestamp = new \MongoDate((int)min(time() + $staleDuration, PHP_INT_MAX));
135145

136146
while (time() < $timeoutTimestamp) {
137147
$query = ['_id' => $id, 'writing' => false, 'readers' => ['$size' => 0]];

tests/LockerTest.php

Lines changed: 103 additions & 50 deletions
Original file line numberDiff line numberDiff line change
@@ -26,41 +26,41 @@ public function setUp()
2626
*/
2727
public function writeLockEmptyCollection()
2828
{
29-
$staleTimestamp = new \MongoDate(time() + 1000);
30-
31-
$this->locker->writeLock('theId', $staleTimestamp);
29+
$this->locker->writeLock('theId', 1000);
3230

3331
$this->assertSame(1, $this->collection->count());
34-
$expected = [
35-
'_id' => 'theId',
36-
'writing' => true,
37-
'writeStaleTs' => $staleTimestamp,
38-
'writePending' => false,
39-
'readers' => [],
40-
];
32+
4133
$actual = $this->collection->findOne();
34+
35+
$actualWriteStaleTs = $actual['writeStaleTs']->sec;
36+
unset($actual['writeStaleTs']);
37+
38+
$expected = ['_id' => 'theId', 'readers' => [], 'writePending' => false, 'writing' => true];
4239
ksort($actual);
43-
$this->assertEquals($expected, $actual);
40+
$this->assertSame($expected, $actual);
41+
42+
$this->assertGreaterThanOrEqual(time() + 1000, $actualWriteStaleTs);
43+
$this->assertLessThan(time() + 1010, $actualWriteStaleTs);
4444
}
4545

4646
/**
4747
* @test
4848
*/
4949
public function writeLockClearStuckWrite()
5050
{
51-
$this->locker->writeLock('theId', new \MongoDate());
51+
$this->locker->writeLock('theId', 0);
5252

53-
$this->locker->writeLock('theId', new \MongoDate(time() + 1000));
53+
$this->locker->writeLock('theId', 1000);
5454
}
5555

5656
/**
5757
* @test
5858
*/
5959
public function writeLockClearStuckRead()
6060
{
61-
$this->locker->readLock('theId', new \MongoDate());
61+
$this->locker->readLock('theId', 0);
6262

63-
$this->locker->writeLock('theId', new \MongoDate(time() + 1000));
63+
$this->locker->writeLock('theId', 1000);
6464
}
6565

6666
/**
@@ -72,40 +72,66 @@ public function writeLockTimeout()
7272
{
7373
$locker = new Locker($this->collection, 100000, 1);
7474

75-
$locker->writeLock('theId', new \MongoDate(time() + 1000));
76-
$locker->writeLock('theId', new \MongoDate(time() + 1000));
75+
$locker->writeLock('theId', 1000);
76+
$locker->writeLock('theId', 1000);
7777
}
7878

7979
/**
8080
* @test
81+
* @expectedException \InvalidArgumentException
82+
* @expectedExceptionMessage $staleDuration must be an int >= 0
8183
*/
82-
public function readLockEmptyCollection()
84+
public function writeLockNonIntStaleDuration()
85+
{
86+
(new Locker($this->collection))->writeLock('theId', true);
87+
}
88+
89+
/**
90+
* @test
91+
* @expectedException \InvalidArgumentException
92+
* @expectedExceptionMessage $staleDuration must be an int >= 0
93+
*/
94+
public function writeLockNegativeStaleDuration()
8395
{
84-
$staleTimestamp = new \MongoDate(time() + 1000);
96+
(new Locker($this->collection))->writeLock('theId', -1);
97+
}
8598

86-
$readerId = $this->locker->readLock('theId', $staleTimestamp);
99+
/**
100+
* @test
101+
*/
102+
public function readLockEmptyCollection()
103+
{
104+
$readerId = $this->locker->readLock('theId', 1000);
87105

88106
$this->assertSame(1, $this->collection->count());
89-
$expected = [
90-
'_id' => 'theId',
91-
'writing' => false,
92-
'writePending' => false,
93-
'readers' => [['id' => $readerId, 'staleTs' => $staleTimestamp]],
94-
'writeStaleTs' => null,
95-
];
107+
96108
$actual = $this->collection->findOne();
109+
110+
$actualReaders = $actual['readers'];
111+
unset($actual['readers']);
112+
113+
$expected = ['_id' => 'theId', 'writePending' => false, 'writeStaleTs' => null, 'writing' => false];
114+
97115
ksort($actual);
98-
$this->assertEquals($expected, $actual);
116+
$this->assertSame($expected, $actual);
117+
118+
$this->assertCount(1, $actualReaders);
119+
$this->assertCount(2, $actualReaders[0]);
120+
121+
$this->assertInstanceOf('\MongoId', $actualReaders[0]['id']);
122+
123+
$this->assertGreaterThanOrEqual(time() + 1000, $actualReaders[0]['staleTs']->sec);
124+
$this->assertLessThan(time() + 1010, $actualReaders[0]['staleTs']->sec);
99125
}
100126

101127
/**
102128
* @test
103129
*/
104130
public function readLockClearStuck()
105131
{
106-
$this->locker->writeLock('theId', new \MongoDate());
132+
$this->locker->writeLock('theId', 0);
107133

108-
$this->locker->readLock('theId', new \MongoDate(time() + 1000));
134+
$this->locker->readLock('theId', 1000);
109135
}
110136

111137
/**
@@ -117,16 +143,36 @@ public function readLockTimeout()
117143
{
118144
$locker = new Locker($this->collection, 100000, 1);
119145

120-
$locker->writeLock('theId', new \MongoDate(time() + 1000));
121-
$locker->readLock('theId', new \MongoDate(time() + 1000));
146+
$locker->writeLock('theId', 1000);
147+
$locker->readLock('theId', 1000);
148+
}
149+
150+
/**
151+
* @test
152+
* @expectedException \InvalidArgumentException
153+
* @expectedExceptionMessage $staleDuration must be an int >= 0
154+
*/
155+
public function readLockNonIntStaleDuration()
156+
{
157+
(new Locker($this->collection))->readLock('theId', true);
158+
}
159+
160+
/**
161+
* @test
162+
* @expectedException \InvalidArgumentException
163+
* @expectedExceptionMessage $staleDuration must be an int >= 0
164+
*/
165+
public function readLockNegativeStaleDuration()
166+
{
167+
(new Locker($this->collection))->readLock('theId', -1);
122168
}
123169

124170
/**
125171
* @test
126172
*/
127173
public function writeUnlock()
128174
{
129-
$this->locker->writeLock('theId', new \MongoDate(time() + 1000));
175+
$this->locker->writeLock('theId', 1000);
130176
$this->locker->writeUnlock('theId');
131177

132178
$this->assertSame(0, $this->collection->count());
@@ -137,7 +183,7 @@ public function writeUnlock()
137183
*/
138184
public function readUnlockEmptyCollection()
139185
{
140-
$readerId = $this->locker->readLock('theId', new \MongoDate(time() + 1000));
186+
$readerId = $this->locker->readLock('theId', 1000);
141187
$this->locker->readUnlock('theId', $readerId);
142188

143189
$this->assertSame(0, $this->collection->count());
@@ -148,23 +194,30 @@ public function readUnlockEmptyCollection()
148194
*/
149195
public function readUnlockExistingReader()
150196
{
151-
$existingStaleTimestamp = new \MongoDate(time() + 1000);
152-
$existingReaderId = $this->locker->readLock('theId', $existingStaleTimestamp);
197+
$existingReaderId = $this->locker->readLock('theId', 1000);
153198

154-
$readerId = $this->locker->readLock('theId', new \MongoDate(time() + 1000));
199+
$readerId = $this->locker->readLock('theId', 1000);
155200
$this->locker->readUnlock('theId', $readerId);
156201

157202
$this->assertSame(1, $this->collection->count());
158-
$expected = [
159-
'_id' => 'theId',
160-
'writing' => false,
161-
'writePending' => false,
162-
'readers' => [['id' => $existingReaderId, 'staleTs' => $existingStaleTimestamp]],
163-
'writeStaleTs' => null,
164-
];
203+
165204
$actual = $this->collection->findOne();
205+
206+
$actualReaders = $actual['readers'];
207+
unset($actual['readers']);
208+
209+
$expected = ['_id' => 'theId', 'writePending' => false, 'writeStaleTs' => null, 'writing' => false];
210+
166211
ksort($actual);
167-
$this->assertEquals($expected, $actual);
212+
$this->assertSame($expected, $actual);
213+
214+
$this->assertCount(1, $actualReaders);
215+
$this->assertCount(2, $actualReaders[0]);
216+
217+
$this->assertInstanceOf('\MongoId', $actualReaders[0]['id']);
218+
219+
$this->assertGreaterThanOrEqual(time() + 1000, $actualReaders[0]['staleTs']->sec);
220+
$this->assertLessThan(time() + 1010, $actualReaders[0]['staleTs']->sec);
168221
}
169222

170223
/**
@@ -178,7 +231,7 @@ public function twoWriters()
178231
$locker = new Locker($db->selectCollection('locks'), 0);
179232

180233
for ($i = 0; $i < 500; ++$i) {
181-
$locker->writeLock('theId', new \MongoDate(time() + 1000));
234+
$locker->writeLock('theId', 1000);
182235

183236
$dataCollection->update(['_id' => 1], ['_id' => 1, 'key' => $keyOne], ['upsert' => true]);
184237
$dataCollection->update(['_id' => 2], ['_id' => 2, 'key' => $keyTwo], ['upsert' => true]);
@@ -219,7 +272,7 @@ public function oneWriterOneReader()
219272
$locker = new Locker($db->selectCollection('locks'), 0);
220273

221274
while (true) {
222-
$readerId = $locker->readLock('theId', new \MongoDate(time() + 1000));
275+
$readerId = $locker->readLock('theId', 1000);
223276

224277
$docs = iterator_to_array($dataCollection->find([], ['_id' => 0])->sort(['_id' => 1]), false);
225278
if ($docs !== [] && $docs !== [['key' => 1], ['key' => 2], ['key' => 3]]) {
@@ -236,7 +289,7 @@ public function oneWriterOneReader()
236289
$locker = new Locker($db->selectCollection('locks'), 0);
237290

238291
for ($i = 0; $i < 1000; ++$i) {
239-
$locker->writeLock('theId', new \MongoDate(time() + 1000));
292+
$locker->writeLock('theId', 1000);
240293

241294
$dataCollection->update(['_id' => 1], ['_id' => 1, 'key' => 1], ['upsert' => true]);
242295
$dataCollection->update(['_id' => 2], ['_id' => 2, 'key' => 2], ['upsert' => true]);
@@ -271,7 +324,7 @@ public function twoWritersTwoReaders()
271324
$locker = new Locker($db->selectCollection('locks'), 0);
272325

273326
while (true) {
274-
$readerId = $locker->readLock('theId', new \MongoDate(time() + 1000));
327+
$readerId = $locker->readLock('theId', 1000);
275328

276329
$docs = iterator_to_array($dataCollection->find([], ['_id' => 0])->sort(['_id' => 1]), false);
277330
if ($docs !== [] &&
@@ -290,7 +343,7 @@ public function twoWritersTwoReaders()
290343
$locker = new Locker($db->selectCollection('locks'), 0);
291344

292345
for ($i = 0; $i < 200; ++$i) {
293-
$locker->writeLock('theId', new \MongoDate(time() + 1000));
346+
$locker->writeLock('theId', 1000);
294347

295348
$dataCollection->update(['_id' => 1], ['_id' => 1, 'key' => $keyOne], ['upsert' => true]);
296349
$dataCollection->update(['_id' => 2], ['_id' => 2, 'key' => $keyTwo], ['upsert' => true]);

0 commit comments

Comments
 (0)