Documentation
¶
Overview ¶
Package readview provides materialized view projections built from event streams for use with the eventsourced framework.
A readview subscribes to events (either via backfill from the event store or live via AMQP) and maintains a denormalized read model in PostgreSQL. This implements the CQRS read side of an event-sourced system.
Usage ¶
rv, err := readview.New(db, store,
readview.Register("user_list", userEventHandler, userResetHandler),
readview.WithBackFillBatchSize(100),
)
if err != nil {
log.Fatal(err)
}
// Start backfill (blocks until complete)
if err := rv.Start(ctx); err != nil {
log.Fatal(err)
}
// Wire up AMQP handlers for live events
for name, handler := range rv.Handlers() {
conn.Handle(name, handler)
}
Backfill ¶
On startup, each readview checks its current position against the event store's maximum global sequence number. If behind, it initiates a backfill that replays historical events through the event handler. Progress is committed in batches (configurable via WithBackFillBatchSize) and tracked via heartbeat timestamps.
Distributed Coordination ¶
Multiple application instances coordinate via PostgreSQL row-level locks on the readview_status table. Only one instance can backfill a given readview at a time. Other instances wait and retry until the lock is released or the heartbeat expires.
Configuration ¶
- Register: Register a named readview with its event and reset handlers
- WithLogger: Set a custom logger (default: slog.Default())
- WithBackFillBatchSize: Set batch commit size during backfill (default: 10)
- WithHeartbeat: Set heartbeat duration for stale backfill detection (default: 5s)
Index ¶
- Variables
- type EventHandler
- type Logger
- type MessageHandler
- type Opt
- type Readview
- func (r *Readview) ExternalMappingHandler(typeMapper goamqp.TypeMapper, messageHandler MessageHandler) messaging.EventHandler[any]
- func (r *Readview) Handlers() map[string]MessageHandler
- func (r *Readview) RegularMappingHandler(name string, typeMapper goamqp.TypeMapper, messageHandler MessageHandler) messaging.EventHandler[any]
- func (r *Readview) Reset(ctx context.Context, name string) error
- func (r *Readview) Start(ctx context.Context) error
- type ResetHandler
- type ViewInfo
Constants ¶
This section is empty.
Variables ¶
var ErrNotReadyToProcessEvents = fmt.Errorf("not ready to process events")
var ErrStatusRowLocked = errors.New("status row locked")
Functions ¶
This section is empty.
Types ¶
type EventHandler ¶
EventHandler is a function that processes an event within a database transaction to update a read model. It receives the transaction to ensure atomicity with the readview status update.
type Logger ¶
type Logger interface {
Info(msg string, args ...any)
Warn(msg string, args ...any)
Error(msg string, args ...any)
}
Logger defines the logging interface used by readview for operational messages.
type MessageHandler ¶
MessageHandler is a function that handles incoming messages from AMQP. It wraps the event processing with readview coordination logic.
type Opt ¶
Opt is a functional option for configuring a Readview.
func Register ¶
func Register(name string, handler EventHandler, resetHandler ResetHandler) Opt
Register registers a named readview with its event handler and reset handler. The name must be unique across all registered readviews. The event handler processes each event, and the reset handler clears data before a full rebuild.
func WithBackFillBatchSize ¶
WithBackFillBatchSize sets the number of events to process before committing during backfill. Smaller batches commit more frequently (safer for restarts), while larger batches are faster. Default: 10.
func WithHeartbeat ¶
WithHeartbeat sets the duration used to determine if a backfill is still active. If the last heartbeat update is older than this duration, the backfill is considered stale and another instance can take over. Default: 5s.
func WithLogger ¶
WithLogger sets the logger used by readview for operational messages. Defaults to slog.Default().
type Readview ¶
type Readview struct {
// contains filtered or unexported fields
}
Readview manages materialized view projections built from event streams. It coordinates backfilling historical events, processing live events, and distributed locking to ensure only one instance processes events at a time.
func New ¶
func New(db *sqlx.DB, store eventsourced.EventStore, opts ...Opt) (*Readview, error)
New creates a Readview instance with the given database connection and event store. It runs schema migrations automatically and initializes all registered views. Default values: backfill batch size = 10, heartbeat = 5s, logger = slog.Default().
func (*Readview) ExternalMappingHandler ¶
func (r *Readview) ExternalMappingHandler(typeMapper goamqp.TypeMapper, messageHandler MessageHandler) messaging.EventHandler[any]
ExternalMappingHandler creates an event handler for external events. Unlike RegularMappingHandler, it does not filter by event source, passing all messages to the readview's message handler.
func (*Readview) Handlers ¶
func (r *Readview) Handlers() map[string]MessageHandler
Handlers returns a map of readview names to their MessageHandler functions. Use these handlers to wire up AMQP message consumption.
func (*Readview) RegularMappingHandler ¶
func (r *Readview) RegularMappingHandler(name string, typeMapper goamqp.TypeMapper, messageHandler MessageHandler) messaging.EventHandler[any]
RegularMappingHandler creates an event handler for regular (non-external) events. It filters out non-Event messages and ExternalSource events, passing only regular events to the readview's message handler. The TypeMapper resolves each message's routing key to its concrete Go type before the handler runs.
func (*Readview) Reset ¶
Reset clears a readview's data using its ResetHandler and re-initiates a full backfill from sequence 0. Returns ErrStatusRowLocked if another instance is currently backfilling.
func (*Readview) Start ¶
Start initiates the backfill process for all registered readviews. It checks the current sequence number against the maximum global sequence and backfills any missing events. Multiple instances coordinate via database row locks; instances that cannot acquire the lock will wait and retry.
type ResetHandler ¶
ResetHandler is called to clear and rebuild a readview's data. It receives the readview name and a transaction for atomic cleanup operations.