sessionstore

package
v0.5.5 Latest Latest
Warning

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

Go to latest
Published: Sep 4, 2026 License: Apache-2.0 Imports: 11 Imported by: 0

README

sessionstore

A database/sql backed session.RawStore. The package owns the popcornweb_session table and never inspects application tables.

The engine is not compiled in here. One package per engine describes its dialect and registers it, and that import is what makes session.backend = "rdb" resolve — against that engine and no other:

import _ "github.com/shibukawa/popcornweb/sessionstore/postgres"

sqlite, postgres, and mysql are implemented. They share every statement whose only difference is placeholder style; an engine package supplies the DDL, the upsert, the bounded delete, and the catalog query, because those are the places the engines genuinely disagree. TestEngineContract runs one suite against all three, which is what keeps them interchangeable.

The import registers the backend and opens nothing. At startup the framework hands it the pool of the RDB middleware, and the backend verifies its own table before the application serves a request — a deployment that skipped the migration is told which migration to apply.

Constructing it directly stays available. The store is not generic; the payload type is added by the host:

store, err := sessionstore.NewStore(db, sessionstore.Options{Dialect: "postgres"})
err = store.VerifySchema(context.Background())
typed := session.Typed[Data](store, session.JSONCodec[Data]{})

The dialect is the name the DSN scheme already resolved to, so a deployment names its engine once.

The caller owns db, because a session store commonly shares the pool of the RDB middleware.

The table is migration-owned. MigrationSQL returns the goose migration a project carries, MigrationName is the name it carries it under, and VerifySchema checks the result at startup without changing the schema. The version prefix belongs to the project: the file takes the next free number when it is written, so installing this later renumbers nothing. EnsureSchema still creates the table for a test or a tool that has no migration directory.

Record timestamps are columns, not payload fields, so renewal updates one row without rewriting or re-encoding the payload. Get treats stored expiry as authoritative and reports session.ErrExpired regardless of what the browser sent. Touch refuses to revive a missing or expired record and refuses a renewal past the absolute expiry.

Backend failures are reported as session.ErrUnavailable without copying driver text, which can contain a DSN or query fragment, into the response path.

Schedule Prune to remove records that expire without ever being revoked; plugin/auth runs it periodically for the store it creates.

SQLite is the supported dialect. EnsureSchema emits SQLite DDL, and another dialect needs its own schema until a provider covers it.

Documentation

Overview

Package sessionstore keeps Popcorn Web login sessions in a relational database. It owns its own table and never inspects application tables.

The engine is not compiled in here. One of the sibling packages describes it and registers itself, so an application blank-imports the engine it runs:

import _ "github.com/shibukawa/popcornweb/sessionstore/postgres"

Index

Constants

View Source
const (
	// DefaultTable is the table owned by this package.
	DefaultTable = "popcornweb_session"
)
View Source
const MigrationName = "init_popcornweb_session"

MigrationName is the stable name of the migration a project carries for this table, without a version. The version belongs to the project: the file takes the next free one when it is written, so adding this capability to a project that already applied migrations renumbers nothing. The name is what makes the file recognizable at whatever version it ended up with.

Variables

View Source
var Columns = []string{
	"key_hash", "created_at_ms", "authenticated_at_ms", "last_seen_at_ms",
	"expires_at_ms", "idle_expires_at_ms", "method", "version", "payload",
}

Columns are the columns of the owned table, in the order every dialect declares them. VerifySchema compares what it finds against this list.

View Source
var ErrSchemaMissing = errors.New("sessionstore: session table is missing")

ErrSchemaMissing reports that the owned table does not exist yet. A project creates it from MigrationSQL rather than at startup.

Functions

func Dialects

func Dialects() []string

Dialects lists the registered dialect names in order, which is what an error reports when the configured engine is not among them.

func MigrationSQL

func MigrationSQL(dialect, table string) (string, error)

MigrationSQL returns the goose migration that creates table under one engine. It is the source of the file a project keeps in its migration directory, and later of the file api:cli-init scaffolds.

func NumberedPlaceholders

func NumberedPlaceholders(statement string) string

NumberedPlaceholders rewrites ? into $1, $2, and so on. An engine whose driver numbers its placeholders points Rebind here instead of restating every statement.

func Register

func Register(dialect Dialect)

Register adds an engine's dialect. An engine package calls it from init, so a blank import is what makes session.backend = "rdb" work against that engine:

