store

package
v0.0.0-...-3e52749 Latest Latest
Warning

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

Go to latest
Published: Mar 26, 2022 License: Apache-2.0 Imports: 22 Imported by: 0

README

The persistent store component used by the project is etcd which provides a mean of storing key/value pairs along with a selection of other features. For the purposes of this document, the important feature is the naming scheme used by the project.

Production Namespace

For production, all persisted keys have a standard prefix of "/CloudChamber/V0.1". Under this prefix, names fall into two classes,

  1. names used for discovery

  2. names used for entities

The primary difference is the manner in which these keys are “discovered”. To support “list” type operation, the store needs to be able to provide a means to locate all the listable elements of the type being located. The underlying mechanism is a prefix search where all the elements in the list have a common prefix.

For example, there is a key/value pair store for each user with an account. All the user keys are stored with single common prefix so a single prefix search will locate all the user records in a single query (size limits allowing).

Names Used for Discovery

The most fundamental elements search for are users, racks and workloads.

Element Prefix used for initial List() operation
users /CloudChamber/V0.1/users
racks /CloudChamber/V0.1/racks
workloads /CloudChamber/V0.1/workloads

Note the use of the plural when searching for the set of elements.

Discovering Users

User records are listable and so to list the user records using a prefix, or a key to retrieve a single user record, use the following table to construct the prefix/key accordingly

Prefix/Key Location (excluding prefix /CloudChamber/V0.1) Returns
prefix /users List of user definitions
key /users/{username} Single user details

Since there are no listable elements within a single user account, all the per-user data can be returned as part of the user listing.

Discovering Racks

Where appropriate, the returned list of items can then be used to issue a second search for the next listable elements. Consider “racks”. Once we have a list of racks, each rack contains a “pdu”, a “tor”, and a collection of blades. To reference a specific rack a key can be constructed using prefix containing the singular of the element, along with some additional information to identify what part of the rack is of interest. For example, each rack contains a single “pdu”, a single “tor” and a set of blades. Use the following table to construct an appropriate prefix/key to reference the tor or pdu records.

However, since there are multiple blades expected, the blade records are listable using a prefix or the details for a specific blade using a key, both of which can constructed using the following table.

Prefix/Key Location (excluding prefix /CloudChamber/V0.1) Returns
prefix /racks List of rack definitions
key /racks/{rackid} Single rack details
Key /rack/{rackid}/tor Details of tor
Key /rack/{rackid}/pdu Details of pdu
prefix /rack/{rackid}/blades List of instance descriptions
key /rack/{rackid}/blades/{bladeid} Single blade details

Fortunately, nothing within a blade is listable, so there all the per-blade data can be returned as part of the blade listing.

Discovering Workloads

Workloads are a little more complicated as there is an additional level so more care must be taken when forming prefixes and keys.

Prefix/Key Location (excluding prefix /CloudChamber/V0.1) Returns
prefix /workloads List of workload named definitions
key /workloads/{workloadid} Single workload definition
prefix /workload/{workloadid}/instances List of instance descriptions
key /workload/{workloadid}/instance/{instanceid}/actual Actual state for specific instance of specific workload
key /workload/{workloadid}/instance/{instanceid}/target target state for specific instance of specific workload

Test Usage and Test Data

For test usage, as enabled via the UseTestNamespace boolean option in the configuration file, the standard prefix is changed to "/CloudChamber/V0.1/Test/Xxxx” where the value of “Xxxx” is determined by the value for the “UseUniqueInstance” config boolean option. If not set (default) then “Xxxx” is “Standard” to form a standard prefix of

"/CloudChamber/V0.1/Test/Standard”

whereas if “UseUniqueInstance” is set to true, then a timestamp is used (determined by the store initiailization time) to allow the results from individual test passes to be retained from pass to pass.

These prefixes give a number of useful behaviours. By having a project specific prefix we should be able to share any etcd instance, assuming of course that no one else uses the same prefix. By having a well-defined test prefix, it becomes easy to monitor, control and prevent any interference between test data and production data.

