pganalytics

package
v0.0.6 Latest Latest
Warning

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

Go to latest
Published: Jul 16, 2026 License: Apache-2.0 Imports: 8 Imported by: 0

Documentation

Overview

Package pganalytics is the reference analytics.Sink: a shared Postgres analytics database with a tenant_id column on every table. It is the ONE place fabriq deliberately co-locates data from many tenants; the database and its credential are separate from tenant DBs and the catalog control DB.

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

func TruncateForTest

func TruncateForTest(ctx context.Context, s *Sink) error

TruncateForTest clears all analytics tables. Test-only.

Types

type Option

type Option func(*Sink)

Option configures the sink at Open.

func WithEventPartitioning

func WithEventPartitioning() Option

WithEventPartitioning creates the event log as a monthly range-partitioned table (PARTITION BY RANGE (at)) so retention becomes an instant DROP PARTITION instead of a delete-scan. Takes effect only on a FRESH database — CREATE TABLE IF NOT EXISTS will not convert an existing non-partitioned events table; migrating an existing deployment is a manual step.

type Sink

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

Sink writes the analytics read model. It ensures its own schema at Open (three CREATE TABLE IF NOT EXISTS, mirroring the catalog control DB) rather than joining fabriq's tenant migration chain — this is a self-contained operator store.

func Open

func Open(ctx context.Context, dsn string, opts ...Option) (*Sink, error)

Open dials the analytics database and ensures the schema. ensureSchema proves reachability (it requires a live connection).

func (*Sink) AllWatermarks

func (s *Sink) AllWatermarks(ctx context.Context, tenantID string) ([]analytics.Watermark, error)

AllWatermarks returns every applied watermark for a tenant in one read.

func (*Sink) AppendEvents

func (s *Sink) AppendEvents(ctx context.Context, events []analytics.Event) error

AppendEvents dedupes on the natural key. The conflict target must match the events primary key, which includes `at` in partitioned mode (`at` is fixed per version, so dedup semantics are unchanged).

func (*Sink) Close

func (s *Sink) Close() error

Close releases the analytics database connection.

func (*Sink) LagByTenant

func (s *Sink) LagByTenant(ctx context.Context) (map[string]float64, error)

LagByTenant reports now() - (that tenant's newest fact commit time), in seconds, per tenant. An empty map means no facts. Same failed-iteration-is-an-error discipline as Watermark.

func (*Sink) MaintainPartitions

func (s *Sink) MaintainPartitions(ctx context.Context, retention time.Duration) (int, int, error)

MaintainPartitions (partitioned mode only) ensures the current and next two months have partitions and, when retention > 0, drops every monthly partition whose entire range is older than now-retention — the instant-reclaim path that replaces a delete-scan. Returns (created, dropped). A no-op when the sink is not partitioned.

func (*Sink) PruneEvents

func (s *Sink) PruneEvents(ctx context.Context, olderThan time.Time) (int64, error)

PruneEvents deletes history events with at < olderThan across all tenants (the (at) index carries the scan) and returns the count removed. Facts are untouched. Idempotent.

func (*Sink) PurgeTenant

func (s *Sink) PurgeTenant(ctx context.Context, tenantID string) (int64, error)

PurgeTenant hard-deletes all of one tenant's rows across the three analytics tables and returns the total removed. Runs in one transaction so a tenant is never left half-erased. Idempotent.

func (*Sink) QueryReadOnly

func (s *Sink) QueryReadOnly(ctx context.Context, query string, args ...any) (rows []map[string]any, cols []string, truncated bool, err error)

QueryReadOnly runs a query inside a READ ONLY transaction against the Postgres analytics store and returns a dynamic result set. The read-only tx is the REAL enforcement — a data-modifying statement (including a data-modifying CTE) errors at the database, regardless of the caller's precheck. The row cap bounds the result; the caller bounds time via ctx.

func (*Sink) ReprojectTenant

func (s *Sink) ReprojectTenant(ctx context.Context, tenantID, aggregate string, transform func(json.RawMessage) (json.RawMessage, error)) (int64, error)

ReprojectTenant re-writes stored fact and event payloads for a tenant (and optional aggregate) through transform, in place. Change detection is semantic: the UPDATE only fires when the re-projected JSONB actually differs (payload IS DISTINCT FROM), so RowsAffected counts real rewrites and a re-run is a no-op — the fake compares its canonical bytes and reaches the same count.

func (*Sink) SetWatermark

func (s *Sink) SetWatermark(ctx context.Context, ws []analytics.Watermark) error

SetWatermark advances monotonically.

func (*Sink) UpsertFacts

func (s *Sink) UpsertFacts(ctx context.Context, facts []analytics.Fact) error

UpsertFacts version-gates: a row is updated only when the incoming version is strictly greater than the stored one.

func (*Sink) Watermark

func (s *Sink) Watermark(ctx context.Context, tenantID, aggregate, aggID string) (int64, error)

Watermark reads the highest applied version (0 if unknown). Follows the catalog.go Get pattern: a failed row iteration must surface as an error, not silently read as "no rows" (see adapters/postgres/catalog.go Get).

Jump to

Keyboard shortcuts

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