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 ¶
- type Backend
- func (b *Backend) CompareAndSwap(ctx context.Context, sessionID, key string, previous, value []byte) (bool, error)
- func (b *Backend) Get(ctx context.Context, sessionID, key string) ([]byte, bool, error)
- func (b *Backend) Pop(ctx context.Context, sessionID, key string) ([]byte, bool, error)
- func (b *Backend) Put(ctx context.Context, sessionID, key string, value []byte, ttl time.Duration) error
- func (b *Backend) Revoke(ctx context.Context, sessionID string, ttl time.Duration) error
- type Options
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 ¶
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) Pop ¶
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.
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.