Also, if the “UseTestNamespace” is set and the “PreCleanStore” is also set (UseUniqueInstance must NOT be set), then the store will automatically remove ALL keys and values with the prefix "/CloudChamber/V0.1/Test/Standard” to allow each test pass to start with a clean namespace.

Further, by using the prefix "/CloudChamber/V0.1/Test" it becomes very easy to manually monitor, identify or purge purge the store any and all test related records using the etcdctl utility without having to disturb the production data at all. This can be achieved with

etcdctl watch --prefix /CloudChamber/V0.1/

Documentation

Overview

Package store contains the implementation for the store used by various internal services to preserve state, both static and dynamic as needed.

It will permit updates, retrievals and will generate events on updates to registered subscribers.

The etcd clientv3 package documentation can be found at https://pkg.go.dev/go.etcd.io/etcd/clientv3?tab=doc

The primary methods to interact with the backing store (etcd at present) provided here are

ListWithPrefix()
ReadTxn()
WriteTxn()
DeleteTxn()
DeleteWithPrefix()

which typically take a string key or set of keys along with a set of string values for the WriteXxx() methods.

The ReadXxx() methods return an array of KeyValueResponse structs which describe a set of zero of more key/value pairs. Note that the keys are returned as string but the values are []byte slices. These can readily be converted to strings as necessary.

There are a set of methods used to establish a Store context object via the New() or NewWithDefault(). Alternatively the Store object can be directly allocated/initialized in a conventional fashion. The following additional methods mya also prove useful

Initialize()
SetAddress()
SetTimeoutConnect()
SetTimeoutRequest()

Once a Store object has been created and initialized, a connection needs to be established with the backend databased before any IO can take place, and once the store is no longer required, the connection must be released. These operations can be achieved with the following methods

Connect()
Disconnect()

Namespace

The store package will prepend all keys with a constant prefix name to allow CloudChamber to share an etcd instance is that is require, though at least for the present this is not advised.

Currently this prefix is the string

/CloudChamber/v0.1

This prefix is not visible to any client of the store package but can be useful with any maintenance operations are being undertaken on the etcd store using (say) the etcdctl utility. In particular all CloudChamber can be listed using

%gopath%\bin\etcdctl get --write-out=simple --prefix /CloudChamber/v0.1

and all the keys can be removed with

%gopath%\bin\etcdctl del --prefix /CloudChamber/v0.1

Index

Constants

View Source
const (
	ConditionCreate                 = Condition("new")
	ConditionRequired               = Condition("req")
	ConditionUnconditional          = Condition("*")
	ConditionRevisionNotEqual       = Condition("!=")
	ConditionRevisionLess           = Condition("<")
	ConditionRevisionLessOrEqual    = Condition("<=")
	ConditionRevisionEqual          = Condition("==")
	ConditionRevisionEqualOrGreater = Condition(">=")
	ConditionRevisionGreater        = Condition(">")
)

Set of specifiers for the type of condition in a conditional record update.

View Source
const (
	RevisionInvalid int64 = 0
)

RevisionInvalid is returned from certain operations if failure cases and is also used when defining unconditional write to the store.

Variables

This section is empty.

Functions

func Decode

func Decode(s string, m proto.Message) error

Decode is a default JSON encoded string to protobuf defined message decoder

func Encode

func Encode(m proto.Message) (s string, err error)

Encode is a default protobuf defined message to JSON encoded string encoder

func Initialize

func Initialize(ctx context.Context, cfg *config.GlobalConfig)

Initialize is a method used to initialise the basic global state used to access the back-end db service.

func PrepareTestNamespace

func PrepareTestNamespace(ctx context.Context, cfg *config.GlobalConfig)

PrepareTestNamespace will prepare the store to be used for test purposes. Optionally, this will clean out the store of data left from previous runs and set a test specific namespace for all subsequent store operations.

NOTE: It is expected that this is call soon after the store is initialized and before any

data related operations have taken place.

Types

type Action

type Action func(string) error

