subsystems

package
v7.4.0 Latest Latest
Warning

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

Go to latest
Published: Apr 11, 2024 License: Apache-2.0 Imports: 8 Imported by: 4

Documentation

Overview

Package subsystems contains interfaces for implementation of custom LaunchDarkly components.

Most applications will not need to refer to these types. You will use them if you are creating a plug-in component, such as a database integration, or a test fixture. They are also used as interfaces for the built-in SDK components, so that plugin components can be used interchangeably with those: for instance, Config.DataStore uses the type subsystems.DataStore as an abstraction for the data store component.

The package also includes concrete types that are used as parameters within these interfaces.

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

This section is empty.

Types

type BasicClientContext

type BasicClientContext struct {
	SDKKey               string
	ApplicationInfo      interfaces.ApplicationInfo
	HTTP                 HTTPConfiguration
	Logging              LoggingConfiguration
	Offline              bool
	ServiceEndpoints     interfaces.ServiceEndpoints
	DataSourceUpdateSink DataSourceUpdateSink
	DataStoreUpdateSink  DataStoreUpdateSink
}

BasicClientContext is the basic implementation of the ClientContext interface, not including any private fields that the SDK may use for implementation details.

func (BasicClientContext) GetApplicationInfo

func (b BasicClientContext) GetApplicationInfo() interfaces.ApplicationInfo

func (BasicClientContext) GetDataSourceUpdateSink

func (b BasicClientContext) GetDataSourceUpdateSink() DataSourceUpdateSink

func (BasicClientContext) GetDataStoreUpdateSink

func (b BasicClientContext) GetDataStoreUpdateSink() DataStoreUpdateSink

func (BasicClientContext) GetHTTP

func (BasicClientContext) GetLogging

func (BasicClientContext) GetOffline

func (b BasicClientContext) GetOffline() bool

func (BasicClientContext) GetSDKKey

func (b BasicClientContext) GetSDKKey() string

func (BasicClientContext) GetServiceEndpoints

func (b BasicClientContext) GetServiceEndpoints() interfaces.ServiceEndpoints

type BigSegmentMembership

type BigSegmentMembership interface {
	// CheckMembership tests whether the context is explicitly included or explicitly excluded in the
	// specified segment, or neither. The segment is identified by a segmentRef which is not the
	// same as the segment key-- it includes the key but also versioning information that the SDK
	// will provide. The store implementation should not be concerned with the format of this.
	//
	// If the context is explicitly included (regardless of whether the context is also explicitly
	// excluded or not-- that is, inclusion takes priority over exclusion), the method returns an
	// OptionalBool with a true value.
	//
	// If the context is explicitly excluded, and is not explicitly included, the method returns an
	// OptionalBool with a false value.
	//
	// If the context's status in the segment is undefined, the method returns OptionalBool{} with no
	// value (so calling IsDefined() on it will return false).
	CheckMembership(segmentRef string) ldvalue.OptionalBool
}

BigSegmentMembership is the return type of BigSegmentStore.GetContextMembership(). It is associated with a single evaluation context, and provides the ability to check whether that context is included in or excluded from any number of Big Segments.

This is an immutable snapshot of the state for this context at the time GetMembership was called. Calling GetMembership should not cause the state to be queried again. The object should be safe for concurrent access by multiple goroutines.

type BigSegmentStore

type BigSegmentStore interface {
	io.Closer

	// GetMetadata returns information about the overall state of the store. This method will be
	// called only when the SDK needs the latest state, so it should not be cached.
	GetMetadata() (BigSegmentStoreMetadata, error)

	// GetMembership queries the store for a snapshot of the current segment state for a specific
	// evaluation context. The contextHash is a base64-encoded string produced by hashing the context key
	// as defined by the Big Segments specification; the store implementation does not need to know the
	// details of how this is done, because it deals only with already-hashed keys, but the string can
	// be assumed to only contain characters that are valid in base64.
	GetMembership(contextHash string) (BigSegmentMembership, error)
}

BigSegmentStore is an interface for a read-only data store that allows querying of context membership in Big Segments.

