From de42e45f01514c039b7b0adc5f2ff18377332fa2 Mon Sep 17 00:00:00 2001 From: halspang <70976921+halspang@users.noreply.github.com> Date: Mon, 15 Feb 2021 10:00:06 -0800 Subject: [PATCH] Update statestore ETag/actor support (#1213) * Update statestore ETag/actor support Add additional column to the supporte statestore table that indicates direct support of ETag usage. Also specify that transactional stores support actors. https://github.com/dapr/docs/issues/1199 * Add ETag example in statestore API reference This commit adds a walkthrough of how to work with ETags including finding them and using them for updates and deletes. * Update _index.md * Update state_api.md Co-authored-by: Mark Fussell Co-authored-by: Yaron Schneider --- .../building-blocks/actors/howto-actors.md | 8 +- .../supported-state-stores/_index.md | 52 +++++------ .../content/en/reference/api/state_api.md | 86 ++++++++++++++++++- 3 files changed, 111 insertions(+), 35 deletions(-) diff --git a/daprdocs/content/en/developing-applications/building-blocks/actors/howto-actors.md b/daprdocs/content/en/developing-applications/building-blocks/actors/howto-actors.md index 82fb6e97a..fe459cd01 100644 --- a/daprdocs/content/en/developing-applications/building-blocks/actors/howto-actors.md +++ b/daprdocs/content/en/developing-applications/building-blocks/actors/howto-actors.md @@ -26,13 +26,7 @@ Actors can save state reliably using state management capability. You can interact with Dapr through HTTP/gRPC endpoints for state management. -To use actors, your state store must support multi-item transactions. This means your state store [component](https://github.com/dapr/components-contrib/tree/master/state) must implement the [TransactionalStore](https://github.com/dapr/components-contrib/blob/master/state/transactional_store.go) interface. The following state stores implement this interface: - -- Redis -- MongoDB -- PostgreSQL -- SQL Server -- Azure CosmosDB +To use actors, your state store must support multi-item transactions. This means your state store [component](https://github.com/dapr/components-contrib/tree/master/state) must implement the [TransactionalStore](https://github.com/dapr/components-contrib/blob/master/state/transactional_store.go) interface. The list of components that support transactions/actors can be found here: [supported state stores]({{< ref supported-state-stores.md >}}). ## Actor timers and reminders diff --git a/daprdocs/content/en/operations/components/setup-state-store/supported-state-stores/_index.md b/daprdocs/content/en/operations/components/setup-state-store/supported-state-stores/_index.md index 5f16b7faf..7a033a514 100644 --- a/daprdocs/content/en/operations/components/setup-state-store/supported-state-stores/_index.md +++ b/daprdocs/content/en/operations/components/setup-state-store/supported-state-stores/_index.md @@ -9,32 +9,36 @@ no_list: true ### Generic -| Name | CRUD | Transactional | Status | -|----------------------------------------------------------------|------|---------------|--------| -| [Aerospike]({{< ref setup-aerospike.md >}}) | ✅ | ❌ | Alpha | -| [Apache Cassandra]({{< ref setup-cassandra.md >}}) | ✅ | ❌ | Alpha | -| [Cloudstate]({{< ref setup-cloudstate.md >}}) | ✅ | ❌ | Alpha | -| [Couchbase]({{< ref setup-couchbase.md >}}) | ✅ | ❌ | Alpha | -| [Hashicorp Consul]({{< ref setup-consul.md >}}) | ✅ | ❌ | Alpha | -| [Hazelcast]({{< ref setup-hazelcast.md >}}) | ✅ | ❌ | Alpha | -| [Memcached]({{< ref setup-memcached.md >}}) | ✅ | ❌ | Alpha | -| [MongoDB]({{< ref setup-mongodb.md >}}) | ✅ | ✅ | Alpha | -| [MySQL]({{< ref setup-mysql.md >}}) | ✅ | ✅ | Alpha | -| [PostgreSQL]({{< ref setup-postgresql.md >}}) | ✅ | ✅ | Alpha | -| [Redis]({{< ref setup-redis.md >}}) | ✅ | ✅ | Alpha | -| [Zookeeper]({{< ref setup-zookeeper.md >}}) | ✅ | ❌ | Alpha | +| Name | CRUD | Transactional
(Supports Actors) | ETag | Status | +|----------------------------------------------------------------|------|---------------------|------|--------| +| [Aerospike]({{< ref setup-aerospike.md >}}) | ✅ | ❌ | ✅ | Alpha | +| [Apache Cassandra]({{< ref setup-cassandra.md >}}) | ✅ | ❌ | ❌ | Alpha | +| [Cloudstate]({{< ref setup-cloudstate.md >}}) | ✅ | ❌ | ✅ | Alpha | +| [Couchbase]({{< ref setup-couchbase.md >}}) | ✅ | ❌ | ✅ | Alpha | +| [Hashicorp Consul]({{< ref setup-consul.md >}}) | ✅ | ❌ | ❌ | Alpha | +| [Hazelcast]({{< ref setup-hazelcast.md >}}) | ✅ | ❌ | ❌ | Alpha | +| [Memcached]({{< ref setup-memcached.md >}}) | ✅ | ❌ | ❌ | Alpha | +| [MongoDB]({{< ref setup-mongodb.md >}}) | ✅ | ✅ | ❌ | Alpha | +| [MySQL]({{< ref setup-mysql.md >}}) | ✅ | ✅ | ✅ | Alpha | +| [PostgreSQL]({{< ref setup-postgresql.md >}}) | ✅ | ✅ | ✅ | Alpha | +| [Redis]({{< ref setup-redis.md >}}) | ✅ | ✅ | ✅ | Alpha | +| RethinkDB | ✅ | ✅ | ✅ | Alpha | +| [Zookeeper]({{< ref setup-zookeeper.md >}}) | ✅ | ❌ | ✅ | Alpha | ### Google Cloud Platform (GCP) -| Name | CRUD | Transactional | Status | -|-------------------------------------------------------|------|---------------|--------| -| [GCP Firestore]({{< ref setup-firestore.md >}}) | ✅ | ❌ | Alpha | +| Name | CRUD | Transactional
(Supports Actors) | ETag | Status | +|-------------------------------------------------------|------|---------------------|------|--------| +| [GCP Firestore]({{< ref setup-firestore.md >}}) | ✅ | ❌ | ❌ | Alpha | ### Microsoft Azure -| Name | CRUD | Transactional | Status | -|------------------------------------------------------------------|------|---------------|--------| -| [Azure Blob Storage]({{< ref setup-azure-blobstorage.md >}}) | ✅ | ❌ | Alpha | -| [Azure CosmosDB]({{< ref setup-azure-cosmosdb.md >}}) | ✅ | ✅ | Alpha | -| [Azure SQL Server]({{< ref setup-sqlserver.md >}}) | ✅ | ❌ | Alpha | -| [Azure Table Storage]({{< ref setup-azure-tablestorage.md >}}) | ✅ | ❌ | Alpha | - +| Name | CRUD | Transactional
(Supports Actors) | ETag | Status | +|------------------------------------------------------------------|------|---------------------|------|--------| +| [Azure Blob Storage]({{< ref setup-azure-blobstorage.md >}}) | ✅ | ❌ | ✅ | Alpha | +| [Azure CosmosDB]({{< ref setup-azure-cosmosdb.md >}}) | ✅ | ✅ | ✅ | Alpha | +| [Azure SQL Server]({{< ref setup-sqlserver.md >}}) | ✅ | ✅ | ✅ | Alpha | +| [Azure Table Storage]({{< ref setup-azure-tablestorage.md >}}) | ✅ | ❌ | ✅ | Alpha | +### Amazon Web Services (AWS) +| Name | CRUD | Transactional
(Supports Actors) | ETag | Status | +|------------------------------------------------------------------|------|---------------------|------|--------| +| AWS DynamoDB | ✅ | ❌ | ❌ | Alpha | diff --git a/daprdocs/content/en/reference/api/state_api.md b/daprdocs/content/en/reference/api/state_api.md index 2ad2c0c9c..3a4a3d48d 100644 --- a/daprdocs/content/en/reference/api/state_api.md +++ b/daprdocs/content/en/reference/api/state_api.md @@ -103,7 +103,8 @@ curl -X POST http://localhost:3500/v1.0/state/starwars \ -d '[ { "key": "weapon", - "value": "DeathStar" + "value": "DeathStar", + "etag": "1234" }, { "key": "planet", @@ -288,7 +289,7 @@ None. ### Example ```shell -curl -X "DELETE" http://localhost:3500/v1.0/state/starwars/planet -H "ETag: xxxxxxx" +curl -X "DELETE" http://localhost:3500/v1.0/state/starwars/planet -H "If-Match: xxxxxxx" ``` ## State transactions @@ -377,6 +378,7 @@ curl -X POST http://localhost:3500/v1.0/state/starwars/transaction \ ## Configuring state store for actors Actors don't support multiple state stores and require a transactional state store to be used with Dapr. Currently Mongodb, Redis, PostgreSQL, SQL Server, and Azure CosmosDB implement the transactional state store interface. + To specify which state store to be used for actors, specify value of property `actorStateStore` as true in the metadata section of the state store component yaml file. Example: Following components yaml will configure redis to be used as the state store for Actors. @@ -434,9 +436,9 @@ When a strong consistency hint is attached, a state store should: * For read requests, the state store should return the most up-to-date data consistently across replicas. * For write/delete requests, the state store should synchronisely replicate updated data to configured quorum before completing the write request. -### Example +### Example - Complete options request example -The following is a sample *set* request with a complete operation option definition: +The following is an example *set* request with a complete options definition: ```shell curl -X POST http://localhost:3500/v1.0/state/starwars \ @@ -454,6 +456,82 @@ curl -X POST http://localhost:3500/v1.0/state/starwars \ ]' ``` +### Example - Working with ETags +The following is an example which walks through the usage of an ETag when setting/deleting an object in a compatible statestore. + +First, store an object in a statestore (this sample uses Redis that has been defined as 'statestore'): + +```shell +curl -X POST http://localhost:3500/v1.0/state/statestore \ + -H "Content-Type: application/json" \ + -d '[ + { + "key": "sampleData", + "value": "1" + } + ]' +``` + +Get the object to find the ETag that was set automatically by the statestore: + +```shell +curl http://localhost:3500/v1.0/state/statestore/sampleData -v +* Connected to localhost (127.0.0.1) port 3500 (#0) +> GET /v1.0/state/statestore/sampleData HTTP/1.1 +> Host: localhost:3500 +> User-Agent: curl/7.64.1 +> Accept: */* +> +< HTTP/1.1 200 OK +< Server: fasthttp +< Date: Sun, 14 Feb 2021 04:51:50 GMT +< Content-Type: application/json +< Content-Length: 3 +< Etag: 1 +< Traceparent: 00-3452582897d134dc9793a244025256b1-b58d8d773e4d661d-01 +< +* Connection #0 to host localhost left intact +"1"* Closing connection 0 +``` + +The returned ETag here was 1. Sending a new request to update or delete the data with the wrong ETag will return an error (omitting the ETag will allow the request): + +```shell +# Update +curl -X POST http://localhost:3500/v1.0/state/statestore \ + -H "Content-Type: application/json" \ + -d '[ + { + "key": "sampleData", + "value": "2", + "etag": "2" + } + ]' +{"errorCode":"ERR_STATE_SAVE","message":"failed saving state in state store statestore: possible etag mismatch. error from state store: ERR Error running script (call to f_83e03ec05d6a3b6fb48483accf5e594597b6058f): @user_script:1: user_script:1: failed to set key nodeapp||sampleData"} + +# Delete +curl -X DELETE -H 'If-Match: 5' http://localhost:3500/v1.0/state/statestore/sampleData +{"errorCode":"ERR_STATE_DELETE","message":"failed deleting state with key sampleData: possible etag mismatch. error from state store: ERR Error running script (call to f_9b5da7354cb61e2ca9faff50f6c43b81c73c0b94): @user_script:1: user_script:1: failed to delete node +app||sampleData"} +``` + +In order to update or delete the object, simply match the ETag in either the request body (update) or the `If-Match` header (delete). Note, when the state is updated, it receives a new ETag so further updates or deletes will need to use the new ETag. + +```shell +# Update +curl -X POST http://localhost:3500/v1.0/state/statestore \ + -H "Content-Type: application/json" \ + -d '[ + { + "key": "sampleData", + "value": "2", + "etag": "1" + } + ]' + +# Delete +curl -X DELETE -H 'If-Match: 1' http://localhost:3500/v1.0/state/statestore/sampleData +``` ## Next Steps - [State management overview]({{< ref state-management-overview.md >}}) - [How-To: Save & get state]({{< ref howto-get-save-state.md >}})