Action defines the signature for a function to be invoked when the WithAction option is used.

ToDo: need to add WithAction(actionRoutine) and WithRawValue() options.

type Cluster

type Cluster struct {
	ID      uint64
	Members []ClusterMember
}

Cluster is a structure which describes aspects of a cluster and the members of that cluster.

type ClusterMember

type ClusterMember struct {
	ID         uint64
	Name       string
	PeerURLs   []string
	ClientURLs []string
}

ClusterMember is a structure which describes aspects of a single member within a cluster

type Condition

type Condition string

Condition is a type used to define the test to be applied when making a conditional write to the store

func (*Condition) String

func (c *Condition) String() string

type Operations

type Operations interface {
	// Connect is a function that will establish a connection between the caller
	// backend store. A connection is required before any other function in this
	// interface may be attempted.
	//
	Connect() error

	// Disconnect is a function that terminates the connection between the
	// caller and the backend store. Once the connection has been terminated,
	// no further function on this interface may be attempted.
	//
	Disconnect()

	// Create is a function to create a single key, value record pair
	//
	Create(ctx context.Context, r namespace.KeyRoot, n string, v string) (revision int64, err error)

	// CreateMultiple is a function to create a set of related key, value pairs within a single operation (txn)
	//
	CreateMultiple(ctx context.Context, r namespace.KeyRoot, kvs *map[string]string) (revision int64, err error)

	// Delete is a function to delete a single key, value record pair
	//
	Delete(ctx context.Context, r namespace.KeyRoot, n string, rev int64) (revision int64, err error)

	// DeleteMultiple is a function to delete a set of related key, revision pairs
	// within a single operation (txn)
	//
	DeleteMultiple(ctx context.Context, r namespace.KeyRoot, kvs *map[string]int64) (int64, error)

	// List is a method to return all the user records using a single call.
	//
	List(ctx context.Context, r namespace.KeyRoot, n string) (records *map[string]Record, revision int64, err error)

	// Read is a method to retrieve the user record associated with the
	// supplied name, deal with any store related key prefixes, and decode
	// the json encoded record into something the caller understands.
	//
	Read(ctx context.Context, kr namespace.KeyRoot, n string) (value *string, revision int64, err error)

	// Update is a function to conditionally update a value for a single key
	//
	Update(ctx context.Context, r namespace.KeyRoot, n string, rev int64, v string) (revision int64, err error)

	// Watch is a method use to establish a watch point on a portion of
	// the namespace identified by the supplied prefix name.
	//
	Watch(ctx context.Context, r namespace.KeyRoot, n string) (*Watch, error)
}

Operations defines the exported store functions that callers are expected to use.

type Option

type Option func(*Options)

Option is the signature of the option functions used to select additional optional parameters on a base routine call.

func WithAction

func WithAction(action Action) Option

WithAction allows a caller to supply an action routine which is invoked on each record being processed within the transaction

func WithKeysOnly

func WithKeysOnly() Option

WithKeysOnly is an option applying to a Read() request to avoid reading any value(s) associated with the requested set of one or more keys.

This option is primarily useful when attempting to determine which keys are present when there is no immediate need to know the associated values. By restricting the amount of data being retrieved, this option may lead to an increase in performance and/or a reduction in consumed resources.

func WithPrefix

func WithPrefix() Option

WithPrefix is an option used to indicate the supplied name should be used as a prefix for the request. This is primarily useful for Read() and Delete() calls to indicate the supplied name is the root for a wildcard operation.

Care should be used when applying this option on a Delete() call as a small error could easily lead to an entire namespace being inadvertently deleted.

func WithRevision

func WithRevision(rev int64) Option

WithRevision is an option to supply a specific revision that applies to the request. For example, to modify a basic Read() request to read a specific revision of a record.

type Options

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

Options is a set of options supplied via zero or more WithXxxx() functions

type Record

type Record struct {
	Revision int64
	Value    string
}

Record is a struct defining a single value and the associated revision describing the store revision when the value was last updated.

type Request

