State Store

Creating a state store component requires just a few basic steps.

Import state store packages

Create the file components/statestore.go and add import statements for the state store related packages.

  1. package components
  2. import (
  3. "context"
  4. "github.com/dapr/components-contrib/state"
  5. )

Implement the Store interface

Create a type that implements the Store interface.

  1. type MyStateStore struct {
  2. }
  3. func (store *MyStateStore) Init(metadata state.Metadata) error {
  4. // Called to initialize the component with its configured metadata...
  5. }
  6. func (store *MyStateStore) GetComponentMetadata() map[string]string {
  7. // Not used with pluggable components...
  8. return map[string]string{}
  9. }
  10. func (store *MyStateStore) Features() []state.Feature {
  11. // Return a list of features supported by the state store...
  12. }
  13. func (store *MyStateStore) Delete(ctx context.Context, req *state.DeleteRequest) error {
  14. // Delete the requested key from the state store...
  15. }
  16. func (store *MyStateStore) Get(ctx context.Context, req *state.GetRequest) (*state.GetResponse, error) {
  17. // Get the requested key value from the state store, else return an empty response...
  18. }
  19. func (store *MyStateStore) Set(ctx context.Context, req *state.SetRequest) error {
  20. // Set the requested key to the specified value in the state store...
  21. }
  22. func (store *MyStateStore) BulkGet(ctx context.Context, req []state.GetRequest) (bool, []state.BulkGetResponse, error) {
  23. // Get the requested key values from the state store...
  24. }
  25. func (store *MyStateStore) BulkDelete(ctx context.Context, req []state.DeleteRequest) error {
  26. // Delete the requested keys from the state store...
  27. }
  28. func (store *MyStateStore) BulkSet(ctx context.Context, req []state.SetRequest) error {
  29. // Set the requested keys to their specified values in the state store...
  30. }

Register state store component

In the main application file (for example, main.go), register the state store with an application service.

  1. package main
  2. import (
  3. "example/components"
  4. dapr "github.com/dapr-sandbox/components-go-sdk"
  5. "github.com/dapr-sandbox/components-go-sdk/state/v1"
  6. )
  7. func main() {
  8. dapr.Register("<socket name>", dapr.WithStateStore(func() state.Store {
  9. return &components.MyStateStoreComponent{}
  10. }))
  11. dapr.MustRun()
  12. }

Bulk state stores

While state stores are required to support the bulk operations, their implementations sequentially delegate to the individual operation methods.

Transactional state stores

State stores that intend to support transactions should implement the optional TransactionalStore interface. Its Multi() method receives a request with a sequence of delete and/or set operations to be performed within a transaction. The state store should iterate over the sequence and apply each operation.

  1. func (store *MyStateStoreComponent) Multi(ctx context.Context, request *state.TransactionalStateRequest) error {
  2. // Start transaction...
  3. for _, operation := range request.Operations {
  4. switch operation.Operation {
  5. case state.Delete:
  6. deleteRequest := operation.Request.(state.DeleteRequest)
  7. // Process delete request...
  8. case state.Upsert:
  9. setRequest := operation.Request.(state.SetRequest)
  10. // Process set request...
  11. }
  12. }
  13. // End (or rollback) transaction...
  14. return nil
  15. }

Queryable state stores

State stores that intend to support queries should implement the optional Querier interface. Its Query() method is passed details about the query, such as the filter(s), result limits, pagination, and sort order(s) of the results. The state store uses those details to generate a set of values to return as part of its response.

  1. func (store *MyStateStoreComponent) Query(ctx context.Context, req *state.QueryRequest) (*state.QueryResponse, error) {
  2. // Generate and return results...
  3. }

ETag and other semantic error handling

The Dapr runtime has additional handling of certain error conditions resulting from some state store operations. State stores can indicate such conditions by returning specific errors from its operation logic:

ErrorApplicable OperationsDescription
NewETagError(state.ETagInvalid, …)Delete, Set, Bulk Delete, Bulk SetWhen an ETag is invalid
NewETagError(state.ETagMismatch, …)Delete, Set, Bulk Delete, Bulk SetWhen an ETag does not match an expected value
NewBulkDeleteRowMismatchError(…)Bulk DeleteWhen the number of affected rows does not match the expected rows

Next steps