coder / coder/websocket

Add lightweight write deadlines to Conn

Offen
#572 0 Kommentare 0 Reaktionen 0 zugewiesene Personen Auf GitHub ansehen
Vorherrschende Sprache
Go
Sterne
5.5k
Forks
372
PR-Merge-Kennzahlen
Keine gemergten PRs in 30 T.

Beschreibung

### Problem

Long-lived WebSocket servers often need every outbound write to be bounded. With `v1.8.15`, the main API requires creating a deadline context for every message:

```go
package main

import (
"context"
"log"
"net/http"
"time"

"github.com/coder/websocket"
)

func main() {
http.HandleFunc("/", serve)
log.Fatal(http.ListenAndServe(":8080", nil))
}

func serve(w http.ResponseWriter, r *http.Request) {
c, err := websocket.Accept(w, r, nil)
if err != nil {
return
}
defer c.CloseNow()

connCtx := c.CloseRead(r.Context())
ticker := time.NewTicker(10 * time.Millisecond)
defer ticker.Stop()

for {
select {
case <-connCtx.Done():
return
case <-ticker.C:
ctx, cancel := context.WithTimeout(context.Background(), time.Second)
err := c.Write(ctx, websocket.MessageText, []byte(`{"type":"event"}`))
cancel()
if err != nil {
return
}
}
}
}
```

Using `context.Background()` avoids the timeout-related allocations added around each frame, but also removes the bounded-write guarantee.

`websocket.NetConn` exposes `SetWriteDeadline`, but it is a full bidirectional adapter. It creates read and write contexts, timers, and locks, and calls `SetReadLimit(-1)` even when only its write side is needed.

A loopback benchmark with `v1.8.15` on Go 1.27rc2 produced:

| Strategy | Write path | Connection construction |
|---|---:|---:|
| `context.WithTimeout` | 1304 B/op, 12 allocs/op | no extra adapter |
| `NetConn.SetWriteDeadline` | 664 B/op, 5 allocs/op | 848 B, 13 allocs |

The write measurements use the same client reader, so the allocation difference is the relevant result. At high connection counts, the fixed `NetConn` cost is material.

### Request

Could `*websocket.Conn` provide a lightweight write-only deadline mechanism, for example `SetWriteDeadline(time.Time)` or an equivalent API?

Ideally it would:

- reuse connection-owned deadline state instead of allocating a timer context per write;
- bound both write-lock acquisition and the underlying write;
- preserve the current behavior where a timed-out write closes the WebSocket;
- remain safe with concurrent `Ping`, `Close`, and data writes;
- avoid creating read-side state or changing the configured read limit;
- allow zero time to clear the deadline.

Related: #252 discusses the same API direction for read deadlines.

Beitragsleitfaden

Für dieses Repository ist kein Beitragsleitfaden indexiert

Rechercherichtung

Untersuche zuerst den Schreibpfad auf *websocket.Conn und die Behandlung von Deadlines in websocket.NetConn, und lies anschließend Issue #252 für die Richtung bezüglich der Read-Deadline. Als erledigt gilt eine nur für Schreibvorgänge geltende Deadline-Mechanik, die den Erwerb des Locks und Schreibvorgänge begrenzt, das Verhalten beim Schließen nach einem Timeout beibehält, bei gleichzeitigen Operationen sicher bleibt, Zustände auf der Leseseite vermeidet und bei einer Zeit von null gelöscht wird.

Vom Indexierungsmodell aus dem Issue-Text verfasst.

Bewertung

Tech-Stack
go
Bereich
backend-api-design, networking
Issue-Typ
Feature
Schwierigkeit
5/5
Geschätzter Aufwand
Über eine Woche
Aktivitätsstatus
Ruhig
Klarheit
Größtenteils klar
Anfängerfreundlichkeit
45/100

Neue Issues direkt in Ihr Postfach

Eine kurze Übersicht über anfängerfreundliche GitHub-Issues.