type Request struct {
	Reason     string
	Records    map[string]Record
	Conditions map[string]Condition
	Actions    map[string]Action
}

Request is a struct defining the collection of values needed to make a request of the underlying store. Which values need to be set depend on the request. For example, setting any "value" for a read request is ignored.

type Response

type Response struct {
	Revision int64
	Records  map[string]Record
}

Response is a struct defining the set of values returned from a request.

type Store

type Store struct {
	Endpoints         []string
	TimeoutConnect    time.Duration
	TimeoutRequest    time.Duration
	Mutex             sync.Mutex
	Client            *clientv3.Client
	UnprefixedKV      clientv3.KV
	UnprefixedWatcher clientv3.Watcher
	UnprefixedLease   clientv3.Lease
	Namespace         string
	NamespaceSuffix   string
	TraceFlags        TraceFlags
}

Store is a struct used to collect all the interesting state values associated with communicating with an instance of the store

func New

func New(
	endpoints []string,
	timeoutConnect time.Duration,
	timeoutRequest time.Duration,
	traceFlags TraceFlags,
	namespace string) *Store

New is a method supplied values. These values can later be overridden with

SetAddress()
SetTimeoutConnection()
SetTimeoutResponse()

providing the store has not yet connected to the back-end db service.

func NewStore

func NewStore() *Store

NewStore is a method to allocate a new Store struct using the defaults which can later be overridden with

SetAddress()
SetTimeoutConnection()
SetTimeoutResponse()

providing the store has not yet connected to the back-end db service.

func (*Store) Connect

func (store *Store) Connect() error

Connect is a method that will establish a connection between the store object and the backend etcd database. A connection is required before any IO to the database can be attempted.

func (*Store) Create

func (store *Store) Create(ctx context.Context, r namespace.KeyRoot, n string, v string) (revision int64, err error)

Create is a function to create a single key, value record pair

func (*Store) CreateMultiple

func (store *Store) CreateMultiple(ctx context.Context, r namespace.KeyRoot, kvs *map[string]string) (revision int64, err error)

CreateMultiple is a function to create a set of related key, value pairs within a single operation (txn)

func (*Store) CreateWithEncode

func (store *Store) CreateWithEncode(
	ctx context.Context,
	r namespace.KeyRoot,
	n string,
	m proto.Message) (revision int64, err error)

CreateWithEncode is a function to create a single key, value record pair

func (*Store) Delete

func (store *Store) Delete(ctx context.Context, r namespace.KeyRoot, n string, rev int64) (revision int64, err error)

Delete is a function to delete a single key, value record pair

func (*Store) DeleteMultiple

func (store *Store) DeleteMultiple(ctx context.Context, r namespace.KeyRoot, kvs *map[string]int64) (int64, error)

DeleteMultiple is a function to delete a set of related key, revision pairs within a single operation (txn)

func (*Store) DeleteTxn

func (store *Store) DeleteTxn(ctx context.Context, request *Request) (response *Response, err error)

DeleteTxn is a method to delete a set of arbitrary keys within a single txn so they form a (self-)consistent operation.

func (*Store) DeleteWithPrefix

func (store *Store) DeleteWithPrefix(ctx context.Context, keyPrefix string) (response *Response, err error)

DeleteWithPrefix is a method used to remove an entire sub-tree of key/value pairs which have a common key name prefix.

func (*Store) Disconnect

func (store *Store) Disconnect()

Disconnect is a method used to terminate the connection between the store object instance and the backed etcd service. Once the connection has been terminated, no further IO should be attempted.

func (*Store) GetAddress

func (store *Store) GetAddress() []string

GetAddress is a method to retrieve the current set of addresses used to connect to the underlying store.

func (*Store) GetClusterMembers

func (store *Store) GetClusterMembers() (result *Cluster, err error)

GetClusterMembers is a method to fetch a description of the cluster to which the store object is currently connected.

func (*Store) GetNamespaceSuffix

func (store *Store) GetNamespaceSuffix() string

