Skip to content

Commit c403622

Browse files
authored
docs(adr): compress to 3 records in the domain-modeling format (#81)
Rewrite each surviving record as a single paragraph, dropping the **Decision:** line, the ## Context / ## Decision & rationale headings and the **Revisit trigger:** section. Dropped: 0002-keep-construction-factories - keeping two single-caller constructor-bundling factories is trivially reversible and was not a real trade-off, so it fails the admission test. Its rule, that the package mirrors FastStream's own Redis broker layout, is folded into the new 0002. Renumbered: 0003-no-per-call-middlewares-kwarg -> 0002, 0004-typing-extensions-over-version-gated-imports -> 0003. No merges.
1 parent 8a24295 commit c403622

7 files changed

Lines changed: 42 additions & 162 deletions
Lines changed: 12 additions & 46 deletions
Original file line numberDiff line numberDiff line change
@@ -1,46 +1,12 @@
1-
# The fake broker keeps immediate delivery; no in-memory `TimerStore`
2-
3-
**Decision:** `TestTimersBroker` keeps its current model — the fake producer encodes and dispatches
4-
every published Timer immediately to the handler via `process_message`, regardless of its Activation
5-
time. We do **not** add an `InMemoryTimerStore` adapter or a `TimerStore` `Protocol` for testing.
6-
7-
## Context
8-
9-
An architecture review proposed that "the fake fakes the wrong layer": put a real
10-
`InMemoryTimerStore` behind the `TimerStore` seam so the fake broker exercises real Claim / Lease /
11-
Commit and truthful inspection, instead of stubbing the Redis client and dispatching immediately.
12-
Three defects were claimed: (1) the fake duplicates the envelope encode; (2) `has_pending`,
13-
`get_pending_timers` and `cancel_all` are stubbed to canned empties ("lies"); (3) at-least-once and
14-
Lease semantics cannot be tested through the fake. The options weighed were: A — drive the *real*
15-
subscriber poll loop against an in-memory store; B — a real in-memory store for state with
16-
deterministic delivery; or drop it.
17-
18-
## Decision & rationale
19-
20-
Researching FastStream's own Redis broker (`faststream/redis/testing.py`) dismantled the premises:
21-
22-
- **The encode is not duplicated.** FastStream's `FakeProducer.publish` also encodes via
23-
`build_message` / `message_format.encode` — encoding is inherent to the fake-producer pattern, not
24-
a smell. `FakeTimersProducer` mirrors it.
25-
- **The inspection stubs are not lies.** FastStream fakes *every* subscriber — including the polling
26-
list subscriber, our closest analog — by bypassing the poll loop and calling
27-
`handler.process_message` directly from the fake producer; it never runs the real loop in tests.
28-
Under an immediate-delivery contract a published Timer has already fired and been removed, so
29-
`has_pending → False`, `get_pending_timers → []` and `cancel_all → 0` are *truthful*, not stubbed
30-
lies.
31-
- **Option A is non-idiomatic and fragile.** Running the real `_consume` loop under FastStream's
32-
`TestBroker` means skipping `_fake_start` — forfeiting the handler-mock wiring every FastStream
33-
test broker relies on — and driving an infinite 50 ms poll loop inside a unit test; a spike doing
34-
so hung at teardown.
35-
36-
That leaves only one way to make inspection report *pending* future Timers: stop delivering future
37-
Timers immediately. That is a **breaking change** to a public testing API — existing user tests
38-
publish a future Timer and expect their handler to fire without waiting — so it is rejected. With
39-
immediate delivery retained the inspection methods are already correct, and the proposal has no
40-
remaining defect to fix. `scheduled_timers` already lets users assert what was Scheduled.
41-
42-
**Revisit trigger:** a concrete need to test Schedule / Pending / cancel semantics — a future Timer
43-
observed as Pending before it fires — through the fake. If that arises, prefer an **opt-in**
44-
`TestTimersBroker(..., respect_activation=True)` (default off, so non-breaking) that withholds
45-
not-yet-Due Timers; not a change to the default immediate-delivery contract, and not a real
46-
subscriber loop in tests.
1+
# The fake broker delivers every Timer immediately
2+
3+
`FakeTimersProducer` encodes each published Timer and hands it straight to the matching subscriber's
4+
`process_message`, ignoring the Activation time, and `_patch_broker` stubs the Redis client so
5+
`has_pending` is False, `get_pending_timers` empty and `cancel_all` zero. Putting a real in-memory
6+
store behind the `TimerStore` seam, so the fake exercised Claim, Lease and Commit, was rejected:
7+
FastStream fakes every subscriber, its polling list subscriber included, by bypassing the poll loop,
8+
and under immediate delivery a published Timer has already fired, so those inspection results are
9+
truthful rather than canned. Driving the real `_consume` loop under `TestBroker` means skipping
10+
`_fake_start` and running a 50 ms poll loop inside a unit test; a spike doing so hung at teardown.
11+
Reporting a future Timer as Pending requires withholding it, which breaks every user test that
12+
publishes one and expects its handler to fire, so it could only arrive as an opt-in flag.

‎docs/adr/0002-keep-construction-factories.md‎

Lines changed: 0 additions & 40 deletions
This file was deleted.
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
# No per-call `middlewares=` on subscribers, publishers and routes
2+
3+
`TimersRegistrator.subscriber()`, `.publisher()`, `TimersRoute` and `TimersRoutePublisher` take no
4+
`middlewares=` keyword; `TimersBroker` and `TimersRouter` keep theirs, and broker or router scope is
5+
the only way to wrap timer handling. FastStream 0.7 removed `add_call(middlewares_=...)`, so the
6+
forwarding target no longer exists. Routing the kwarg to broker scope internally would have been a
7+
silent change of meaning, since broker middleware runs for every Topic, and re-implementing
8+
per-subscriber middleware locally would reproduce what upstream had just deleted while binding us to
9+
an internal we had stopped depending on. The package was at version `"0"` with no stability promise,
10+
so a hard break users see at import time was in policy. Behaving exactly like the brokers FastStream
11+
ships is this integration's value, which is also why the single-caller `create_publisher` and
12+
`create_subscriber` factories stay rather than being inlined into the registrator: they mirror
13+
FastStream's own Redis broker layout. If upstream restores call-scoped middleware the kwarg returns
14+
as a thin forward.

‎docs/adr/0003-no-per-call-middlewares-kwarg.md‎

Lines changed: 0 additions & 35 deletions
This file was deleted.
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
# Backport 3.12-only typing via `typing_extensions`, not version-gated imports
2+
3+
Lowering the supported floor from 3.13 to 3.11 broke exactly two constructs: the PEP 695
4+
`type RedisClient = ...` alias, a `SyntaxError` on 3.11, and `override` imported from `typing`,
5+
which landed in 3.12. The alias became a plain `typing.TypeAlias` annotation, which needs no
6+
backport; `override` is imported from `typing_extensions` unconditionally, never gated on
7+
`sys.version_info`, and `typing-extensions` is a declared direct runtime dependency rather than a
8+
pin borrowed from FastStream, which is free to drop it. A `sys.version_info` gate was rejected
9+
because 3.11 needs the package installed either way, so it buys only more code at every affected
10+
site and a second path for `ty` to check. Dropping `@override` was rejected because it is what
11+
catches a signature drift against FastStream's base classes, precisely the failure this integration
12+
is exposed to. When the floor rises to 3.12 both constructs gain stdlib spellings and the direct
13+
dependency goes with them.

‎docs/adr/0004-typing-extensions-over-version-gated-imports.md‎

Lines changed: 0 additions & 39 deletions
This file was deleted.

‎docs/agents/domain.md‎

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ Single-context repo:
1818
├── CONTEXT.md
1919
├── docs/adr/
2020
│ ├── 0001-fake-broker-keeps-immediate-delivery.md
21-
│ └── 0002-keep-construction-factories.md
21+
│ └── 0002-no-per-call-middlewares-kwarg.md
2222
└── faststream_redis_timers/
2323
```
2424

@@ -32,4 +32,5 @@ If the concept you need isn't in the glossary yet, that's a signal: either you'r
3232

3333
If your output contradicts an existing ADR, surface it explicitly rather than silently overriding:
3434

35-
> _Contradicts ADR-0001 (fake broker keeps immediate delivery), but worth reopening because…_
35+
> _Contradicts ADR-0001 (the fake broker delivers every Timer immediately), but worth reopening
36+
> because…_

0 commit comments

Comments
 (0)