Skip to content

Commit efe22fa

Browse files
Radoslaw Ziemniewiczclaude
andcommitted
The README every package in this framework has
A `Why this exists` naming the one hard requirement — a message has to survive being written down in one process and read back in another, which is why a message carries only what it is about — and a benchmark against symfony/messenger: 1.48 µs a message against 2.29. With the paragraph that matters more than the table: a microsecond either way is not what a queue costs. What a queue costs is the transport, three or four orders of magnitude more than either number, and this measures the part that does not matter. What does matter is that Messenger reaches AMQP, Redis, Doctrine and SQS where this has an array and a directory. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01ARqBYLxrHqevcVmxHkpJcZ
1 parent 0a37e9c commit efe22fa

1 file changed

Lines changed: 61 additions & 5 deletions

File tree

‎README.md‎

Lines changed: 61 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -15,13 +15,30 @@
1515

1616
A simple queue: messages, handlers and workers.
1717

18+
## Why this exists
19+
20+
A queue has one hard requirement and everything else is convenience: **a message has to survive
21+
being written down in one process and read back in another**. That is why a message here carries
22+
only what it is about, and what to do with it is a separate class the container builds — a
23+
message holding its own dependencies cannot be written down at all.
24+
25+
The rest is what an application actually needs from a queue and no more: a delay, named queues,
26+
retries with a backoff, and somewhere for the messages that will not work to sit so they are not
27+
in the way of the ones behind them.
28+
29+
## Requirements
30+
31+
- PHP 8.1 or newer
32+
1833
## Installation
1934

2035
```shell
2136
composer require quillstack/queue
2237
```
2338

24-
## Messages and handlers
39+
## Usage
40+
41+
### Messages and handlers
2542

2643
A message carries only what it is about. What is done with it is a handler, built by the
2744
container, which asks for what it needs through its constructor:
@@ -58,7 +75,7 @@ $handlers->handle(SendWelcomeEmail::class, SendWelcomeEmailHandler::class);
5875
A handler registered for a class answers anything extending or implementing it, so one
5976
handler can take a whole family of messages through an interface they share.
6077

61-
## Pushing
78+
### Pushing
6279

6380
```php
6481
$queue->push(new SendWelcomeEmail('radek@quillstack.com'));
@@ -72,7 +89,7 @@ $queue->push(new SendReminder($id), 'emails', 3600);
7289
$queue->push(new ChargeCard($id), delay: new DateInterval('PT5M'));
7390
```
7491

75-
## Working
92+
### Working
7693

7794
```php
7895
$worker = new Worker($queue, $handlers, $container, tries: 3, backoff: 10);
@@ -90,7 +107,7 @@ nobody handles is set aside at once: waiting will not give it a handler.
90107
$queue->failed(); // the messages which will not be tried again
91108
```
92109

93-
## Where the messages live
110+
### Where the messages live
94111

95112
`ArrayQueue` keeps them for as long as the process runs, which is what a test wants and what
96113
a single request needs when whatever it queued is handled before the response goes out.
@@ -105,12 +122,41 @@ $queue = new FileQueue(new LocalStorage(), __DIR__ . '/var/queue');
105122
The file is taken away before the message is handed over, so two workers reading the same
106123
directory cannot both be given it.
107124

108-
## Time
125+
### Time
109126

110127
Both take a PSR-20 clock, which decides when a message is due. Without one they read the
111128
wall clock. `Quillstack\Clock\FrozenClock` stands still until it is moved, so a test can
112129
watch a delay pass without waiting for it.
113130

131+
## Benchmark
132+
133+
Measured with [quillstack/benchmark](https://github.com/quillstack/benchmark) on a thousand
134+
messages pushed and handled in memory. Runs are interleaved and unconcurrent, each figure is the
135+
median of five, and PHP is 8.5.7.
136+
137+
| | Version |
138+
| --- | --- |
139+
| quillstack/queue | v0.6.0 |
140+
| symfony/messenger | v7.4.17 |
141+
142+
| | Per message | Relative |
143+
| --- | --- | --- |
144+
| **quillstack/queue** | **1.48 µs** | — |
145+
| symfony/messenger | 2.29 µs | 1.5× |
146+
147+
**A microsecond either way is not what a queue costs.** What a queue costs is the transport — a
148+
row written to a database, a message sent to Redis, an HTTP call to SQS — and that is three or
149+
four orders of magnitude more than either number here. This table measures the part that does not
150+
matter, and says so.
151+
152+
What matters is what each reaches. `symfony/messenger` has transports for AMQP, Redis, Doctrine,
153+
Amazon SQS, Beanstalkd and more, message serialization across processes, routing rules, retry
154+
strategies you can replace, failure transports, and a middleware stack around handling. This has
155+
an array, a directory of files, and a worker.
156+
157+
If your messages leave this machine, use Messenger. If they need to survive a restart and be
158+
picked up by a worker on the same box, this is a great deal less to understand.
159+
114160
## Tests
115161

116162
```shell
@@ -123,6 +169,16 @@ Coverage needs phpdbg:
123169
composer test:coverage
124170
```
125171

172+
## The rest of Quillstack
173+
174+
This is one component of [Quillstack](https://github.com/quillstack), a PHP framework which is
175+
as simple to use as it is strict about what it does.
176+
177+
- [quillstack/di](https://github.com/quillstack/di) — what builds a handler
178+
- [quillstack/clock](https://github.com/quillstack/clock) — what decides whether a message is due
179+
- [quillstack/logger](https://github.com/quillstack/logger) — where a message that failed is written
180+
- [quillstack/framework](https://github.com/quillstack/framework) — where the worker command lives
181+
126182
## License
127183

128184
MIT. See [LICENSE](LICENSE).

0 commit comments

Comments
 (0)