Skip to content

[Feature] UDS Connection Warm Pool (Pre-established Connection Sliding Window) #58

@ElioNeto

Description

@ElioNeto

Context

Inspired by the Cranker Connector's sliding window of pre-established connections, vyx should maintain a pool of ready-to-use UDS connections per worker to eliminate connection setup latency on the hot path.

Cranker's key insight: each connector keeps N connections open and waiting before a request arrives. When one connection is consumed by a request, a new one is opened immediately to maintain the pool size. This ensures the first byte of a request never waits for connection establishment.

The Problem

Currently, the uds.Transport in vyx creates a new Unix Domain Socket connection for each request on-demand:

Request arrives
    │
    ▼
 transport.Send()
    │
    ▼
net.Dial("/tmp/vyx/node:api.sock")  ← connection setup latency here
    │
    ▼
write IPC frame
    │
    ▼
read response

Under load (many concurrent requests), this creates:

  • Repeated connection setup overhead
  • File descriptor exhaustion risk
  • No reuse of idle connections

Proposed Solution

Implement a connection pool with a sliding window per worker ID inside uds.Transport.

Pool Behaviour

                    Pool for "node:api" (window size = 4)

  [■ ready][■ ready][■ ready][■ ready]   ← all pre-connected
                     │
             Request arrives, acquires slot
                     │
  [■ ready][■ ready][□ in-use][■ ready]
                     │
          Immediately open a new connection
                     │
  [■ ready][■ ready][□ in-use][■ ready][■ new]
                     │
           Request completes, connection returned or closed
                     │
  [■ ready][■ ready][■ ready][■ ready]   ← window maintained

Configuration

ipc:
  socket_dir: /tmp/vyx
  pool:
    min_idle: 2        # minimum pre-established connections per worker
    max_size: 16       # maximum total connections per worker
    idle_timeout: 30s  # close connections idle longer than this

Interface changes

// ConnectionPool manages a sliding window of UDS connections for a single worker.
type ConnectionPool struct {
    workerID  string
    socketPath string
    minIdle   int
    maxSize   int
    idle      chan net.Conn
    mu        sync.Mutex
}

func (p *ConnectionPool) Acquire(ctx context.Context) (net.Conn, error)
func (p *ConnectionPool) Release(conn net.Conn)
func (p *ConnectionPool) replenish()  // called after each Acquire to maintain minIdle
func (p *ConnectionPool) Close() error

Expected Impact

Scenario Before After
First request to worker ~0.5ms (dial) + IPC ~0.05ms (pool) + IPC
100 concurrent requests 100 dials max_size dials, rest queue
Worker restart all connections invalid pool detects + reconnects

Acceptance Criteria

  • ConnectionPool implemented in core/infrastructure/ipc/uds
  • uds.Transport uses ConnectionPool per registered worker
  • Pool maintains min_idle pre-established connections at all times
  • Connections exceeding idle_timeout are closed and replaced
  • On worker restart, stale connections are detected (broken pipe) and the pool reconnects
  • max_size cap is respected; excess requests wait with context cancellation support
  • ipc.pool block added to vyx.yaml schema
  • Benchmarks (go test -bench) comparing pooled vs. non-pooled throughput
  • Unit tests for pool replenishment, idle timeout, and error recovery

References

Metadata

Metadata

Assignees

No one assigned

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions