Skip to content

Commit 1c10bcd

Browse files
committed
adding InMemoryFifoQueueStore
1 parent b82df0b commit 1c10bcd

File tree

13 files changed

+278
-24
lines changed

13 files changed

+278
-24
lines changed

README.md

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,11 @@
1-
# airlock-php
1+
# airlock-php - Distributed locking with manners
2+
23
[![Codacy Badge](https://app.codacy.com/project/badge/Grade/cdc12dbceac04dc8bbece4012222cd3d)](https://app.codacy.com/gh/clegginabox/airlock-php/dashboard?utm_source=gh&utm_medium=referral&utm_content=&utm_campaign=Badge_grade)
34
![Tests](https://github.com/clegginabox/airlock-php/actions/workflows/tests.yaml/badge.svg)
45
[![Codacy Badge](https://app.codacy.com/project/badge/Coverage/cdc12dbceac04dc8bbece4012222cd3d)](https://app.codacy.com/gh/clegginabox/airlock-php/dashboard?utm_source=gh&utm_medium=referral&utm_content=&utm_campaign=Badge_coverage)
56

67
<img width="830" height="453" alt="airlock-php-red" src="https://github.com/user-attachments/assets/361fb9d2-00a4-4a11-b8cf-cde4fc951b9f" />
78

8-
9-
# Distributed locking with manners
10-
119
British-style queuing for your code and infrastructure. First come, first served. As it should be.
1210

1311
(Not to be confused with a message queue. Airlock doesn’t process messages — it just decides who’s coming in and who’s staying outside in the rain.)

rector.php

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,6 @@
33
declare(strict_types=1);
44

55
use Rector\Config\RectorConfig;
6-
use Rector\PHPUnit\Set\PHPUnitSetList;
76

87
return RectorConfig::configure()
98
->withComposerBased()

src/Bridge/Symfony/Seal/SymfonySemaphoreSeal.php

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,6 @@
55
namespace Clegginabox\Airlock\Bridge\Symfony\Seal;
66

77
use Clegginabox\Airlock\Exception\LeaseExpiredException;
8-
use Clegginabox\Airlock\Exception\SealAcquiringException;
98
use Clegginabox\Airlock\Exception\SealReleasingException;
109
use Clegginabox\Airlock\Seal\RefreshableSeal;
1110
use Clegginabox\Airlock\Seal\ReleasableSeal;
@@ -28,7 +27,7 @@ public function __construct(
2827
) {
2928
}
3029

31-
public function tryAcquire(): SymfonySemaphoreToken
30+
public function tryAcquire(): ?SymfonySemaphoreToken
3231
{
3332
$key = new Key($this->resource, $this->limit, $this->weight);
3433
$semaphore = $this->factory->createSemaphoreFromKey(
@@ -38,7 +37,7 @@ public function tryAcquire(): SymfonySemaphoreToken
3837
);
3938

4039
if (!$semaphore->acquire()) {
41-
throw new SealAcquiringException('Unable to acquire semaphore');
40+
return null;
4241
}
4342

4443
return new SymfonySemaphoreToken($key);
Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace Clegginabox\Airlock\Queue\Storage\Fifo;
6+
7+
class InMemoryFifoQueueStore implements FifoQueueStorage
8+
{
9+
/**
10+
* @var list<string>
11+
*/
12+
private array $queue = [];
13+
14+
public function addToBack(string $identifier): int
15+
{
16+
if (!in_array($identifier, $this->queue, true)) {
17+
$this->queue[] = $identifier;
18+
}
19+
20+
return $this->getPosition($identifier);
21+
}
22+
23+
public function remove(string $identifier): void
24+
{
25+
$this->queue = array_values(
26+
array_filter($this->queue, static fn ($id) => $id !== $identifier)
27+
);
28+
}
29+
30+
public function peekFront(): ?string
31+
{
32+
return $this->queue[0] ?? null;
33+
}
34+
35+
public function popFront(): ?string
36+
{
37+
return array_shift($this->queue);
38+
}
39+
40+
public function getPosition(string $identifier): ?int
41+
{
42+
$index = array_search($identifier, $this->queue, true);
43+
44+
return $index === false ? null : $index + 1;
45+
}
46+
47+
public function contains(string $identifier): bool
48+
{
49+
return in_array($identifier, $this->queue, true);
50+
}
51+
52+
public function clear(): void
53+
{
54+
$this->queue = [];
55+
}
56+
}

src/Queue/Storage/Fifo/Redis/RedisFifoQueueStore.php renamed to src/Queue/Storage/Fifo/RedisFifoQueueStore.php

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,8 @@
22

33
declare(strict_types=1);
44

5-
namespace Clegginabox\Airlock\Queue\Storage\Fifo\Redis;
5+
namespace Clegginabox\Airlock\Queue\Storage\Fifo;
66

7-
use Clegginabox\Airlock\Queue\Storage\Fifo\FifoQueueStorage;
87
use Redis;
98

109
/**

src/Queue/Storage/Lottery/Redis/RedisLotteryQueueStore.php renamed to src/Queue/Storage/Lottery/RedisLotteryQueueStore.php

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,8 @@
22

33
declare(strict_types=1);
44

5-
namespace Clegginabox\Airlock\Queue\Storage\Lottery\Redis;
5+
namespace Clegginabox\Airlock\Queue\Storage\Lottery;
66

7-
use Clegginabox\Airlock\Queue\Storage\Lottery\LotteryQueueStorage;
87
use Redis;
98

109
class RedisLotteryQueueStore implements LotteryQueueStorage

src/Seal/InMemorySeal.php

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace Clegginabox\Airlock\Seal;
6+
7+
class InMemorySeal implements ReleasableSeal, RefreshableSeal
8+
{
9+
public function tryAcquire(): ?SealToken
10+
{
11+
}
12+
13+
public function refresh(SealToken $token, ?float $ttlInSeconds = null): SealToken
14+
{
15+
// TODO: Implement refresh() method.
16+
}
17+
18+
public function release(SealToken $token): void
19+
{
20+
// TODO: Implement release() method.
21+
}
22+
}

src/Seal/InMemorySealToken.php

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace Clegginabox\Airlock\Seal;
6+
7+
class InMemorySealToken implements SealToken
8+
{
9+
public function getResource(): string
10+
{
11+
// TODO: Implement getResource() method.
12+
}
13+
14+
public function getId(): string
15+
{
16+
// TODO: Implement getId() method.
17+
}
18+
19+
public function __toString()
20+
{
21+
// TODO: Implement __toString() method.
22+
}
23+
}

tests/Integration/Bridge/Symfony/SymfonySemaphoreSealTest.php

Lines changed: 5 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,6 @@
66

77
use Clegginabox\Airlock\Bridge\Symfony\Seal\SymfonySemaphoreSeal;
88
use Clegginabox\Airlock\Exception\LeaseExpiredException;
9-
use Clegginabox\Airlock\Exception\SealAcquiringException;
109
use Clegginabox\Airlock\Tests\Factory\RedisFactory;
1110
use PHPUnit\Framework\TestCase;
1211
use Redis;
@@ -48,9 +47,9 @@ public function testItRespectsCapacityLimit(): void
4847
$token2 = $this->constraint->tryAcquire();
4948
$this->assertNotNull($token2, 'Should acquire 2nd slot');
5049

51-
// 3. Try third slot -> Should FAIL (throw SealAcquiringException)
52-
$this->expectException(SealAcquiringException::class);
53-
$this->constraint->tryAcquire();
50+
// 3. Try third slot -> Should FAIL (returns null)
51+
$token3 = $this->constraint->tryAcquire();
52+
$this->assertNull($token3, 'Should not acquire 3rd slot');
5453
}
5554

5655
public function testReleaseOpensSlot(): void
@@ -60,8 +59,8 @@ public function testReleaseOpensSlot(): void
6059
$this->constraint->tryAcquire();
6160

6261
// Verify full
63-
$this->expectException(SealAcquiringException::class);
64-
$this->constraint->tryAcquire();
62+
$token2 = $this->constraint->tryAcquire();
63+
$this->assertNull($token2);
6564

6665
// Release one
6766
$this->constraint->release($token1);

tests/Integration/Queue/FifoQueueTest.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66

77
use Clegginabox\Airlock\Queue\FifoQueue;
88
use Clegginabox\Airlock\Queue\RedisFifoQueue;
9-
use Clegginabox\Airlock\Queue\Storage\Fifo\Redis\RedisFifoQueueStore;
9+
use Clegginabox\Airlock\Queue\Storage\Fifo\RedisFifoQueueStore;
1010
use Clegginabox\Airlock\Tests\Factory\RedisFactory;
1111
use PHPUnit\Framework\TestCase;
1212
use Redis;

0 commit comments

Comments
 (0)