"Big Segments" are a specific type of user segments. For more information, read the LaunchDarkly documentation about user segments: https://docs.launchdarkly.com/home/users

type BigSegmentStoreMetadata

type BigSegmentStoreMetadata struct {
	// LastUpToDate is the timestamp of the last update to the BigSegmentStore. It is zero if
	// the store has never been updated.
	LastUpToDate ldtime.UnixMillisecondTime
}

BigSegmentStoreMetadata contains values returned by BigSegmentStore.GetMetadata().

type BigSegmentsConfiguration

type BigSegmentsConfiguration interface {
	// GetStore returns the data store instance that is used for Big Segments data.
	GetStore() BigSegmentStore

	// GetContextCacheSize returns the value set by BigSegmentsConfigurationBuilder.CacheSize.
	GetContextCacheSize() int

	// GetContextCacheTime returns the value set by BigSegmentsConfigurationBuilder.CacheTime.
	GetContextCacheTime() time.Duration

	// GetStatusPollInterval returns the value set by BigSegmentsConfigurationBuilder.StatusPollInterval.
	GetStatusPollInterval() time.Duration

	// StaleAfter returns the value set by BigSegmentsConfigurationBuilder.StaleAfter.
	GetStaleAfter() time.Duration
}

BigSegmentsConfiguration encapsulates the SDK's configuration with regard to Big Segments.

"Big Segments" are a specific type of user segments. For more information, read the LaunchDarkly documentation about user segments: https://docs.launchdarkly.com/home/users

See ldcomponents.BigSegmentsConfigurationBuilder for more details on these properties.

type ClientContext

type ClientContext interface {
	// GetSDKKey returns the configured SDK key.
	GetSDKKey() string

	// GetApplicationInfo returns the configuration for application metadata.
	GetApplicationInfo() interfaces.ApplicationInfo

	// GetHTTP returns the configured HTTPConfiguration.
	GetHTTP() HTTPConfiguration

	// GetLogging returns the configured LoggingConfiguration.
	GetLogging() LoggingConfiguration

	// GetOffline returns true if the client was configured to be completely offline.
	GetOffline() bool

	// GetServiceEndpoints returns the configuration for service URIs.
	GetServiceEndpoints() interfaces.ServiceEndpoints

	// GetDataSourceUpdateSink returns the component that DataSource implementations use to deliver
	// data and status updates to the SDK.
	//
	// This component is only available when the SDK is creating a DataSource. Otherwise the method
	// returns nil.
	GetDataSourceUpdateSink() DataSourceUpdateSink

	// GetDataStoreUpdateSink returns the component that DataSource implementations use to deliver
	// data store status updates to the SDK.
	//
	// This component is only available when the SDK is creating a DataStore. Otherwise the method
	// returns nil.
	GetDataStoreUpdateSink() DataStoreUpdateSink
}

ClientContext provides context information from LDClient when creating other components.

This is passed as a parameter to the factory methods for implementations of DataStore, DataSource, etc. The actual implementation type may contain other properties that are only relevant to the built-in SDK components and are therefore not part of the public interface; this allows the SDK to add its own context information as needed without disturbing the public API. However, for test purposes you may use the simple struct type BasicClientContext.

type ComponentConfigurer

type ComponentConfigurer[T any] interface {
	// Build is called internally by the SDK to create an implementation instance. Applications
	// should not need to call this method.
	Build(clientContext ClientContext) (T, error)
}

ComponentConfigurer is a common interface for SDK component factories and configuration builders. Applications should not need to implement this interface.

type DataSource

type DataSource interface {
	io.Closer

	// IsInitialized returns true if the data source has successfully initialized at some point.
	//
	// Once this is true, it should remain true even if a problem occurs later.
	IsInitialized() bool

	// Start tells the data source to begin initializing. It should not try to make any connections
	// or do any other significant activity until Start is called.
	//
	// The data source should close the closeWhenReady channel if and when it has either successfully
	// initialized for the first time, or determined that initialization cannot ever succeed.
	Start(closeWhenReady chan<- struct{})
}

DataSource describes the interface for an object that receives feature flag data.

type DataSourceUpdateSink

