Skip to content

Commit 650e030

Browse files
authored
Merge pull request #2 from clegginabox/claude/github-pages-vitepress-L66c0
Move documentation to external site and update README
2 parents 521f12a + 383273f commit 650e030

File tree

6 files changed

+2564
-146
lines changed

6 files changed

+2564
-146
lines changed

.github/workflows/docs.yaml

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
name: Deploy Docs
2+
3+
on:
4+
push:
5+
branches: [main]
6+
workflow_dispatch:
7+
8+
permissions:
9+
contents: read
10+
pages: write
11+
id-token: write
12+
13+
concurrency:
14+
group: pages
15+
cancel-in-progress: false
16+
17+
jobs:
18+
build:
19+
runs-on: ubuntu-latest
20+
steps:
21+
- uses: actions/checkout@v4
22+
with:
23+
fetch-depth: 0
24+
25+
- uses: actions/setup-node@v4
26+
with:
27+
node-version: 22
28+
cache: npm
29+
30+
- run: npm ci
31+
- run: npm run docs:build
32+
33+
- uses: actions/upload-pages-artifact@v3
34+
with:
35+
path: docs/.vitepress/dist
36+
37+
deploy:
38+
environment:
39+
name: github-pages
40+
url: ${{ steps.deployment.outputs.page_url }}
41+
needs: build
42+
runs-on: ubuntu-latest
43+
steps:
44+
- id: deployment
45+
uses: actions/deploy-pages@v4

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,5 +4,6 @@
44
.idea/
55
.DS_Store
66
.phpcs-cache
7+
node_modules/
78
docs/.vitepress/dist
89
docs/.vitepress/cache

README.md

Lines changed: 3 additions & 145 deletions
Original file line numberDiff line numberDiff line change
@@ -25,150 +25,8 @@ British-style queuing for your code and infrastructure. First come, first served
2525
> [!CAUTION]
2626
> **Very Early Work in Progress** - This library is under active development and not yet production-ready. APIs will change, many implementations are stubs and test coverage is incomplete. Use at your own risk, contributions welcome.
2727
28-
## The Core Idea
28+
## Documentation
2929