GetNamespaceSuffix is a method which can be used to query the current namespace prefix being used for individual requests to the underlying store service.

func (*Store) GetTimeoutConnect

func (store *Store) GetTimeoutConnect() time.Duration

GetTimeoutConnect is a method which can be used to query the current timeout being used when the connection to the underlying store service is being established.

func (*Store) GetTimeoutRequest

func (store *Store) GetTimeoutRequest() time.Duration

GetTimeoutRequest is a method which can be used to query the current timeout being used for individual requests to the underlying store service.

func (*Store) GetTraceFlags

func (store *Store) GetTraceFlags() TraceFlags

GetTraceFlags is a method to retrieve the current trace flags value.

func (*Store) Initialize

func (store *Store) Initialize(
	endpoints []string,
	timeoutConnect time.Duration,
	timeoutRequest time.Duration,
	traceFlags TraceFlags,
	namespaceSuffix string) error

Initialize is a method used to initialize a specific instance of a Store struct.

func (*Store) List

func (store *Store) List(ctx context.Context, r namespace.KeyRoot, n string) (records *map[string]Record, revision int64, err error)

List is a method to return all the user records using a single call.

NOTE: The returned set of records may exist but contain no records.

NOTE: This should only be used at present if the number of user records

      is limited as there is a limit to the number of records that can
      be fetched from the store in a single shot. Eventually this will
      be updated to use an "interrupted" enum style call to allow for
		 an essentially infinite number of records.

func (*Store) ListWithPrefix

func (store *Store) ListWithPrefix(ctx context.Context, keyPrefix string) (response *Response, err error)

ListWithPrefix is a method used to query for a set of zero or more key/value pairs which have a common prefix. The method will return all matching key/value pairs so care should be taken with key naming to avoid attempting to fetch a large number of key/value pairs.

It is not an error to attempt to retrieve an empty set. For example, when querying for the presence of a set of values, this method can be used which would successfully return an empty set of key/value pairs if there are no matches for the supplied key prefix.

func (*Store) Read

func (store *Store) Read(ctx context.Context, kr namespace.KeyRoot, n string) (value *string, revision int64, err error)

Read is a method to retrieve the user record associated with the supplied name, deal with any store related key prefixes, and decode the json encoded record into something the caller understands.

NOTE: A future enhancement may occur where the caller supplies an action

routine to take care of the decoding of the record itself allowing
this layer to only have to deal with the manipulation of the store,
the keys used to persist the callers records but not have to worry
about the encode/decode formats or indeed the target record itself.

func (*Store) ReadSingle

func (store *Store) ReadSingle(ctx context.Context, kr namespace.KeyRoot, n string, options ...Option) (int64, int64, *string, error)

ReadSingle is a method to retrieve the user record associated with the supplied name, deal with any store related key prefixes, and decode the json encoded record into something the caller understands.

NOTE: A future enhancement may occur where the caller supplies an action

routine to take care of the decoding of the record itself allowing
this layer to only have to deal with the manipulation of the store,
the keys used to persist the callers records but not have to worry
about the encode/decode formats or indeed the target record itself.

func (*Store) ReadWithDecode

func (store *Store) ReadWithDecode(
	ctx context.Context,
	kr namespace.KeyRoot,
	n string,
	m proto.Message) (revision int64, err error)

ReadWithDecode is a method to retrieve the user record associated with the supplied name, deal with any store related key prefixes, and decode the json encoded record into something the caller understands.

NOTE: A future enhancement may occur where the caller supplies an action

routine to take care of the decoding of the record itself allowing
this layer to only have to deal with the manipulation of the store,
the keys used to persist the callers records but not have to worry
about the encode/decode formats or indeed the target record itself.

func (*Store) SetAddress

func (store *Store) SetAddress(endpoints []string) error

SetAddress is a method that can be used to set or update the set of one or more endpoint that CloudChamber should use to connect to the backend etcd store.

This method can only be used when the store object is not connected.

func (*Store) SetNamespaceSuffix

func (store *Store) SetNamespaceSuffix(suffix string) error