type DataSourceUpdateSink interface {
	// Init overwrites the current contents of the data store with a set of items for each collection.
	//
	// If the underlying data store returns an error during this operation, the SDK will log it,
	// and set the data source state to DataSourceStateInterrupted with an error of
	// DataSourceErrorKindStoreError. It will not return the error to the data source, but will
	// return false to indicate that the operation failed.
	Init(allData []ldstoretypes.Collection) bool

	// Upsert updates or inserts an item in the specified collection. For updates, the object will only be
	// updated if the existing version is less than the new version.
	//
	// To mark an item as deleted, pass an ItemDescriptor with a nil Item and a nonzero version
	// number. Deletions must be versioned so that they do not overwrite a later update in case updates
	// are received out of order.
	//
	// If the underlying data store returns an error during this operation, the SDK will log it,
	// and set the data source state to DataSourceStateInterrupted with an error of
	// DataSourceErrorKindStoreError. It will not return the error to the data source, but will
	// return false to indicate that the operation failed.
	Upsert(kind ldstoretypes.DataKind, key string, item ldstoretypes.ItemDescriptor) bool

	// UpdateStatus informs the SDK of a change in the data source's status.
	//
	// Data source implementations should use this method if they have any concept of being in a valid
	// state, a temporarily disconnected state, or a permanently stopped state.
	//
	// If newState is different from the previous state, and/or newError is non-empty, the SDK
	// will start returning the new status (adding a timestamp for the change) from
	// DataSourceStatusProvider.GetStatus(), and will trigger status change events to any
	// registered listeners.
	//
	// A special case is that if newState is DataSourceStateInterrupted, but the previous state was
	// but the previous state was DataSourceStateInitializing, the state will remain at Initializing
	// because Interrupted is only meaningful after a successful startup.
	UpdateStatus(newState interfaces.DataSourceState, newError interfaces.DataSourceErrorInfo)

	// GetDataStoreStatusProvider returns an object that provides status tracking for the data store, if
	// applicable.
	//
	// This may be useful if the data source needs to be aware of storage problems that might require it
	// to take some special action: for instance, if a database outage may have caused some data to be
	// lost and therefore the data should be re-requested from LaunchDarkly.
	GetDataStoreStatusProvider() interfaces.DataStoreStatusProvider
}

DataSourceUpdateSink is an interface that a data source implementation will use to push data into the SDK.

Application code does not need to use this type. It is for data source implementations.

The data source interacts with this object, rather than manipulating the data store directly, so that the SDK can perform any other necessary operations that must happen when data is updated. The SDK passes this in the ClientContext when it is creating a data source component.

type DataStore

type DataStore interface {
	io.Closer

	// Init overwrites the store's contents with a set of items for each collection.
	//
	// All previous data should be discarded, regardless of versioning.
	//
	// The update should be done atomically. If it cannot be done atomically, then the store
	// must first add or update each item in the same order that they are given in the input
	// data, and then delete any previously stored items that were not in the input data.
	Init(allData []ldstoretypes.Collection) error

	// Get retrieves an item from the specified collection, if available.
	//
	// If the specified key does not exist in the collection, it should return an ItemDescriptor
	// whose Version is -1.
	//
	// If the item has been deleted and the store contains a placeholder, it should return an
	// ItemDescriptor whose Version is the version of the placeholder, and whose Item is nil.
	Get(kind ldstoretypes.DataKind, key string) (ldstoretypes.ItemDescriptor, error)

	// GetAll retrieves all items from the specified collection.
	//
	// If the store contains placeholders for deleted items, it should include them in the results,
	// not filter them out.
	GetAll(kind ldstoretypes.DataKind) ([]ldstoretypes.KeyedItemDescriptor, error)

	// Upsert updates or inserts an item in the specified collection. For updates, the object will only be
	// updated if the existing version is less than the new version.
	//
	// The SDK may pass an ItemDescriptor whose Item is nil, to represent a placeholder for a deleted
	// item. In that case, assuming the version is greater than any existing version of that item, the
	// store should retain that placeholder rather than simply not storing anything.
	//
	// The method returns true if the item was updated, or false if it was not updated because the store
	// contains an equal or greater version.
	Upsert(kind ldstoretypes.DataKind, key string, item ldstoretypes.ItemDescriptor) (bool, error)

	// IsInitialized returns true if the data store contains a data set, meaning that Init has been
	// called at least once.
	//
	// In a shared data store, it should be able to detect this even if Init was called in a
	// different process: that is, the test should be based on looking at what is in the data store.
	// Once this has been determined to be true, it can continue to return true without having to
	// check the store again; this method should be as fast as possible since it may be called during
	// feature flag evaluations.
	IsInitialized() bool

	// IsStatusMonitoringEnabled returns true if this data store implementation supports status
	// monitoring.
	//
	// This is normally only true for persistent data stores created with ldcomponents.PersistentDataStore(),
	// but it could also be true for any custom DataStore implementation that makes use of the
	// statusUpdater parameter provided to the DataStoreFactory. Returning true means that the store
	// guarantees that if it ever enters an invalid state (that is, an operation has failed or it knows
	// that operations cannot succeed at the moment), it will publish a status update, and will then
	// publish another status update once it has returned to a valid state.
	//
	// The same value will be returned from DataStoreStatusProvider.IsStatusMonitoringEnabled().
	IsStatusMonitoringEnabled() bool
}

