---
id: "stateful-websocket-scale-in-without-drain"
type: "rule"
severity: "warning"
url: "https://localhost:3000/catalogue/rules/stateful-websocket-scale-in-without-drain"
---

# Stateful WebSocket scale-in without drain

An autoscaling realtime gateway does not drain connections when scaling in, so every scale-down is a reconnect storm.

| | |
|---|---|
| Severity | warning |
| Selects | every node of kind realtime-gateway |
| Components in scope | 20 |

## Why

Scaling a gateway down kills every live WebSocket on the removed instance at once. Without a drain step those clients all reconnect together.

## What it checks

It looks at every node of kind realtime-gateway, and fires when:

- all of these hold:
  - the node's `autoscaling` is true
  - the node's `drainOnScaleIn` is false

Message template:

```
Realtime gateway '{{node.id}}' autoscales but does not drain connections on scale-in. Every scale-down becomes a reconnect storm.
```

## Applies to

- [Real-time gateway](https://localhost:3000/catalogue/kinds/realtime-gateway.md) `realtime-gateway`

20 catalogue components fall under it.

## Tripped by

Reference designs whose broken twin names this rule in its header:

- [discord-messages](https://localhost:3000/d/zeema/discord-messages)
- [distributed-email](https://localhost:3000/d/zeema/distributed-email)
- [facebook-messenger](https://localhost:3000/d/zeema/facebook-messenger)
- [figma-multiplayer](https://localhost:3000/d/zeema/figma-multiplayer)
- [linear-sync](https://localhost:3000/d/zeema/linear-sync)
- [nearby-friends](https://localhost:3000/d/zeema/nearby-friends)
- [slack-messaging](https://localhost:3000/d/zeema/slack-messaging)
- [spotify-playback](https://localhost:3000/d/zeema/spotify-playback)
- [twitch-live](https://localhost:3000/d/zeema/twitch-live)
- [whatsapp-messaging](https://localhost:3000/d/zeema/whatsapp-messaging)

## Source (`rule` YAML, verbatim)

```yaml
# yaml-language-server: $schema=../schema/rule.schema.json
# Autoscaling down a gateway that holds live WebSockets kills every connection on the
# removed instance at once. Without a drain step the clients all reconnect together.
# Tripped by: designs/slack-messaging.broken.yaml
schemaVersion: "1"
type: "rule"
id: "stateful-websocket-scale-in-without-drain"
severity: "warning"
select:
  node:
    kind: "realtime-gateway"
when:
  all:
    - prop: "autoscaling"
      equals: true
    - prop: "drainOnScaleIn"
      equals: false
message: "Realtime gateway '{{node.id}}' autoscales but does not drain connections on scale-in. Every scale-down becomes a reconnect storm."
```

Human page: https://localhost:3000/catalogue/rules/stateful-websocket-scale-in-without-drain
