Documentation
¶
Index ¶
- func AlignBucketStart(t time.Time, bucketSeconds int) time.Time
- func BucketExpr(db *gorm.DB, column string, bucketSeconds int) string
- func BuildIndexes(db *gorm.DB, indexes ...Index)
- func DensifyBuckets(rows []BucketedRow, startUnix int64, bucketSeconds int, nBuckets int) []int64
- func EnsureIndexes(db *gorm.DB, indexes ...Index) error
- type BucketCount
- type BucketedRow
- type Index
Constants ¶
This section is empty.
Variables ¶
This section is empty.
Functions ¶
func AlignBucketStart ¶
AlignBucketStart rounds `t` down to the nearest `bucketSeconds` boundary. Used so the API and the rollup-writer agree on bucket edges to the second.
func BucketExpr ¶
BucketExpr returns the SQL expression that floors `created_at` to a bucket-aligned unix timestamp. Same shape on every dialect — only the epoch-extraction function differs.
The expression returns an integer number of seconds since the epoch, truncated down to the nearest `bucketSeconds` boundary. Group by this expression, count(*), and you have a contiguous-bucket histogram.
func BuildIndexes ¶ added in v0.6.0
BuildIndexes runs EnsureIndexes without holding up the caller: in the background on Postgres and MySQL, where a build can take minutes on a large table but does not block writers, and inline on SQLite, whose databases are small and which allows one writer at a time regardless. A failure is logged rather than returned — the indexes speed reads up; nothing depends on them existing — and the next startup tries again.
func DensifyBuckets ¶
func DensifyBuckets(rows []BucketedRow, startUnix int64, bucketSeconds int, nBuckets int) []int64
DensifyBuckets takes a sparse list of {bucketStart, count} rows from the DB and emits a dense `nBuckets`-long slice aligned to `startUnix`. Bucket indexes outside the range are dropped — they can't render in a heatmap of fixed width.
func EnsureIndexes ¶ added in v0.6.0
EnsureIndexes creates missing indexes and removes their declared obsolete field indexes after the replacements are usable. It is safe to run from several processes at once: osctrl-api and osctrl-tls both run it at startup.
On Postgres the build is CONCURRENTLY, so writers are never blocked however large the table is. A concurrent build that is interrupted — the process restarting mid-build, say — leaves an invalid index that Postgres keeps maintaining on every write but never reads; those are dropped and rebuilt, unless another session is still building them.
MySQL builds secondary indexes online. SQLite holds its single write lock for the build, which is short on the databases it is used for.
Types ¶
type BucketCount ¶
BucketCount represents one row of a bucketed count query.
type BucketedRow ¶
type BucketedRow struct {
BucketStart int64 `gorm:"column:bucket_start"`
Cnt int64 `gorm:"column:cnt"`
}
BucketedRow is the raw scan target for the GROUP BY query. Stays dialect-agnostic since every dialect returns BIGINT for FLOOR/CAST expressions.
type Index ¶ added in v0.6.0
type Index struct {
// Model is the GORM model of the table, which also supplies any table
// prefix from the naming strategy.
Model any
// Name must be unique in the schema.
Name string
// Columns are database column names, in index order.
Columns []string
// Replaces lists obsolete GORM field indexes to drop only after this
// replacement exists and is usable. Names respect the naming strategy.
Replaces []string
}
Index is a secondary index created outside GORM's struct tags.
Tags would be the usual way, but they are unsafe for string columns that were created without an index: GORM's MySQL driver types an untagged string as longtext and an indexed one as varchar(191), so tagging an existing column makes AutoMigrate issue an ALTER that rebuilds the table and fails, or truncates, on any value longer than 191 characters. Tags also cannot reach the timestamps of an embedded gorm.Model.