import _ "github.com/shibukawa/popcornweb/sessionstore/postgres"

A duplicate or incomplete dialect panics: two descriptions of one engine is a build mistake, not a runtime condition.

func ScanColumns

func ScanColumns(rows sqlbind.Rows) ([]string, error)

ScanColumns reads one column name per row, which is the shape every engine's catalog query is written to return.

Types

type Dialect

type Dialect struct {
	// Name is the dialect identifier rule:rdb-dsn-resolution resolves a DSN
	// scheme to, which is what a configured DSN and this store agree on.
	Name string
	// CreateTable is the deterministic DDL of the owned table.
	CreateTable func(table string) string
	// Upsert replaces one row by primary key. Every engine spells the
	// conflict clause its own way.
	Upsert func(table string) string
	// Prune deletes at most a limited number of expired rows. The subquery
	// form that reads well elsewhere is not accepted by every engine.
	Prune func(table string) string
	// Columns lists the columns of table in declaration order, or none at all
	// when the table does not exist. The executor is a *sql.DB or a native
	// one, so an engine queries it through sqlbind.Query.
	Columns func(ctx context.Context, db sqlbind.SQLExecutor, table string) ([]string, error)
	// Rebind adapts ? placeholders to the engine's own numbering. A nil
	// Rebind leaves a statement as written.
	Rebind func(statement string) string
}

Dialect is everything one database engine has to say differently about the table this package owns. The statements that only differ in placeholder style are written once in the store and rewritten by Rebind, so an engine package describes the schema and the two statements no dialect shares.

type Options

type Options struct {
	// Dialect is the registered engine name, which is the dialect the DSN
	// scheme already resolved to.
	Dialect         string
	Table           string
	Now             func() time.Time
	MaxPayloadBytes int
	MaxPruneBatch   int
}

Options bounds record size, selects the owned table, and names the engine whose dialect the statements take.

type Store

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

Store is a relational session.RawStore. The caller owns db and must call EnsureSchema, or carry the migration, before serving requests.

func NewStore

func NewStore(db sqlbind.SQLExecutor, options Options) (*Store, error)

NewStore constructs a Store over db, which is a *sql.DB or the native executor of an engine that bypasses database/sql. db stays owned by the caller, because a session store commonly shares the pool of the RDB middleware. Wrap the result with session.Typed to give a Manager the payload type it stores.

func (*Store) Delete

func (s *Store) Delete(ctx context.Context, keyHash string) error

func (*Store) Dialect

func (s *Store) Dialect() string

Dialect reports the engine this store speaks.

func (*Store) EnsureSchema

func (s *Store) EnsureSchema(ctx context.Context) error

EnsureSchema creates the owned table when it is missing. A project that carries MigrationSQL does not need it; VerifySchema is the startup check.

func (*Store) Get

func (s *Store) Get(ctx context.Context, keyHash string) (session.RawRecord, error)

func (*Store) Prune

func (s *Store) Prune(ctx context.Context, before time.Time, limit int) (int64, error)

Prune removes at most limit records that expired before the given time.

func (*Store) Put

func (s *Store) Put(ctx context.Context, keyHash string, record session.RawRecord) error

func (*Store) SchemaSQL

func (s *Store) SchemaSQL() string

SchemaSQL returns the deterministic DDL of the owned table under this store's engine.

func (*Store) Touch

func (s *Store) Touch(ctx context.Context, keyHash string, lastSeenAt, idleExpiresAt time.Time) error

func (*Store) VerifySchema

func (s *Store) VerifySchema(ctx context.Context) error

VerifySchema reports whether the owned table exists with the expected columns. It never changes the schema.

Directories

Path Synopsis
Package dynamo stores login sessions in DynamoDB.
Package dynamo stores login sessions in DynamoDB.
Package firestore stores login sessions in Firestore in Datastore mode.
Package firestore stores login sessions in Firestore in Datastore mode.
Package mysql registers the MySQL dialect of the session store.
Package mysql registers the MySQL dialect of the session store.
Package postgres registers the PostgreSQL dialect of the session store.
Package postgres registers the PostgreSQL dialect of the session store.
Package redis stores Popcorn Web login sessions in Redis or Valkey.
Package redis stores Popcorn Web login sessions in Redis or Valkey.
Package sqlite registers the SQLite dialect of the session store.
Package sqlite registers the SQLite dialect of the session store.

Jump to

Keyboard shortcuts

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