components-contrib/state
Wang Bing db99b43068
Use revive instead of golint (#1685)
Signed-off-by: pigletfly <wangbing.adam@gmail.com>

Co-authored-by: Yaron Schneider <schneider.yaron@live.com>
2022-05-06 12:55:17 -07:00
..
aerospike update license to Apache v2.0 (#1406) 2022-01-04 19:53:31 -08:00
alicloud/tablestore update license to Apache v2.0 (#1406) 2022-01-04 19:53:31 -08:00
aws/dynamodb Added TTL to state store binding for DynamoDB (#1512) 2022-03-04 08:18:33 -08:00
azure 💄💄💄 #PickyLinter 2022-04-08 11:45:39 -07:00
cassandra update license to Apache v2.0 (#1406) 2022-01-04 19:53:31 -08:00
cockroachdb StateStore API implementation for CockroachDB (#1559) 2022-03-22 08:24:14 -07:00
couchbase Use revive instead of golint (#1685) 2022-05-06 12:55:17 -07:00
gcp/firestore update license to Apache v2.0 (#1406) 2022-01-04 19:53:31 -08:00
hashicorp/consul update license to Apache v2.0 (#1406) 2022-01-04 19:53:31 -08:00
hazelcast update license to Apache v2.0 (#1406) 2022-01-04 19:53:31 -08:00
jetstream Jetstream KV state store component (#1501) 2022-02-17 10:19:03 -08:00
memcached update license to Apache v2.0 (#1406) 2022-01-04 19:53:31 -08:00
mongodb fix conf tests. 2022-03-25 21:26:21 +05:30
mysql Fix ordering in transaction API for MySQL (#1523) 2022-03-14 16:39:32 -07:00
oci/objectstorage Add config file authentication to oci object storage state store (#1430) 2022-02-06 19:27:31 -08:00
oracledatabase Introducing Oracle Database backed state store component (#1454) 2022-02-08 12:00:34 -08:00
postgresql Use revive instead of golint (#1685) 2022-05-06 12:55:17 -07:00
query state store query API: remove preceding '.' in key names (#1500) 2022-02-11 15:00:43 -08:00
redis Use revive instead of golint (#1685) 2022-05-06 12:55:17 -07:00
rethinkdb update license to Apache v2.0 (#1406) 2022-01-04 19:53:31 -08:00
sqlserver Update transaction API to execute sequentially (#1542) 2022-03-09 11:37:28 -08:00
utils update license to Apache v2.0 (#1406) 2022-01-04 19:53:31 -08:00
zookeeper update license to Apache v2.0 (#1406) 2022-01-04 19:53:31 -08:00
Readme.md Remove Cloudstate component (#1303) 2021-11-18 15:30:40 -08:00
errors.go Change BulkDelete error handling in transaction 2022-02-21 15:40:58 +05:30
errors_test.go update license to Apache v2.0 (#1406) 2022-01-04 19:53:31 -08:00
feature.go update license to Apache v2.0 (#1406) 2022-01-04 19:53:31 -08:00
metadata.go update license to Apache v2.0 (#1406) 2022-01-04 19:53:31 -08:00
request_options.go update license to Apache v2.0 (#1406) 2022-01-04 19:53:31 -08:00
request_options_test.go update license to Apache v2.0 (#1406) 2022-01-04 19:53:31 -08:00
requests.go Added ContentType to pubsub/binding/state request-response (#1376) 2022-01-28 10:17:04 -08:00
responses.go Added ContentType to pubsub/binding/state request-response (#1376) 2022-01-28 10:17:04 -08:00
store.go update license to Apache v2.0 (#1406) 2022-01-04 19:53:31 -08:00
store_test.go update license to Apache v2.0 (#1406) 2022-01-04 19:53:31 -08:00
transactional_store.go update license to Apache v2.0 (#1406) 2022-01-04 19:53:31 -08:00

Readme.md

State Stores

State Stores provide a common way to interact with different data store implementations, and allow users to opt-in to advanced capabilities using defined metadata.

Currently supported state stores are:

  • Aerospike
  • Alibaba Cloud Tablestore
  • AWS DynamoDB
  • Azure Blob Storage
  • Azure CosmosDB
  • Azure Table Storage
  • Cassandra
  • Cloud Firestore (Datastore mode)
  • Couchbase
  • Etcd
  • HashiCorp Consul
  • Hazelcast
  • Memcached
  • MongoDB
  • MySQL
  • PostgreSQL
  • Redis
  • RethinkDB
  • SQL Server
  • Zookeeper

Implementing a new State Store

A compliant state store needs to implement one or more interfaces: Store and TransactionalStore.

The interface for Store:

type Store interface {
	Init(metadata Metadata) error
	Delete(req *DeleteRequest) error
	BulkDelete(req []DeleteRequest) error
	Get(req *GetRequest) (*GetResponse, error)
	Set(req *SetRequest) error
	BulkSet(req []SetRequest) error
}

The interface for TransactionalStore:

type TransactionalStore interface {
	Init(metadata Metadata) error
	Multi(reqs []TransactionalRequest) error
}

See the documentation site for examples.

Implementing State Query API

State Store has an optional API for querying the state.

Please refer to the documentation site for API description and definition.

// Querier is an interface to execute queries.
type Querier interface {
        Query(req *QueryRequest) (*QueryResponse, error)
}

Below are the definitions of structures (including nested) for QueryRequest and QueryResponse.

// QueryResponse is the request object for querying the state.
type QueryRequest struct {
        Query    query.Query       `json:"query"`
        Metadata map[string]string `json:"metadata,omitempty"`
}

type Query struct {
        Filters map[string]interface{} `json:"filter"`
        Sort    []Sorting              `json:"sort"`
        Page    Pagination             `json:"page"`

        // derived from Filters
        Filter Filter
}

type Sorting struct {
        Key   string `json:"key"`
        Order string `json:"order,omitempty"`
}

type Pagination struct {
        Limit int    `json:"limit"`
        Token string `json:"token,omitempty"`
}

// QueryResponse is the response object on querying state.
type QueryResponse struct {
        Results  []QueryItem       `json:"results"`
        Token    string            `json:"token,omitempty"`
        Metadata map[string]string `json:"metadata,omitempty"`
}

// QueryItem is an object representing a single entry in query results.
type QueryItem struct {
        Key   string  `json:"key"`
        Data  []byte  `json:"data"`
        ETag  *string `json:"etag,omitempty"`
        Error string  `json:"error,omitempty"`
}

Upon receiving the query request, Dapr validates it and transforms into object Query, which, in turn, is passed on to the state store component.

The Query object has a member Filter that implements parsing interface per component as described below.

type Filter interface {
	Parse(interface{}) error
}

type FilterEQ struct {
	Key string
	Val interface{}
}

type FilterIN struct {
	Key  string
	Vals []interface{}
}

type FilterAND struct {
	Filters []Filter
}

type FilterOR struct {
	Filters []Filter
}

To simplify the process of query translation, we leveraged visitor design pattern. A state store component developer would need to implement the visit method, and the runtime will use it to construct the native query statement.

type Visitor interface {
	// returns "equal" expression
	VisitEQ(*FilterEQ) (string, error)
	// returns "in" expression
	VisitIN(*FilterIN) (string, error)
	// returns "and" expression
	VisitAND(*FilterAND) (string, error)
	// returns "or" expression
	VisitOR(*FilterOR) (string, error)
	// receives concatenated filters and finalizes the native query
	Finalize(string, *MidQuery) error
}

The Dapr runtime implements QueryBuilder object that takes in Visitor interface and constructs the native query.

type QueryBuilder struct {
	visitor Visitor
}

func (h *QueryBuilder) BuildQuery(mq *MidQuery) error {...}

The last part is to implement Querier interface in the component:

type Querier interface {
	Query(req *QueryRequest) (*QueryResponse, error)
}

A sample implementation might look like that:

func (m *MyComponent) Query(req *state.QueryRequest) (*state.QueryResponse, error) {
	ctx, cancel := context.WithTimeout(context.Background(), timeout)
	defer cancel()

	query := &Query{} // Query implements Visitor interface
	qbuilder := state.NewQueryBuilder(query)
	if err := qbuilder.BuildQuery(&req.Query); err != nil {
		return &state.QueryResponse{}, err
	}
	data, token, err := query.execute(ctx)
	if err != nil {
		return &state.QueryResponse{}, err
	}
	return &state.QueryResponse{
		Results:  data,
		Token:    token,
	}, nil
}

Some of the examples of State Query API implementation are MongoDB and CosmosDB state store components.