DataStore is an interface for a data store that holds feature flags and related data received by the SDK.

Ordinarily, the only implementations of this interface are the default in-memory implementation, which holds references to actual SDK data model objects, and the persistent data store implementation that delegates to a PersistentDataStore.

type DataStoreUpdateSink

type DataStoreUpdateSink interface {
	// UpdateStatus informs the SDK of a change in the data store's operational status.
	//
	// This is what makes the status monitoring mechanisms in DataStoreStatusProvider work.
	UpdateStatus(newStatus interfaces.DataStoreStatus)
}

DataStoreUpdateSink is an interface that a data store implementation can use to report information back to the SDK.

Application code does not need to use this type. It is for data store implementations.

The SDK passes this in the ClientContext when it is creating a data store component.

type DiagnosticDescription

type DiagnosticDescription interface {
	// DescribeConfiguration should return a JSON value or ldvalue.Null().
	//
	// For custom components, this must be a string value that describes the basic nature of this component
	// implementation (e.g. "Redis"). Built-in LaunchDarkly components may instead return a JSON object
	// containing multiple properties specific to the LaunchDarkly diagnostic schema.
	DescribeConfiguration(context ClientContext) ldvalue.Value
}

DiagnosticDescription is an optional interface for components to describe their own configuration.

The SDK uses a simplified JSON representation of its configuration when recording diagnostics data. Any component type that implements ComponentConfigurer may choose to contribute values to this representation, although the SDK may or may not use them.

type HTTPConfiguration

type HTTPConfiguration struct {
	// DefaultHeaders contains the basic headers that should be added to all HTTP requests from SDK
	// components to LaunchDarkly services, based on the current SDK configuration. This map is never
	// modified once created.
	DefaultHeaders http.Header

	// CreateHTTPClient is a function that returns a new HTTP client instance based on the SDK configuration.
	//
	// The SDK will ensure that this field is non-nil before passing it to any component.
	CreateHTTPClient func() *http.Client
}

HTTPConfiguration encapsulates top-level HTTP configuration that applies to all SDK components.

See ldcomponents.HTTPConfigurationBuilder for more details on these properties.

type LoggingConfiguration

type LoggingConfiguration struct {
	// Loggers is a configured ldlog.Loggers instance for general SDK logging.
	Loggers ldlog.Loggers

	// LogDataSourceOutageAsErrorAfter is the time threshold, if any, after which the SDK
	// will log a data source outage at Error level instead of Warn level. See
	// LoggingConfigurationBuilderLogDataSourceOutageAsErrorAfter().
	LogDataSourceOutageAsErrorAfter time.Duration

	// LogEvaluationErrors is true if evaluation errors should be logged.
	LogEvaluationErrors bool

	// LogContextKeyInErrors is true if context keys may be included in logging.
	LogContextKeyInErrors bool
}

