sqlstore

package
v0.6.1 Latest Latest
Warning

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

Go to latest
Published: Sep 28, 2026 License: MIT Imports: 13 Imported by: 0

Documentation

Overview

Package sqlstore keeps CronWatch's jobs, runs and state in the app's own database through database/sql: SQLite, Postgres or MySQL (and MariaDB). The app brings its driver and its *sql.DB; this package imports none, so the cronwatch module needs no driver at all.

db, _ := sql.Open("sqlite", "file:data/app.db")    // modernc.org/sqlite
db, _ := sql.Open("pgx", os.Getenv("DATABASE_URL")) // github.com/jackc/pgx/v5/stdlib
db, _ := sql.Open("mysql", "app:pw@tcp(db:3306)/app") // github.com/go-sql-driver/mysql

store, err := sqlstore.New(db, sqlstore.Postgres)
cw, err := cronwatch.New(cronwatch.WithStore(store))

The tables are the SDK's (stores/sql.ts): the same names, columns and statements, and the SDK's JSON in the JSON columns byte for byte, so a Go process shares a database with a Node, Ruby, Python or PHP one.

Index

Constants

View Source
const DefaultPrefix = "cronwatch_"

DefaultPrefix starts every table name unless Prefix says otherwise.

Variables

This section is empty.

Functions

This section is empty.

Types

type Dialect

type Dialect string

Dialect is the database's SQL.

const (
	SQLite   Dialect = "sqlite"
	Postgres Dialect = "postgres"
	// MySQL 8.0.13 or newer, or MariaDB 10.6 or newer.
	MySQL Dialect = "mysql"
)

The dialects.

type Option

type Option func(*Store)

Option configures a store.

func Prefix

func Prefix(prefix string) Option

Prefix starts every table name: lowercase letters, digits and underscores. Default "cronwatch_".

type Store

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

Store is a cronwatch.Store over a *sql.DB. Safe for use by many goroutines at once.

On SQLite it holds one connection of the pool (the SDK's store has one connection too): an in-memory database is one per connection, and one writer at a time is what SQLite allows anyway. That connection is put in WAL mode (with the SDK's retry of a busy database while switching), with busy_timeout 5000 and synchronous NORMAL. On Postgres and MySQL it uses the pool, each statement on its own (autocommit), so its writes never join a transaction the app has open. So a pool limited to one connection (db.SetMaxOpenConns(1)) leaves the app none on SQLite, and waits on an app's open transaction on the others: give it room for the store too.

The tests are in the sqltest module beside this package (SQLite, Postgres, MySQL and MariaDB, and a file shared with the SDK in Node), kept apart so the drivers never become the cronwatch module's requirements.

func New

func New(db *sql.DB, dialect Dialect, options ...Option) (*Store, error)

New is a store over db in the dialect given.

func (*Store) Close

func (s *Store) Close() error

Close gives SQLite's connection back to the pool. The *sql.DB is the app's, and stays open.

func (*Store) CompareAndSetState

func (s *Store) CompareAndSetState(ctx context.Context, st cronwatch.JobState, expected int64) (bool, error)

func (*Store) DeleteJob

func (s *Store) DeleteJob(ctx context.Context, name string) error

DeleteJob removes the job, its runs and its state in one transaction.

func (*Store) DeleteRunIf

func (s *Store) DeleteRunIf(ctx context.Context, id, job string, status cronwatch.RunStatus) (bool, error)

DeleteRunIf deletes a run only while it is of job and in status, in one statement, and says whether it did. A DELETE counts the rows it matched on every dialect, MySQL included.

func (*Store) Dialect

func (s *Store) Dialect() Dialect

Dialect is the store's dialect.

func (*Store) GetJob

func (s *Store) GetJob(ctx context.Context, name string) (*cronwatch.StoredJob, error)

func (*Store) GetRun

func (s *Store) GetRun(ctx context.Context, id string) (*cronwatch.Run, error)

func (*Store) GetState

func (s *Store) GetState(ctx context.Context, job string) (*cronwatch.JobState, error)

func (*Store) Init

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

Init makes the tables. On Postgres many processes starting at once would race CREATE TABLE IF NOT EXISTS, which Postgres can reject with a unique violation on pg_type, so they take turns under an advisory lock per prefix. MySQL commits CREATE TABLE at once, so call Init when nothing is open (it runs at the client's first use).

func (*Store) InsertRun

func (s *Store) InsertRun(ctx context.Context, r cronwatch.Run) error

func (*Store) LastRun

func (s *Store) LastRun(ctx context.Context, job string) (*cronwatch.Run, error)

func (*Store) ListJobs

func (s *Store) ListJobs(ctx context.Context) ([]cronwatch.StoredJob, error)

func (*Store) ListRuns

func (s *Store) ListRuns(ctx context.Context, job string, limit int) ([]cronwatch.Run, error)

func (*Store) Prune

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

func (*Store) RunningRuns

func (s *Store) RunningRuns(ctx context.Context) ([]cronwatch.Run, error)

func (*Store) SetState

func (s *Store) SetState(ctx context.Context, st cronwatch.JobState) error

func (*Store) TablePrefix

func (s *Store) TablePrefix() string

TablePrefix is the prefix of the store's tables.

func (*Store) UpdateRun

func (s *Store) UpdateRun(ctx context.Context, r cronwatch.Run) error

func (*Store) UpdateRunIf

func (s *Store) UpdateRunIf(ctx context.Context, r cronwatch.Run, from []cronwatch.RunStatus) (bool, error)

func (*Store) UpsertJob

func (s *Store) UpsertJob(ctx context.Context, def cronwatch.Definition, now int64) error

Jump to

Keyboard shortcuts

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