coder / coder/websocket

Add lightweight write deadlines to Conn

Abierto
#572 0 comentarios 0 reacciones 0 asignados Ver en GitHub
Lenguaje dominante
Go
Estrellas
5.5k
Forks
372
Métricas de merge de PR
Sin PR fusionados en 30 d

Descripción

### 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.

Guía de contribución

No hay ninguna guía de contribución indexada para este repositorio

Línea de trabajo

Inspecciona primero la ruta de escritura en *websocket.Conn y la gestión de deadlines en websocket.NetConn; después, lee el issue #252 para conocer la dirección relacionada con el read-deadline. Se considera terminado cuando exista un mecanismo de deadline exclusivo para escritura que limite la adquisición del lock y las escrituras, conserve el comportamiento de cierre por timeout, siga siendo seguro con operaciones concurrentes, evite el estado del lado de lectura y se borre con un tiempo cero.

Escrito por el modelo de indexación a partir del texto del issue.

Evaluación

Stack tecnológico
go
Área
backend-api-design, networking
Tipo de issue
Nueva funcionalidad
Dificultad
5/5
Tiempo estimado
Más de una semana
Estado de actividad
Tranquilo
Claridad
Bastante claro
Aptitud para principiantes
45/100

Recibe los nuevos issues en tu correo

Un resumen breve de issues de GitHub para principiantes.