30-
*Everything has a breaking point.* A database has connection limits. An API has rate limits. A checkout flow falls over if 50,000 people hit it at once.
31-
An airlock sits in front of that thing and makes everyone wait their turn nicely.
30+
Full documentation, guides and examples at **[clegginabox.github.io/airlock-php](https://clegginabox.github.io/airlock-php/)**.
3231

33-
Every Airlock is composed of:
34-
- A Seal — how capacity is enforced (the velvet rope)
35-
- An Admission Strategy — who gets in next (the queue itself)
36-
- An optional Notifier — how waiters are told it’s their turn (the “your table is ready” buzzer)
37-
38-
Swap one piece, get a different system. Same interface, different behaviour. Dead simple.
39-
40-
## Best-Effort / Anti-Hug Gate
41-
42-
No fairness guarantees. First request to hit free capacity wins. If two requests arrive at the same time, one gets in and one doesn’t — and there’s no predicting which.
43-
44-
Fast, simple, resilient. Perfect for protecting an endpoint from the hug of death when you don’t care who gets through, just how many.
45-
46-
```php
47-
use Clegginabox\Airlock\Bridge\Symfony\Seal\SymfonySemaphoreSeal;use Clegginabox\Airlock\OpportunisticAirlock;use Symfony\Component\Semaphore\SemaphoreFactory;use Symfony\Component\Semaphore\Store\RedisStore;
48-
49-
$redis = new Redis();
50-
$redis->connect('127.0.0.1');
51-
52-
// Allow up to N concurrent “expensive” requests.
53-
$seal = new SymfonySemaphoreSeal(
54-
factory: new SemaphoreFactory(new RedisStore($redis)),
55-
resource: 'site_capacity',
56-
limit: 20,
57-
ttlInSeconds: 30,
58-
autoRelease: false,
59-
);
60-
61-
$airlock = new OpportunisticAirlock($seal);
62-
63-
$result = $airlock->enter($clientId);
64-
```
65-
66-
## Strict Fairness (FIFO)
67-
68-
The proper British queue. Exact arrival order. Deterministic. No cutting, no exceptions.
69-
70-
If someone in front of you wanders off to browse the shop, the whole queue waits. Dead heads must be handled explicitly — the system won’t assume they’ve left just because they’ve gone quiet.
71-
72-
```php
73-
use Clegginabox\Airlock\QueueAirlock;
74-
use Clegginabox\Airlock\Queue\RedisFifoQueue;
75-
76-
$seal = new SemaphoreSeal(... limit: 50);
77-
$queue = new RedisFifoQueue($redis);
78-
79-
$airlock = new QueueAirlock($seal, $queue);
80-
81-
$result = $airlock->enter($userId);
82-
```
83-
84-
## Lottery (Fast, Unfair)
85-
86-
No ordering. High throughput. Self-healing under disconnects.
87-
88-
The Ryanair boarding approach. Priority boarding means nowt when everyone’s already elbowing toward the gate. If someone disconnects, they simply drop out of the draw — no cleanup required.
89-
90-
```php
91-
use Clegginabox\Airlock\QueueAirlock;
92-
use Clegginabox\Airlock\Queue\RedisLotteryQueue;
93-
94-
$seal = new SemaphoreSeal(... limit: 50);
95-
$queue = new RedisLotteryQueue($redis);
96-
97-
$airlock = new QueueAirlock($seal, $queue);
98-
```
99-
100-
## Aging Lottery (Fair-ish)
101-
102-
Same as the lottery, but the longer you wait, the better your odds. Eventually even the unluckiest punter gets through.
103-
104-
The “I’ve been waiting ages, surely it’s my turn” system. Not strictly fair, but feels fairer — and sometimes that’s what matters.
105-
106-
```php
107-
use Clegginabox\Airlock\QueueAirlock;
108-
use Clegginabox\Airlock\Queue\RedisLotteryQueue;
109-
110-
$seal = new SemaphoreSeal(... limit: 50);
111-
$queue = new RedisAgingLotteryQueue($redis);
112-
113-
$airlock = new QueueAirlock($seal, $queue);
114-
```
115-
116-
## Priority Queue (Logged-in Users First)
117-
118-
Higher priority users jump ahead. FIFO within the same tier. Guests wait, members skip the line.
119-
120-
The members’ entrance at the club. You’re still queuing, just… better.
121-
122-
```php
123-
use Clegginabox\Airlock\QueueAirlock;
124-
use Clegginabox\Airlock\Queue\PriorityQueue;
125-
126-
$seal = new SemaphoreSeal(... limit: 50);
127-
$queue = new PriorityQueue($redis);
128-
129-
$airlock = new QueueAirlock($seal, $queue);
130-
131-
// Priority: higher = better. Logged-in users get priority 10, guests get 0.
132-
$priority = $user->isLoggedIn() ? 10 : 0;
133-
134-
$result = $airlock->enter($userId, $priority);
135-
```
136-
137-
Tiered priorities work too - VIPs at 100, paid members at 50, free users at 10, anonymous at 0.
138-
139-
## Singleton / Idempotency
140-
141-
Exactly one at a time. No queue, no waiting room UI — just a simple “is someone already doing this?” check.
142-
143-
Perfect for cron jobs that must never overlap, or user actions that shouldn’t fire twice if they double-click. The “we’re not having two of those” approach.
144-
145-
```php
146-
use Clegginabox\Airlock\Bridge\Symfony\Seal\SymfonyLockSeal;use Clegginabox\Airlock\OpportunisticAirlock;
147-
148-
$seal = new SymfonyLockSeal();
149-
$airlock = new OpportunisticAirlock($seal);
150-
151-
$airlock->withAdmitted('job:invoice', function () {
152-
// guaranteed single-flight
153-
});
154-
```
155-
156-
## When Not to Use Airlock
157-
158-
Airlock is not trying to be Cloudflare. If you’re selling Glastonbury tickets to the entire country at once, you need infrastructure with a budget bigger than this library’s test coverage.
159-
160-
Airlock is for the stuff in between. The internal dashboard that falls over when someone sends a company-wide email. The checkout flow that can’t handle a flash sale. The webhook endpoint that your biggest customer keeps hammering.
161-
162-
It’s probably not the right fit if:
163-
- **You just want to return 429s** — A rate limiter is simpler. Airlock assumes callers are willing to wait their turn.
164-
- **Waiting is not an option** — If requests must fail immediately, you want fail-fast guards, not admission control.
165-
166-
## Plans/Ideas/Roadmap
167-
168-
- Queues (Aging Lottery, Priority, MaxWait, RetryPenalty)
169-
- Symfony integration (Lock, Semaphore & RateLimiter)
170-
- Laravel integration (Lock & RateLimiter)
171-
- AMPHP integration (amphp/sync)
172-
- Extend Symfony Semaphore with more storage backends (Remote, DynamoDB)
173-
- Cloudflare Durable Objects integration
174-
- Composite Seal (combine RateLimiter + Lock/Semaphore)
32+
See the [example app](https://airlock.clegginabox.co.uk/recipes) for real-world usage patterns.

docs/.vitepress/config.mts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ import { defineConfig } from 'vitepress'
22

33
// https://vitepress.dev/reference/site-config
44
export default defineConfig({
5+
base: '/airlock-php/',
56
title: "Airlock",
67
description: "British-style queuing for your infrastructure.",
78
themeConfig: {

0 commit comments

Comments
 (0)