Help us shape the future of Go! Take the 2026 Go Developer Survey

redisbackend

package
v1.2.0 Latest Latest
Warning

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

Go to latest
Published: Oct 30, 2026 License: Apache-2.0 Imports: 9 Imported by: 0

Documentation

Overview

Package redisbackend implements session.Backend on Redis, so that sessions work across replicas.

It accepts any redis.UniversalClient (a Client, a ClusterClient or a FailoverClient); the caller owns the client and closes it. Every key of one session carries the session ID as Redis Cluster hash tag, so the Lua scripts that keep Put, Pop and Revoke atomic run on a single slot.

The package never logs and never starts goroutines. Errors of go-redis are reported by message only; callers cannot match on them. The Backend is safe for concurrent use.

Index

Examples

Constants

This section is empty.

Variables

This section is empty.

Functions

This section is empty.

Types

type Backend

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

Backend implements session.Backend on Redis. One string per value plus a set indexing the keys of the session; the layout is <prefix>:{<sessionID>} (index), <prefix>:{<sessionID>}:<key> (value), <prefix>:{<sessionID>}#revoked (revocation marker) and <prefix>:{<sessionID>}#popped:<token> and #swapped:<token> (the result of one Pop or CompareAndSwap, kept one minute for its retry). A value the index does not list (its index evicted) counts as absent. It is safe for concurrent use.

func New

func New(options *Options) (*Backend, error)

New returns a Backend on options.Client. The options are copied; the client stays with the caller.

Example

ExampleNew shares sessions between replicas: every replica builds its Store on a backend pointing at the same Redis. The caller owns the client and closes it. miniredis stands in for the server here.

package main

import (
	"context"
	"fmt"
	"time"

	"github.com/alicebob/miniredis/v2"
	"github.com/redis/go-redis/v9"

	"github.com/govengers/auth/session"
	"github.com/govengers/auth/session/redisbackend"
)

func main() {
	server, err := miniredis.Run()
	if err != nil {
		fmt.Println(err)
		return
	}
	defer server.Close()
	client := redis.NewClient(&redis.Options{Addr: server.Addr()})
	defer func() { _ = client.Close() }() // nothing to flush

	backend, err := redisbackend.New(&redisbackend.Options{Client: client, KeyPrefix: "myapp:session"})
	if err != nil {
		fmt.Println(err)
		return
	}
	key, err := session.ParseKey("MDEyMzQ1Njc4OWFiY2RlZjAxMjM0NTY3ODlhYmNkZWY=")
	if err != nil {
		fmt.Println(err)
		return
	}
	store, err := session.NewStore(&session.StoreOptions{Backend: backend, EncryptionKey: key})
	if err != nil {
		fmt.Println(err)
		return
	}
	fmt.Println("session lifetime:", store.MaximumAge())

	// The backend itself stores opaque values per (session ID, key) and refuses Puts after Revoke.
	ctx := context.Background()
	if err := backend.Put(ctx, "session-id", "key", []byte("value"), time.Hour); err != nil {
		fmt.Println(err)
		return
	}
	value, found, err := backend.Get(ctx, "session-id", "key")
	fmt.Println(string(value), found, err)
	if err := backend.Revoke(ctx, "session-id", time.Hour); err != nil {
		fmt.Println(err)
		return
	}
	fmt.Println(backend.Put(ctx, "session-id", "key", []byte("value"), time.Hour))

}
Output:
session lifetime: 24h0m0s
value true <nil>
session revoked

func (*Backend) CompareAndSwap

func (b *Backend) CompareAndSwap(ctx context.Context, sessionID, key string, previous, value []byte) (bool, error)

CompareAndSwap replaces the live value under (sessionID, key) with value, keeping its expiry, when it still equals previous; a client retry of the step after a lost reply reports the same result.

func (*Backend) Get

func (b *Backend) Get(ctx context.Context, sessionID, key string) ([]byte, bool, error)

Get returns the live value under (sessionID, key).

func (*Backend) Pop

func (b *Backend) Pop(ctx context.Context, sessionID, key string) ([]byte, bool, error)

Pop returns the live value under (sessionID, key) and deletes it in one atomic step; a client retry of the step after a lost reply returns the same value.

func (*Backend) Put

func (b *Backend) Put(ctx context.Context, sessionID, key string, value []byte, ttl time.Duration) error

Put stores value under (sessionID, key) for ttl; session.ErrRevoked when sessionID carries a marker. Every method refuses an empty sessionID and one containing '{' or '}' with an error.

func (*Backend) Revoke

func (b *Backend) Revoke(ctx context.Context, sessionID string, ttl time.Duration) error

Revoke deletes every key of sessionID. When there was any, it writes a marker that refuses Puts for ttl, or until the latest of the deleted values would have expired when that is sooner.

type Options

type Options struct {
	Client    redis.UniversalClient // required; the caller owns and closes it
	KeyPrefix string                // default "session"; must not contain '{' or '}' (they delimit the hash tag)
}

Options configures New.

Jump to

Keyboard shortcuts

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