Skip to content

Commit 7e528cc

Browse files
MagicMock/mock.effective_git_name/130407769905136claude
authored andcommitted
Add bouncer metaphor and refine request-flow diagram
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
1 parent 29c4886 commit 7e528cc

1 file changed

Lines changed: 7 additions & 7 deletions

File tree

  • docs/blog/posts/2026-05-29_unblocking_apps_performance_bottleneck

docs/blog/posts/2026-05-29_unblocking_apps_performance_bottleneck/main.md

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -15,29 +15,29 @@ For a while now there was a problem with the Immich app. On the web it did not s
1515

1616
## How requests travel inside a Shard
1717

18-
Every request that hits a shard goes through Traefik first. It is then decided whether that request may pass on to the app or is blocked. All requests from paired devices may pass on as well as requests that target a public endpoint. This is decided by the shard core application. And that means every request gets forwarded by Traefik to that application where the decision needs to happen.
18+
Every request that hits a shard goes through Traefik first. It is then decided whether that request may pass on to the app or is blocked. All requests from paired devices may pass on as well as requests that target a public endpoint. This is decided by the shard core application. And that means every request gets forwarded by Traefik to that application where the decision needs to happen. It is like a bouncer who has to check your ID for every single sip of your drink, not just once at the door.
1919

2020
In order to make that decision, the shard core needs to query a few bits of information. What kind of app is targeted? How is its permission model configured? What device is requesting access? This is all in the database, so the database needs to be queried. And to query the database, a database connection is required. This connection is not created each time, but pulled from a pool of connections that are standing by and can be used and then returned to the pool.
2121

2222
```mermaid
2323
flowchart TD
2424
Browser([Paired browser])
2525
Traefik[Traefik]
26-
ShardCore[shard_core<br/>/internal/auth]
27-
DB[(Postgres)]
28-
App[App container<br/>e.g. Actual Budget]
26+
ShardCore[Shard Core<br/>/internal/auth]
27+
DB[(Database)]
28+
App[App<br/>e.g. Immich]
2929
3030
Browser -->|HTTPS request| Traefik
3131
Traefik -->|forwardAuth| ShardCore
3232
ShardCore -->|find app + identity| DB
33-
ShardCore -->|200 / 401| Traefik
34-
Traefik -->|forward on 200| App
33+
ShardCore -->|allow/block| Traefik
34+
Traefik -->|forward| App
3535
App -->|response| Browser
3636
```
3737

3838
## The Bottleneck
3939

40-
As it turned out, each single request pulled two connections from the pool to answer the question of whether it is allowed or must be blocked. Also the pool had only four connections available at a time. This is the default setting and I never questioned it, I did not even consciously see it. But when an app opens 30 or 40 requests on its first start, only the first two can be served right away. All the others are waiting for database connections to be returned and then given out again. A whole bunch of requests block for a long time.
40+
As it turned out, each single request pulled two connections from the pool to answer the question of whether it is allowed or must be blocked. Also the pool had only four connections available at a time. This is the default setting and I never questioned it, I did not even consciously see it. But when an app opens 30 or 40 requests on its first start, only the first two can be served right away. All the others are waiting for database connections to be returned and then given out again. A whole bunch of requests block for a long time. The crowd piles up behind the rope while the bouncer works through them two at a time.
4141

4242
In fact, for apps like Immich or Actual, which open lots of connections during their startup, the long tail of piled-up and waiting requests frequently hit the 45-second browser timeout and the app fails to load at all.
4343

0 commit comments

Comments
 (0)