LoggingConfiguration encapsulates the SDK's general logging configuration.

See ldcomponents.LoggingConfigurationBuilder for more details on these properties.

type PersistentDataStore

type PersistentDataStore interface {
	io.Closer

	// Init overwrites the store's contents with a set of items for each collection.
	//
	// All previous data should be discarded, regardless of versioning.
	//
	// The update should be done atomically. If it cannot be done atomically, then the store
	// must first add or update each item in the same order that they are given in the input
	// data, and then delete any previously stored items that were not in the input data.
	Init(allData []ldstoretypes.SerializedCollection) error

	// Get retrieves an item from the specified collection, if available.
	//
	// If the specified key does not exist in the collection, it should return a SerializedItemDescriptor
	// with a Version of -1 and an Item of nil.
	//
	// If the item has been deleted and the store contains a placeholder, it should return that
	// placeholder rather than filtering it out.
	Get(kind ldstoretypes.DataKind, key string) (ldstoretypes.SerializedItemDescriptor, error)

	// GetAll retrieves all items from the specified collection.
	//
	// If the store contains placeholders for deleted items, it should include them in the results,
	// not filter them out.
	GetAll(kind ldstoretypes.DataKind) ([]ldstoretypes.KeyedSerializedItemDescriptor, error)

	// Upsert updates or inserts an item in the specified collection. For updates, the object will only be
	// updated if the existing version is less than the new version.
	//
	// The SDK may pass a SerializedItemDescriptor that represents a placeholder for a deleted item. In
	// that case, assuming the version is greater than any existing version of that item, the store should
	// retain that placeholder rather than simply not storing anything.
	//
	// The method returns the updated item if the update was successful; or, if the update was not
	// successful because the store contained an equal or higher version, it returns the item that is
	// in the store.
	Upsert(kind ldstoretypes.DataKind, key string, item ldstoretypes.SerializedItemDescriptor) (bool, error)

	// IsInitialized returns true if the data store contains a data set, meaning that Init has been
	// called at least once.
	//
	// In a shared data store, it should be able to detect this even if Init was called in a
	// different process: that is, the test should be based on looking at what is in the data store.
	// Once this has been determined to be true, it can continue to return true without having to
	// check the store again; this method should be as fast as possible since it may be called during
	// feature flag evaluations.
	IsInitialized() bool

	// IsStoreAvailable tests whether the data store seems to be functioning normally.
	//
	// This should not be a detailed test of different kinds of operations, but just the smallest possible
	// operation to determine whether (for instance) we can reach the database.
	//
	// Whenever one of the store's other methods returns an error, the SDK will assume that it may have
	// become unavailable (e.g. the database connection was lost). The SDK will then call
	// IsStoreAvailable() at intervals until it returns true.
	IsStoreAvailable() bool
}

PersistentDataStore is an interface for a data store that holds feature flags and related data in a serialized form.

This interface should be used for database integrations, or any other data store implementation that stores data in some external service. The SDK will provide its own caching layer on top of the persistent data store; the data store implementation should not provide caching, but simply do every query or update that the SDK tells it to do.

Implementations must be safe for concurrent access from multiple goroutines.

Error handling is defined as follows: if any data store operation encounters a database error, or is otherwise unable to complete its task, it should return an error value to make the SDK aware of The SDK will log the exception and will assume that the data store is now in a non-operational non-operational state; the SDK will then start polling IsStoreAvailable() to determine when the store has started working again.

Whenever a new implementation of this interface is written, it should have unit tests that use testhelpers.storetest.PersistentDataStoreTestSuite. This ensures that all of the interface methods are exercised consistently in various scenarios that might be encountered in real SDK usage.

Directories

Path Synopsis
Package ldstoreimpl contains SDK data store implementation objects that may be used by external code such as custom data store integrations and internal LaunchDarkly components.
Package ldstoreimpl contains SDK data store implementation objects that may be used by external code such as custom data store integrations and internal LaunchDarkly components.
Package ldstoretypes contains types that are only needed when implementing custom components.
Package ldstoretypes contains types that are only needed when implementing custom components.

Jump to

Keyboard shortcuts

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