# Redis + Socket.IO multi-instance realtime (Step 26)

This project does **not** use Docker. Run Redis natively on your machine.

## 1. Install Redis (no Docker)

### Windows

Option A — [Memurai](https://www.memurai.com/) (Redis-compatible for Windows), or  
Option B — WSL2 Ubuntu:

```bash
sudo apt update
sudo apt install redis-server
sudo service redis-server start
redis-cli ping   # -> PONG
```

Option C — official Redis for Windows builds / Chocolatey (if available in your environment).

### Linux

```bash
sudo apt update
sudo apt install redis-server
sudo systemctl enable --now redis-server
redis-cli ping
```

### macOS

```bash
brew install redis
brew services start redis
redis-cli ping
```

## 2. Environment variables

Add to `development.env` / `test.env` / deployment secrets:

```env
REDIS_HOST=127.0.0.1
REDIS_PORT=6379
REDIS_PASSWORD=
REDIS_DB=0
PRESENCE_TTL_SECONDS=60
PRESENCE_HEARTBEAT_SECONDS=20
DRIVER_LOCATION_BROADCAST_MIN_INTERVAL_MS=1000
```

Do not commit real Redis passwords.

## 3. Failure / graceful degrade

| Concern | Behavior when Redis is down |
|--------|------------------------------|
| MySQL / REST / Auth / Payments / Rides | Unaffected |
| Socket.IO | In-memory adapter (single Nest instance only) |
| Presence | Process-local fallback Map |
| Driver location throttle | Process-local Map fallback |
| FCM / DB notifications | Unchanged (post-commit) |

Redis is **never** inside DB transactions. Realtime failures never roll back rides.

## 4. Socket.IO Redis adapter

On boot (`main.ts`):

1. `RedisService` connects (optional).
2. `RedisIoAdapter.connectToRedis()` creates pub/sub clients.
3. `app.useWebSocketAdapter(redisIoAdapter)` when Redis is up.

Cross-instance path:

```
Instance A emit → Redis pub/sub → Instance B → clients in room
```

Rooms unchanged:

- `user:{userId}`
- `ride:{rideId}`

Events unchanged:

- `ride.created`, `ride.status_changed`, `ride.cancelled`
- `notification.created`, `ride.driver_location`
- `chat.message_created`, `payment.paid`

## 5. Presence architecture

Temporary Redis keys (TTL = `PRESENCE_TTL_SECONDS`, default 60):

- `presence:user:{userId}:sockets` — SET of socket ids
- `presence:user:{userId}:seen` — lastSeenAt ISO string

Multi-device: user stays **online** while the SET is non-empty.

On JWT-authenticated connect:

1. Join `user:{userId}`
2. `PresenceService.connect(userId, socketId)`
3. Server heartbeat every `PRESENCE_HEARTBEAT_SECONDS` (also client `presence:ping`)

On disconnect: remove socket id; clear keys if count becomes 0.

**Not** the same as `Driverdetail.availabilityStatus` (MySQL business willingness to accept rides).

## 6. Driver location throttle

After MySQL location save:

```
SET driver-location-throttle:{driverId} 1 PX 1000 NX
```

- `OK` → emit `ride.driver_location`
- key exists → skip Socket broadcast (DB row already updated)

Atomic `SET NX PX` — no GET-then-SET race across instances.

## 7. Health

`GET /health` returns Redis `status: up|down` without credentials.

## 8. Single vs multi instance

| Mode | Redis | Broadcasts | Presence / throttle |
|------|-------|------------|---------------------|
| Single | optional | local | local fallback OK |
| Multi | required for correct rooms | Redis adapter | Redis SET / SET NX |
