package interfaces import ( "fmt" "time" ) // DataSourceStatusProvider is an interface for querying the status of a DataSource. The data source is the // component that receives updates to feature flag data; normally this is a streaming connection, but it // could be polling or file data depending on your configuration. // // An implementation of this interface is returned by // [github.com/launchdarkly/go-server-sdk/v6.LDClient.GetDataSourceStatusProvider()]. // Application code should not implement this interface. // // There are three ways to interact with the data source status. One is to simply get the current status; // if its State property is DataSourceStateValid, then the SDK is able to receive feature flag updates. // // status := client.GetDataSourceStatusProvider().GetStatus() // isValid = status.State == interfaces.DataSourceStateValid // // Second, you can use AddStatusListener to get a channel that provides a status update whenever the // connection has an error or starts working again. // // statusCh := client.GetDataSourceStatusProvider().AddStatusListener() // go func() { // for newStatus := range statusCh { // log.Printf("data source status is now: %+v", newStatus) // } // }() // // Third, you can use WaitFor to block until the data source has the desired status. For instance, if you // did not want to wait for a connection when you originally created the client, you could set the // timeout to zero so that the connection happens in the background. Then, when you need to do something // that requires a valid connection (possibly on another goroutine), you can wait until it is valid. // // client, _ := ld.MakeCustomClient(sdkKey, config, 0) // // // later... // inited := client.GetDataSourceStatusProvider().WaitFor(interfaces.DataSourceStateValid, 10 * time.Second) // if !inited { // // do whatever is appropriate if initialization has timed out // } type DataSourceStatusProvider interface { // GetStatus returns the current status of the data source. // // All of the built-in data source implementations are guaranteed to update this status whenever they // successfully initialize, encounter an error, or recover after an error. GetStatus() DataSourceStatus // AddStatusListener subscribes for notifications of status changes. The returned channel will receive a // new DataSourceStatus value for any change in status. // // The listener will be notified whenever any property of the status has changed. See DataSourceStatus for // an explanation of the meaning of each property and what could cause it to change. // // It is the caller's responsibility to consume values from the channel. Allowing values to accumulate in // the channel can cause an SDK goroutine to be blocked. If you no longer need the channel, call // RemoveStatusListener. AddStatusListener() <-chan DataSourceStatus // RemoveStatusListener unsubscribes from notifications of status changes. The specified channel must be // one that was previously returned by AddStatusListener(); otherwise, the method has no effect. RemoveStatusListener(listener <-chan DataSourceStatus) // WaitFor is a synchronous method for waiting for a desired connection state. // // If the current state is already desiredState when this method is called, it immediately returns. // Otherwise, it blocks until 1. the state has become desiredState, 2. the state has become // DataSourceStateOff (since that is a permanent condition), or 3. the specified timeout elapses. // // A scenario in which this might be useful is if you want to create the LDClient without waiting // for it to initialize, and then wait for initialization at a later time or on a different goroutine: // // // create the client but do not wait // client = ld.MakeCustomClient(sdkKey, config, 0) // // // later, possibly on another goroutine: // inited := client.GetDataSourceStatusProvider().WaitFor(DataSourceStateValid, 10 * time.Second) // if !inited { // // do whatever is appropriate if initialization has timed out // } WaitFor(desiredState DataSourceState, timeout time.Duration) bool } // DataSourceStatus is information about the data source's status and the last status change. // // See [DataSourceStatusProvider]. type DataSourceStatus struct { // State represents the overall current state of the data source. It will always be one of the // DataSourceState constants such as DataSourceStateValid. State DataSourceState // StateSince is the date/time that the value of State most recently changed. // // The meaning of this depends on the current State: // - For DataSourceStateInitializing, it is the time that the SDK started initializing. // - For DataSourceStateValid, it is the time that the data source most recently entered a valid // state, after previously having been either Initializing or Interrupted. // - For DataSourceStateInterrupted, it is the time that the data source most recently entered an // error state, after previously having been Valid. // - For DataSourceStateOff, it is the time that the data source encountered an unrecoverable error // or that the SDK was explicitly shut down. StateSince time.Time // LastError is information about the last error that the data source encountered, if any. // // This property should be updated whenever the data source encounters a problem, even if it does // not cause State to change. For instance, if a stream connection fails and the // state changes to DataSourceStateInterrupted, and then subsequent attempts to restart the // connection also fail, the state will remain Interrupted but the error information // will be updated each time-- and the last error will still be reported in this property even if // the state later becomes Valid. // // If no error has ever occurred, this field will be an empty DataSourceErrorInfo{}. LastError DataSourceErrorInfo } // String returns a simple string representation of the status. func (e DataSourceStatus) String() string { return fmt.Sprintf("Status(%s,%s,%s)", e.State, e.StateSince.Format(time.RFC3339), e.LastError) } // DataSourceState is any of the allowable values for [DataSourceStatus].State. // // See [DataSourceStatusProvider]. type DataSourceState string const ( // DataSourceStateInitializing is the initial state of the data source when the SDK is being // initialized. // // If it encounters an error that requires it to retry initialization, the state will remain at // Initializing until it either succeeds and becomes DataSourceStateValid, or permanently fails and // becomes DataSourceStateOff. DataSourceStateInitializing DataSourceState = "INITIALIZING" // DataSourceStateValid indicates that the data source is currently operational and has not had // any problems since the last time it received data. // // In streaming mode, this means that there is currently an open stream connection and that at least // one initial message has been received on the stream. In polling mode, it means that the last poll // request succeeded. DataSourceStateValid DataSourceState = "VALID" // DataSourceStateInterrupted indicates that the data source encountered an error that it will // attempt to recover from. // // In streaming mode, this means that the stream connection failed, or had to be dropped due to some // other error, and will be retried after a backoff delay. In polling mode, it means that the last poll // request failed, and a new poll request will be made after the configured polling interval. DataSourceStateInterrupted DataSourceState = "INTERRUPTED" // DataSourceStateOff indicates that the data source has been permanently shut down. // // This could be because it encountered an unrecoverable error (for instance, the LaunchDarkly service // rejected the SDK key; an invalid SDK key will never become valid), or because the SDK client was // explicitly shut down. DataSourceStateOff DataSourceState = "OFF" ) // DataSourceErrorInfo is a description of an error condition that the data source encountered. // // See [DataSourceStatusProvider]. type DataSourceErrorInfo struct { // Kind is the general category of the error. It will always be one of the DataSourceErrorKind // constants such as DataSourceErrorKindNetworkError, or "" if there have not been any errors. Kind DataSourceErrorKind // StatusCode is the HTTP status code if the error was DataSourceErrorKindErrorResponse, or zero // otherwise. StatusCode int // Message is any any additional human-readable information relevant to the error. The format of // this message is subject to change and should not be relied on programmatically. Message string // Time is the date/time that the error occurred. Time time.Time } // String returns a simple string representation of the error. func (e DataSourceErrorInfo) String() string { ret := string(e.Kind) if e.StatusCode > 0 || e.Message != "" { ret += "(" if e.StatusCode > 0 { ret += fmt.Sprintf("%d", e.StatusCode) } if e.Message != "" { if e.StatusCode > 0 { ret += "," } ret += e.Message } ret += ")" } if !e.Time.IsZero() { ret += fmt.Sprintf("@%s", e.Time.Format(time.RFC3339)) } return ret } // DataSourceErrorKind is any of the allowable values for [DataSourceErrorInfo].Kind. // // See [DataSourceStatusProvider]. type DataSourceErrorKind string const ( // DataSourceErrorKindUnknown indicates an unexpected error, such as an uncaught exception, // further described by DataSourceErrorInfo.Message. DataSourceErrorKindUnknown DataSourceErrorKind = "UNKNOWN" // DataSourceErrorKindNetworkError represents an I/O error such as a dropped connection. DataSourceErrorKindNetworkError DataSourceErrorKind = "NETWORK_ERROR" // DataSourceErrorKindErrorResponse means the LaunchDarkly service returned an HTTP response // with an error status, available in DataSourceErrorInfo.StatusCode. DataSourceErrorKindErrorResponse DataSourceErrorKind = "ERROR_RESPONSE" // DataSourceErrorKindInvalidData means the SDK received malformed data from the LaunchDarkly // service. DataSourceErrorKindInvalidData DataSourceErrorKind = "INVALID_DATA" // DataSourceErrorKindStoreError means the data source itself is working, but when it tried // to put an update into the data store, the data store failed (so the SDK may not have the // latest data). // // Data source implementations do not need to report this kind of error; it will be // automatically reported by the SDK whenever one of the update methods of DataSourceUpdateSink // encounters a failure. DataSourceErrorKindStoreError DataSourceErrorKind = "STORE_ERROR" )