ctlstore

package module
v0.0.4 Latest Latest
Warning

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

Go to latest
Published: Aug 3, 2023 License: MIT Imports: 22 Imported by: 0

README

ctlstore

ctlstore is a distributed data store that provides very low latency, always-available, "infinitely" scalable reads. The underlying mechanism for this is a SQLite database that runs on every host called the LDB. A daemon called the Reflector plays logged writes from a central database into the LDB. As this involves replicating the full data store on every host, it is only practical for situations where the write rate (<100/s total) and data volumes (<10GB total) are low.

Recommended reading:

Security

Note that because ctlstore replicates the central database to an LDB on each host with a reflector, that LDB contains all of the control data. In its current state that means that any application which has access to the LDB can access all of the data within it.

The implications of this are that you should not store data in ctlstore that should only be accessed by a subset of the applications that can read the LDB. Things like secrets, passwords, and so on, are an example of this.

The ctlstore system is meant to store non-sensitive configuration data.

Development

A MySQL database is needed to run the tests, which can be started using Docker Compose:

$ docker-compose up -d

Run the tests using make:

$ make test
# For more verbosity (`Q=` trick applies to all targets)
$ make test Q=

A single ctlstore binary is used for all functionality. Build it with make:

$ make build

Sync non-stdlib dependencies and pull them into ./vendor

$ make deps

Ctlstore uses Go modules. To build a docker image, the dependencies must be vendored first:

$ make vendor

Many of ctlstore's unit tests use mocks. To regenerate the mocks using counterfeiter:

$ make generate

Tying the Pieces Together

This project includes a docker-compose file docker-compose-example.yml. This initializes and runs

  • mysql (ctlstore SoR)
  • executive service (guards the ctlstore SoR)
  • reflector (builds the LDB)
  • heartbeat (mutates a ctlstore table periodically)
  • sidecar (provides HTTP API access to ctlstore reader API)
  • supervisor (periodically snapshots LDB)

To start it, run:

$ make deps
$ make vendor
$ docker-compose -f docker-compose-example.yml up -d

Documentation

Index

Constants

View Source
const (
	DefaultCtlstorePath      = "/var/spool/ctlstore/"
	DefaultChangelogFilename = "change.log"
)

Variables

View Source
var (
	ErrTableHasNoPrimaryKey = errors.New("Table provided has no primary key")
	ErrNeedFullKey          = errors.New("All primary key fields are required")
	ErrNoLedgerUpdates      = errors.New("no ledger updates have been received yet")
)
View Source
var Version string

Version is the current ctlstore client library version.

Functions

func Initialize deprecated

func Initialize(ctx context.Context, appName string, statsHandler stats.Handler)

Initialize sets up global state for thing including global metrics globalstats data and possibly more as time goes on.

Deprecated: see InitializeWithConfig

func InitializeWithConfig

func InitializeWithConfig(ctx context.Context, cfg Config)

InitializeWithConfig sets up global state for thing including global metrics globalstats data and possibly more as time goes on.

Types

type Config

type Config struct {
	// Stats specifies the config for reporting stats to the global
	// ctlstore stats namespace.
	//
	// By default, global stats are enabled with a set of sane defaults.
	Stats *globalstats.Config

	// LDBVersioning, if enabled, will instruct ctlstore to look for
	// LDBs inside of timestamp-delimited folders, and ctlstore will
	// hot-reload new LDBs as they appear.
	//
	// By default, this is disabled.
	LDBVersioning bool
}

type LDBReader

type LDBReader struct {
	Db *sql.DB
	// contains filtered or unexported fields
}

LDBReader reads data from the LDB. The external interface is thread-safe and it is safe to create as many of these as needed across multiple processes.

func NewLDBReaderFromDB

func NewLDBReaderFromDB(db *sql.DB) *LDBReader

Constructs an LDBReader from a sql.DB. Really only useful for testing.

func Reader

func Reader() (*LDBReader, error)

Reader returns an LDBReader that can be used globally.

func ReaderForPath

func ReaderForPath(path string) (*LDBReader, error)

ReaderForPath opens an LDB at the provided path and returns an LDBReader instance pointed at that LDB.

func (*LDBReader) Close

func (reader *LDBReader) Close() error

func (*LDBReader) GetLastSequence

func (reader *LDBReader) GetLastSequence(ctx context.Context) (schema.DMLSequence, error)

GetLastSequence returns the highest sequence number applied to the DB

func (*LDBReader) GetLedgerLatency

func (reader *LDBReader) GetLedgerLatency(ctx context.Context) (time.Duration, error)