SetNamespaceSuffix is a method that can be used to update the namespace prefix being used for all read and write operations. For production use this will typically be the default namespace but for test usage this is set to a specific test namespace to both avoid interfering with production data and to allow simpler cleanup and removal of old temporary test data.

func (*Store) SetTimeoutConnect

func (store *Store) SetTimeoutConnect(timeout time.Duration) error

SetTimeoutConnect is a method that can be used to update the timeout the store object uses when establishing a connection to the backend etcd store.

This method can only be used when the store object is not connected.

func (*Store) SetTimeoutRequest

func (store *Store) SetTimeoutRequest(timeout time.Duration) error

SetTimeoutRequest is a method that can be used to update the timeout the store object uses when establishing a connection to the backend etcd store.

The request timeout can be set or updated regardless of the connection state of the store object, although it will only affect IO initiated after the method has returned.

func (*Store) SetTraceFlags

func (store *Store) SetTraceFlags(traceLevel TraceFlags)

SetTraceFlags is a method that can be used to change the verbosity of the tracing.

The verbosity level can be set or updated regardless of the connection state of the store object.

func (*Store) SetWatch

func (store *Store) SetWatch(ctx context.Context, key string) (response *Watch, err error)

SetWatch is a method used to establish a watchpoint on a single key/value pair

func (*Store) SetWatchWithPrefix

func (store *Store) SetWatchWithPrefix(ctx context.Context, keyPrefix string) (response *Watch, err error)

SetWatchWithPrefix is a method used to establish a watchpoint on a entire sub-tree of key/value pairs which have a common key name prefix

func (*Store) Update

func (store *Store) Update(ctx context.Context, r namespace.KeyRoot, n string, rev int64, v string) (revision int64, err error)

Update is a function to conditionally update a value for a single key

func (*Store) UpdateClusterConnections

func (store *Store) UpdateClusterConnections() error

UpdateClusterConnections is a method which updates the current set of connections to the connected underlying store according to the currently available connections

func (*Store) UpdateWithEncode

func (store *Store) UpdateWithEncode(
	ctx context.Context,
	kr namespace.KeyRoot,
	n string,
	rev int64,
	m proto.Message) (revision int64, err error)

UpdateWithEncode is a function to conditionally update a value for a single key

func (*Store) Watch

func (store *Store) Watch(ctx context.Context, r namespace.KeyRoot, n string) (*Watch, error)

Watch is a method use to establish a watch point on a portion of the namespace identified by the supplied prefix name.

func (*Store) WriteTxn

func (store *Store) WriteTxn(ctx context.Context, request *Request) (response *Response, err error)

WriteTxn is a method to write/update a set of arbitrary keys within a single txn so they form a (self-)consistent set.

type TraceFlags

type TraceFlags uint

TraceFlags is the type used when setting of fetching the relevant flags representing the level of verbosity being used when tracing.

type Watch

type Watch struct {
	Events chan WatchEvent
	// contains filtered or unexported fields
}

Watch is a struct returned by a SetWatchXxx() call to the caller to allow the caller to observe the notification and when no longer required, close the channel via the Close() method

func (*Watch) Close

func (w *Watch) Close(ctx context.Context) error

Close is used to close the upstream event notification channel.

type WatchEvent

type WatchEvent struct {
	Type     WatchEventType
	Revision int64
	Key      string
	NewRev   int64
	NewVal   string
	OldRev   int64
	OldVal   string
}

WatchEvent is a struct used to describe a specific event to a particular key

type WatchEventType

type WatchEventType uint

WatchEventType is used to indicate the type of event that happened on the key under consideration

const (
	// WatchEventTypeCreate indicates the event was a Create operation
	//
	WatchEventTypeCreate WatchEventType = iota

	// WatchEventTypeUpdate indicates the event was an Update or modify operation
	//
	WatchEventTypeUpdate

	// WatchEventTypeDelete indicates the event was a Delete operation
	//
	WatchEventTypeDelete
)

Jump to

Keyboard shortcuts

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