Skip to content

Concepts

Reliable connections

Warning

This feature is provided by the contrib/rews package, which is outside of the backward compatibility guarantees of the core SDK. Its API may change without following semantic versioning.

The rews (reliable WebSocket) package wraps a standard WebSocket connection and adds automatic reconnection when the connection is lost. On reconnect, it restores the previous session state including namespace, database, authentication, variables, and live queries.

This is useful for long-running applications that need to survive transient network failures without manual reconnection logic.

Function

Description

rews.New(newConn, interval, unmarshaler, logger)

Creates a new auto-reconnecting WebSocket connection

rews.NewExponentialBackoffRetryer()

Creates a retryer with exponential backoff and jitter

rews.NewFixedDelayRetryer(delay, maxRetries)

Creates a retryer with a fixed delay between attempts

Create a rews.Connection by providing a factory function that constructs the underlying WebSocket connection, a check interval for detecting disconnections, a CBOR unmarshaler, and an optional logger.

import (
	"context"
	"net/url"
	"time"

	surrealdb "github.com/surrealdb/surrealdb.go"
	"github.com/surrealdb/surrealdb.go/contrib/rews"
	"github.com/surrealdb/surrealdb.go/pkg/connection"
	"github.com/surrealdb/surrealdb.go/pkg/connection/gorillaws"
	"github.com/surrealdb/surrealdb.go/surrealcbor"
)

endpoint, _ := url.Parse("ws://localhost:8000/rpc")
codec := surrealcbor.New()

conn := rews.New(
	func(ctx context.Context) (*gorillaws.WebSocket, error) {
		conf := connection.NewConfig(endpoint)
		conf.Marshaler = codec
		conf.Unmarshaler = codec
		return gorillaws.New(conf), nil
	},
	5*time.Second,
	codec,
	nil,
)

Then pass the connection to FromConnection to create a *DB:

db, err := surrealdb.FromConnection(ctx, conn)
if err != nil {
	log.Fatal(err)
}
defer db.Close(ctx)

By default, connection attempts are not retried. Set the Retryer field to enable automatic retries on connection failure.

The ExponentialBackoffRetryer increases the delay between retries exponentially, with optional jitter to avoid thundering herd problems:

retryer := rews.NewExponentialBackoffRetryer()
retryer.MaxRetries = 10
retryer.InitialDelay = 1 * time.Second
retryer.MaxDelay = 30 * time.Second
conn.Retryer = retryer
FieldDefaultDescription
InitialDelay1sDelay before the first retry
MaxDelay30sMaximum delay between retries
Multiplier2.0Exponential backoff multiplier
MaxRetries0 (infinite)Maximum retry attempts, 0 for unlimited
JittertrueAdd randomness to avoid synchronised retries
JitterFactor0.3Maximum jitter as a fraction of the delay

For simpler cases, FixedDelayRetryer uses a constant delay:

conn.Retryer = rews.NewFixedDelayRetryer(2*time.Second, 5)

You can also implement the Retryer interface for custom strategies:

type Retryer interface {
	NextDelay(attempt int, lastErr error) (time.Duration, bool)
	Reset()
}

When the connection is lost and re-established, rews automatically restores:

  1. Namespace and database - the last values passed to .Use()

  2. Authentication - the last token from .SignIn(), .SignUp(), or .Authenticate()

  3. Connection variables - all variables set with .Let() (and removed with .Unset())

  4. Live queries - all active live queries are re-subscribed, and notification routing is restored

The figure below shows the connection states and the order in which the session is restored.

The rews connection of the Go SDK moves from Disconnected to Connecting to Connected, and to Closing and Closed after Close(). At each CheckInterval it checks the socket, and if the socket has closed it reconnects and restores the session; a failed attempt is tried again at the next check. The session is restored in this order: the namespace and database from the last Use(), the authentication with the last token or, if the server refuses it, with the refresh token from SignInWithRefresh, the variables set with Let(), and the live queries from Live() or from a Query() call that starts with LIVE SELECT, whose notifications keep their original IDs. With an expired token and no refresh token the reconnect fails, and a LIVE SELECT that is not at the start of its query is not restored.The rews connection of the Go SDK moves from Disconnected to Connecting to Connected, and to Closing and Closed after Close(). At each CheckInterval it checks the socket, and if the socket has closed it reconnects and restores the session; a failed attempt is tried again at the next check. The session is restored in this order: the namespace and database from the last Use(), the authentication with the last token or, if the server refuses it, with the refresh token from SignInWithRefresh, the variables set with Let(), and the live queries from Live() or from a Query() call that starts with LIVE SELECT, whose notifications keep their original IDs. With an expired token and no refresh token the reconnect fails, and a LIVE SELECT that is not at the start of its query is not restored.

Note

If the authentication token has expired by the time reconnection occurs, the server refuses it. When the session was started with .SignInWithRefresh() or .SignUpWithRefresh(), rews then signs in with the refresh token and keeps the new pair; set OnTokensRefreshed to receive it, because the server revokes a refresh token once it is used. Without a refresh token, re-authentication fails and the reconnect is tried again at the next check.

The rews.Connection tracks its state through a state machine:

StateDescription
DisconnectedNot connected, initial state
ConnectingConnection attempt in progress
ConnectedConnection established and active
ClosingClose requested, shutting down
ClosedFully closed, cannot be reused

Was this page helpful?