GetLedgerLatency returns the difference between the current time and the timestamp from the last DML ledger update processed by the reflector. ErrNoLedgerUpdates will be returned if no DML statements have been processed.

func (*LDBReader) GetRowByKey

func (reader *LDBReader) GetRowByKey(
	ctx context.Context,
	out interface{},
	familyName string,
	tableName string,
	key ...interface{},
) (found bool, err error)

GetRowByKey fetches a row from the supplied table by the key parameter, filling the data into the out param.

The out param may be one of the following types:

  • pointer to struct
  • map[string]interface{}

The key parameter can support composite keys by passing a slice type.

func (*LDBReader) GetRowsByKeyPrefix

func (reader *LDBReader) GetRowsByKeyPrefix(ctx context.Context, familyName string, tableName string, key ...interface{}) (*Rows, error)

GetRowsByKeyPrefix returns a *Rows iterator that will supply all of the rows in the family and table match the supplied primary key prefix.

func (*LDBReader) Ping

func (reader *LDBReader) Ping(ctx context.Context) bool

Ping checks if the LDB is available

type LDBTestTableDef

type LDBTestTableDef struct {
	Family    string
	Name      string
	Fields    [][]string
	KeyFields []string
	Rows      [][]interface{}
}

LDBTestTableDef is used to pass a table definition to CreateTable for use in tests that need the LDB. The way the parameters are specified mimics the executive interface. Fields are passed as tuples of [name string, type string] where type can be something like "string" or "integer," just like the standard executive interface.

type LDBTestUtil

type LDBTestUtil struct {
	DB *sql.DB
	T  testing.TB
}

LDBTestUtil provides basic unit testing facilities for injecting data into a "fake" LDB.

func NewLDBTestUtil

func NewLDBTestUtil(t testing.TB) (*LDBTestUtil, func())

NewLDBTestUtil changes the global default LDB path to a temporary path.

This function is NOT concurrency safe.

func NewLDBTestUtilLocal added in v0.0.2

func NewLDBTestUtilLocal(t testing.TB) (*LDBTestUtil, func())

NewLDBTestUtilLocal is just like NewLDBTestUtil above except it does not rely on global state and is therefore threadsafe, at the cost of requiring users to use ensure that the DB property is used to initialize the ctlstore Reader instead of relying on the global/default init.

func (*LDBTestUtil) CreateTable

func (tu *LDBTestUtil) CreateTable(def LDBTestTableDef)

CreateTable creates a table in the target test LDB.

func (*LDBTestUtil) DeleteAll

func (tu *LDBTestUtil) DeleteAll(family string, table string)

DeleteAll deletes all rows from the given table.

func (*LDBTestUtil) InsertRows

func (tu *LDBTestUtil) InsertRows(family string, table string, rows [][]interface{})

InsertRows well, inserts rows into the LDB. Rows are passed as tuples in the table's column order.

func (*LDBTestUtil) Reset

func (tu *LDBTestUtil) Reset()

Reset completely clears the test LDB

type Rows

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

Rows composes an *sql.Rows and allows scanning ctlstore table rows into structs or maps, similar to how the GetRowByKey reader method works.

The contract around Next/Err/Close is the same was it is for *sql.Rows.

func (*Rows) Close

func (r *Rows) Close() error

Close closes the underlying *sql.Rows.

func (*Rows) Err

func (r *Rows) Err() error

Err returns any error that could have been caused during the invocation of Next(). If Next() returns false, the caller must always check Err() to see if that's why iteration failed.

func (*Rows) Next

func (r *Rows) Next() bool

Next returns true if there's another row available.

func (*Rows) Scan

func (r *Rows) Scan(target interface{}) error

Scan deserializes the current row into the specified target. The target must be either a pointer to a struct, or a map[string]interface{}.

Directories

Path Synopsis
pkg
cmd/ctlstore-mutator
This program sends constant load to the executive service.
This program sends constant load to the executive service.
executive/fakes
Code generated by counterfeiter.
Code generated by counterfeiter.
fakes
Code generated by counterfeiter.
Code generated by counterfeiter.
globalstats
Package globalstats provides configurable singleton stats instance for ctlstore.
Package globalstats provides configurable singleton stats instance for ctlstore.
ldb
ledger/fakes
Code generated by counterfeiter.
Code generated by counterfeiter.
supervisor/fakes
Code generated by counterfeiter.
Code generated by counterfeiter.
utils
this package hosts utilities that probably don't belong elsewhere.
this package hosts utilities that probably don't belong elsewhere.

Jump to

Keyboard shortcuts

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