flyaffinity

package module
v0.1.3 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Aug 27, 2026 License: MIT Imports: 10 Imported by: 0

README

flyaffinity

Session-affinity routing for Fly.io apps that embed a parley relay: pin every connection for a session to one machine so each machine's in-memory relay stays authoritative for its shard, and you can run more than one.

parley keeps session state as live socket ownership (not serializable), so you scale by sharding, not shared storage. This package supplies the platform-specific half that parley deliberately leaves out via relay.Options.Router:

  • Ownership by rendezvous (HRW) hashing over the live machine set — moves only ~1/N sessions when the fleet changes (vs ~all for modulo), with no coordination (a fixed seedless hash, so every machine agrees).
  • Discovery from Fly internal DNS (vms.<app>.internal TXT), cached briefly.
  • Route plugs into relay.Options.Router: serve here when off-Fly, single-machine, already-replayed (one-hop Fly-Replay-Src loop guard), or this machine is the owner; otherwise reply Fly-Replay: instance=<owner>.
aff := flyaffinity.New(os.Getenv("FLY_APP_NAME"), os.Getenv("FLY_MACHINE_ID"), 7*time.Second)
relaySrv := relay.New(relay.Options{Router: aff.Route})
// aff.Peers(ctx) feeds a dashboard.NewAggregator for cluster-wide stats.

Off Fly (FLY_MACHINE_ID empty) or at one machine, Route always serves here — identical to a single-node relay. Requires clients that send parley's ?s=<session-hex> routing hint (parley's session package does this since v0.5.0). Server-only (imports parley/relay); not part of a WASM build.

Extracted from kibitz / confab, which both run on it.

MIT licensed.

Documentation

Overview

Package flyaffinity routes relay connections so every connection for a given session lands on the same Fly machine (session-affinity sharding). Each machine's parley relay stays in-memory authoritative for its shard.

Ownership is by rendezvous (highest-random-weight) hashing over the live machine set, discovered from Fly internal DNS. HRW moves only ~1/N sessions when the machine set changes (vs ~all for modulo), and needs no coordination: every machine computes the identical owner from the same peer set with a fixed, seedless hash. The Route method plugs into parley's relay.Options.Router.

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

This section is empty.

Types

type Resolver

type Resolver struct {
	// contains filtered or unexported fields
}

Resolver answers ownership questions for one machine. Construct with New.

func New

func New(app, self string, ttl time.Duration) *Resolver

New builds a Resolver. app is FLY_APP_NAME, self is FLY_MACHINE_ID (empty when not running on Fly), ttl bounds how long a peer set is cached.

func (*Resolver) Peers

func (a *Resolver) Peers(ctx context.Context) ([]string, error)

Peers returns the base URLs of the OTHER machines (this one excluded) for the dashboard stats aggregator. Fly 6PN per-machine form: http://<id>.vm.<app>.internal:8080 (a hostname — never bracketed).

func (*Resolver) Route

func (a *Resolver) Route(sid wire.SessionID, r *http.Request) relay.RouteResult

Route is the parley relay.Options.Router hook. It decides, before the WebSocket upgrade, whether this machine owns the session (serve here) or must hand it to the owning machine via Fly-Replay.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL