Relay · Limits
限制、錯誤與 backpressure
Relay 會在執行 conversation action 前拒絕超出限制的工作。Client 應依回傳的 retry window 等待;send result 不確定時保留原 message nonce;authorization 或 validation failure 則應視為狀態問題,不是單純的 transport failure。
目前的 default budget
以下是目前的 self-hosted default。Deployment operator 可以為每個 tenant 取代這些設定。
| Budget | Scope | Default |
|---|---|---|
| Message write | Tenant 內單一 user | 每秒 10 次 |
| Message write | 整個 tenant | 每秒 100 次 |
| WebSocket frame | Channel 內單一 user | 每秒 30 次 |
| WebSocket join | User 的獨立 join scope | 每秒 30 次 |
| Attachment presign | Tenant 內單一 user | 每分鐘 30 次 |
| Attachment presign | 整個 tenant | 每分鐘 600 次 |
| Attachment download URL | Tenant 內單一 user | 每分鐘 120 次 |
| Search query | Tenant 內單一 user | 每分鐘 60 次 |
| Search query | 整個 tenant | 每分鐘 600 次 |
| Host credential sync | Tenant | 每秒 20 次 |
| Host credential sync | Source IP | 每秒 60 次 |
| Outbound host webhook | Tenant 與 destination URL | 每秒 50 次 |
Message write budget 適用於 REST message create、edit、retract、pin、unpin 與 reaction mutation。WebSocket message send、edit、retract 與 reaction event 使用相同的 user 與 tenant budget,並額外套用 per-channel frame budget。Pin 與 unpin event 只使用 frame budget。
Channel creation、membership change、channel close、metadata update、notification mute 與一般 read 目前沒有共用的 general request budget。Search 與 attachment operation 使用各自的 limit。
REST rate-limit response
Message、search、upload 或 download request 超過限制時,回傳 429 Too Many Requests:
HTTP/1.1 429 Too Many Requests
Retry-After: 2
Content-Type: application/json
{
"error": "rate_limited"
} Retry-After 是由剩餘 window 向上取整的秒數。至少等待這段時間再 retry。
Host credential sync 也會回傳 429,但目前使用 errors array,且沒有 Retry-After。這是 response consistency gap,不是另一種 retry guarantee。
WebSocket rate-limit response
Channel 內的 event 會以毫秒回傳等待時間:
{
"error": "rate_limited",
"retry_after_ms": 847
} 這個 shape 適用於 message write,以及 cursor update、pin、thread read、typing indicator 等受 frame limit 的 event。
Channel join 超過限制時只回傳:
{
"reason": "rate_limited"
} Join response 沒有 retry value。再次 join 前應使用帶 jitter 的 backoff,不能形成 tight reconnect loop。
常見 HTTP status code
| Status | 意義 | Client action |
|---|---|---|
400 | Admin 或 operator request 格式錯誤 | 修正 request shape |
401 | Credential 缺少、過期、撤銷或無效 | Refresh 或更換 credential |
403 | Actor 缺少 membership 或 permission | Retry 前重新驗證 host authorization |
404 | Resource 不存在,或被 tenant 與 resource scope 隱藏 | 不能假設另一個 tenant 內存在該 resource |
409 | Resource lifecycle 與 operation 衝突 | 決定下一步前先 refresh current state |
410 | Attachment 已 tombstone | 移除過期的 attachment reference |
422 | Domain validation 失敗 | 修正 payload 或 product state |
429 | Rate budget 已耗盡 | 等待 retry window |
500 | Relay 無法完成 request | Operation 有 idempotency strategy 時才 retry |
大部分 conversation endpoint 使用 array 回傳 error:
{
"errors": ["forbidden"]
} Rate-limit、search 與 attachment controller 則使用 singular error field。Client 目前必須支援兩種 envelope。Unified error code 與 response schema 尚未實作。
依 operation 決定 retry
Message creation 遇到 timeout、disconnect、429 或可重試的 5xx 時,保留原 nonce。如果第一次 attempt 已 commit,Relay 會回傳既有 message,不會建立第二則。
Read operation 可以在等待指定 window 後 retry。Edit、reaction、membership change、channel lifecycle action 與 attachment operation 則應先判斷上一次 attempt 是否 commit,因為這些 operation 不一定提供 client idempotency key。
不要用完全相同的條件 retry 401、403、404、409 或 422。Credential、permission、resource、lifecycle 或 payload condition 必須先改變。
Operator override
Operator 可以透過 admin API 取代 tenant 的 stored override:
PUT /api/admin/tenants/{tenant_id}/rate_limits
X-Admin-Token: <admin-token>
Content-Type: application/json
{
"rate_limits": {
"user_message": {
"scale_ms": 1000,
"limit": 20
},
"tenant_message": {
"scale_ms": 1000,
"limit": 200
}
}
} 這個 request 會取代完整 override map。Omitted budget 會使用 deployment default。傳入 empty map 會清除所有 override。每個 value 都必須包含正整數 scale_ms 與 limit field。
這個 endpoint 需要 deployment admin token,不是 tenant self-service control plane。
Backpressure boundary
超過限制的 synchronous REST 與 WebSocket operation,會在 conversation action 執行前被拒絕。Relay 不會把這些 client request 排入 queue 延後執行。
Post-commit WebSocket fan-out、host webhook delivery、search indexing 與 adapter egress 使用 durable internal handoff。Outbound budget 耗盡的 host webhook 會延後,不會立刻 drop。這能保護 request latency,但不保證 maximum delivery delay。
Message order 仍以 channel-local seq 為準,不能使用 worker completion 或 webhook arrival order。
Product boundary
目前的 limiter 使用 node-local ETS counter。Multi-node deployment 中,每個 Relay node 各自計數,因此上表不是 cluster-wide aggregate ceiling。
Cluster-wide rate accounting、channel 與 membership operation 的完整 coverage、統一的 error envelope、標準化 limit 與 remaining header,以及 WebSocket join 的 retry value 仍屬於 product work。
這些 default 描述目前的 deployment behavior,不代表 managed-service quota 或 SLA。