Each save that records a batch also deletes, in the same transaction, the batches older than database.change_batch_retention (30 days by default; 0 or less keeps them for ever), with their records of opportunities. A client still holding a deleted batch gets 410 Gone from ?changed_after= and reloads the whole list, as for any unknown batch.
58 lines
1.8 KiB
Go
58 lines
1.8 KiB
Go
package store
|
|
|
|
import (
|
|
"database/sql"
|
|
"fmt"
|
|
"time"
|
|
)
|
|
|
|
// Option configures Open.
|
|
type Option func(*options)
|
|
|
|
type options struct {
|
|
autoMigrate bool
|
|
changeBatchRetention time.Duration
|
|
}
|
|
|
|
// OpenDB opens the SQLite database at path with the store's connection
|
|
// settings (foreign keys, WAL, busy timeout, immediate transactions). The
|
|
// file is created on first use. Use it for anything that works on the store's
|
|
// database directly, such as running migrations.
|
|
func OpenDB(path string) (*sql.DB, error) {
|
|
return sql.Open("sqlite3", dsn(path))
|
|
}
|
|
|
|
// WithAutoMigrate makes Open apply pending migrations instead of returning
|
|
// ErrNeedsMigration. Intended for tests and throwaway databases; applications
|
|
// should migrate explicitly with the migrate command.
|
|
func WithAutoMigrate() Option {
|
|
return func(opts *options) {
|
|
opts.autoMigrate = true
|
|
}
|
|
}
|
|
|
|
// WithChangeBatchRetention makes the store delete change batches older than
|
|
// retention whenever it records a new one, so a client can catch up from a
|
|
// batch for that long (see ListChangedOpportunities). Without it, or with 0
|
|
// or less, batches are kept for ever.
|
|
func WithChangeBatchRetention(retention time.Duration) Option {
|
|
return func(opts *options) {
|
|
opts.changeBatchRetention = retention
|
|
}
|
|
}
|
|
|
|
// dsn builds the mattn/go-sqlite3 connection string.
|
|
//
|
|
// - _foreign_keys: SQLite ignores REFERENCES clauses unless this is on.
|
|
// - _journal_mode=WAL: readers don't block the writer or each other.
|
|
// - _busy_timeout: wait for a lock rather than failing immediately.
|
|
// - _txlock=immediate: transactions take the write lock at BEGIN, so a
|
|
// read-then-write transaction (as in SaveOpportunity) can't fail with
|
|
// SQLITE_BUSY halfway through.
|
|
func dsn(path string) string {
|
|
return fmt.Sprintf(
|
|
"file:%s?_foreign_keys=on&_journal_mode=WAL&_busy_timeout=5000&_txlock=immediate",
|
|
path,
|
|
)
|
|
}
|