From 49cb7f64ce182bbd2ef8057c205acb79f6ed7e9a Mon Sep 17 00:00:00 2001 From: tanvigour <> Date: Thu, 18 Nov 2021 16:49:56 +0530 Subject: [PATCH 01/20] add rel attribute to remove association with previous page --- daprdocs/layouts/partials/navbar.html | 2 +- daprdocs/layouts/partials/page-meta-links.html | 4 ++-- daprdocs/layouts/partials/version-banner.html | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/daprdocs/layouts/partials/navbar.html b/daprdocs/layouts/partials/navbar.html index e1dc682d4..5a2bc8fd6 100644 --- a/daprdocs/layouts/partials/navbar.html +++ b/daprdocs/layouts/partials/navbar.html @@ -14,7 +14,7 @@ {{ end }} {{ $url := urls.Parse .URL }} {{ $baseurl := urls.Parse $.Site.Params.Baseurl }} - {{ .Name }} + {{ .Name }} {{ end }} {{ if .Site.Params.versions }} diff --git a/daprdocs/layouts/partials/page-meta-links.html b/daprdocs/layouts/partials/page-meta-links.html index bd55efdf3..6bf0d95a1 100644 --- a/daprdocs/layouts/partials/page-meta-links.html +++ b/daprdocs/layouts/partials/page-meta-links.html @@ -21,8 +21,8 @@ {{ $newPageQS := querify "value" $newPageStub.Content "filename" "change-me.md" | safeURL }} {{ $newPageURL := printf "%s/new/%s?%s" $gh_repo $gh_repo_path $newPageQS }} - {{ T "post_edit_this" }} - {{ T "post_create_issue" }} + {{ T "post_edit_this" }} + {{ T "post_create_issue" }} {{ end }} {{ end }} diff --git a/daprdocs/layouts/partials/version-banner.html b/daprdocs/layouts/partials/version-banner.html index 0436da130..09b705b07 100644 --- a/daprdocs/layouts/partials/version-banner.html +++ b/daprdocs/layouts/partials/version-banner.html @@ -8,7 +8,7 @@ {{ with $current_version }}

The documentation you are viewing is for Dapr {{ . | markdownify }} which is an older version of Dapr. {{ with $latest_version }}For up-to-date documentation, see the - latest version.

+ latest version.

{{ end }} {{ end }} From 8901c9d6fe0d6559be1b0657ca751d8487f92fee Mon Sep 17 00:00:00 2001 From: Amulya Varote Date: Tue, 23 Nov 2021 02:52:27 -0800 Subject: [PATCH 02/20] State Management documentation --- .../howto-invoke-discover-services.md | 12 +- .../state-management/howto-get-save-state.md | 920 ++++++++++-------- 2 files changed, 547 insertions(+), 385 deletions(-) diff --git a/daprdocs/content/en/developing-applications/building-blocks/service-invocation/howto-invoke-discover-services.md b/daprdocs/content/en/developing-applications/building-blocks/service-invocation/howto-invoke-discover-services.md index 6e99d4a04..185d718ab 100644 --- a/daprdocs/content/en/developing-applications/building-blocks/service-invocation/howto-invoke-discover-services.md +++ b/daprdocs/content/en/developing-applications/building-blocks/service-invocation/howto-invoke-discover-services.md @@ -184,7 +184,7 @@ Below are code examples that leverage Dapr SDKs for service invocation. {{% codetab %}} ```csharp -//headers +//dependencies using Dapr.Client; using System.Net.Http; @@ -204,7 +204,7 @@ await client.InvokeMethodAsync(result); {{% codetab %}} ```java -//headers +//dependencies import io.dapr.client.DaprClient; import io.dapr.client.DaprClientBuilder; @@ -227,11 +227,11 @@ var result = daprClient.invokeMethod( {{% codetab %}} ```python -//headers +#dependencies from dapr.clients import DaprClient -//code +#code with DaprClient() as daprClient: result = daprClient.invoke_method( @@ -247,7 +247,7 @@ with DaprClient() as daprClient: {{% codetab %}} ```go -//headers +//dependencies import ( dapr "github.com/dapr/go-sdk/client" ) @@ -269,7 +269,7 @@ result, err := client.InvokeMethod(ctx, "checkout", "checkout/" + strconv.Itoa(o {{% codetab %}} ```javascript -//headers +//dependencies import { DaprClient, HttpMethod, CommunicationProtocolEnum } from 'dapr-client'; diff --git a/daprdocs/content/en/developing-applications/building-blocks/state-management/howto-get-save-state.md b/daprdocs/content/en/developing-applications/building-blocks/state-management/howto-get-save-state.md index d00c1ea61..e3a678037 100644 --- a/daprdocs/content/en/developing-applications/building-blocks/state-management/howto-get-save-state.md +++ b/daprdocs/content/en/developing-applications/building-blocks/state-management/howto-get-save-state.md @@ -19,6 +19,12 @@ In this guide we'll start of with the basics: Using the key/value state API to a - [Dapr CLI]({{< ref install-dapr-cli.md >}}) - Initialized [Dapr environment]({{< ref install-dapr-selfhost.md >}}) +## Example: + +The below code examples loosely describe an application that processes orders. In the examples, there is one service - an order processing service. This service has Dapr sidecar and the order processing service uses Dapr to store state in redis state store. + +Diagram showing state management of example service + ## Step 1: Setup a state store A state store component represents a resource that Dapr uses to communicate with a database. @@ -66,130 +72,210 @@ The following example shows how to a single key/value pair using the Dapr state It is important to set an app-id, as the state keys are prefixed with this value. If you don't set it one is generated for you at runtime, and the next time you run the command a new one will be generated and you will no longer be able to access previously saved state. {{% /alert %}} -{{< tabs "HTTP API (Bash)" "HTTP API (PowerShell)" "Python SDK" "PHP SDK">}} +Below are code examples that leverage Dapr SDKs for saving and retrieving a single state. -{{% codetab %}} -Begin by launching a Dapr sidecar: - -```bash -dapr run --app-id myapp --dapr-http-port 3500 -``` - -Then in a separate terminal save a key/value pair into your statestore: -```bash -curl -X POST -H "Content-Type: application/json" -d '[{ "key": "key1", "value": "value1"}]' http://localhost:3500/v1.0/state/statestore -``` - -Now get the state you just saved: -```bash -curl http://localhost:3500/v1.0/state/statestore/key1 -``` - -You can also restart your sidecar and try retrieving state again to see that state persists separate from the app. -{{% /codetab %}} +{{< tabs Dotnet Java Python Go Javascript "HTTP API (Bash)" "HTTP API (PowerShell)">}} {{% codetab %}} -Begin by launching a Dapr sidecar: +```csharp +//dependencies + +using Dapr.Client; + +//code + +string DAPR_STORE_NAME = "statestore"; + +using var client = new DaprClientBuilder().Build(); +await client.SaveStateAsync(DAPR_STORE_NAME, "order_1", orderId.ToString()); +var result = await client.GetStateAsync(DAPR_STORE_NAME, orderId.ToString()); +Console.WriteLine("Result after get: " + result); + +``` + +Once saved run the following command to launch a Dapr sidecar and run the application: ```bash -dapr --app-id myapp --port 3500 run +dapr run --app-id orderprocessing --app-port 6001 --dapr-http-port 3601 --dapr-grpc-port 60001 dotnet run ``` -Then in a separate terminal save a key/value pair into your statestore: -```powershell -Invoke-RestMethod -Method Post -ContentType 'application/json' -Body '[{"key": "key1", "value": "value1"}]' -Uri 'http://localhost:3500/v1.0/state/statestore' -``` - -Now get the state you just saved: -```powershell -Invoke-RestMethod -Uri 'http://localhost:3500/v1.0/state/statestore/key1' -``` - -You can also restart your sidecar and try retrieving state again to see that state persists separate from the app. - {{% /codetab %}} + + {{% codetab %}} -Save the following to a file named `pythonState.py`: +```java +//dependencies + +import io.dapr.client.DaprClient; +import io.dapr.client.DaprClientBuilder; + +//code + +private static final String STATE_STORE_NAME = "statestore"; + +DaprClient client = new DaprClientBuilder().build(); +client.saveState(STATE_STORE_NAME, "order_1", Integer.toString(orderId)).block(); +Mono> result = client.getState(STATE_STORE_NAME, "order_1", String.class); +log.info("Result after get" + result); + +``` + +Once saved run the following command to launch a Dapr sidecar and run the application: + +```bash +dapr run --app-id orderprocessing --app-port 6001 --dapr-http-port 3601 --dapr-grpc-port 60001 mvn spring-boot:run +``` + +{{% /codetab %}} + + +{{% codetab %}} ```python +#dependencies + from dapr.clients import DaprClient -with DaprClient() as d: - d.save_state(store_name="statestore", key="myFirstKey", value="myFirstValue" ) - print("State has been stored") +#code - data = d.get_state(store_name="statestore", key="myFirstKey").data - print(f"Got value: {data}") +DAPR_STORE_NAME = "statestore" + +with DaprClient() as client: + client.save_state(DAPR_STORE_NAME, "order_1", str(orderId)) + result = client.get_state(DAPR_STORE_NAME, "order_1") + logging.info('Result after get: ' + str(result)) ``` -Once saved run the following command to launch a Dapr sidecar and run the Python application: +Once saved run the following command to launch a Dapr sidecar and run the application: ```bash -dapr --app-id myapp run python pythonState.py -``` - -You should get an output similar to the following, which will show both the Dapr and app logs: - -```md -== DAPR == time="2021-01-06T21:34:33.7970377-08:00" level=info msg="starting Dapr Runtime -- version 0.11.3 -- commit a1a8e11" app_id=Braidbald-Boot scope=dapr.runtime type=log ver=0.11.3 -== DAPR == time="2021-01-06T21:34:33.8040378-08:00" level=info msg="standalone mode configured" app_id=Braidbald-Boot scope=dapr.runtime type=log ver=0.11.3 -== DAPR == time="2021-01-06T21:34:33.8040378-08:00" level=info msg="app id: Braidbald-Boot" app_id=Braidbald-Boot scope=dapr.runtime type=log ver=0.11.3 -== DAPR == time="2021-01-06T21:34:33.9750400-08:00" level=info msg="component loaded. name: statestore, type: state.redis" app_id=Braidbald-Boot scope=dapr.runtime type=log ver=0.11.3 -== DAPR == time="2021-01-06T21:34:33.9760387-08:00" level=info msg="API gRPC server is running on port 51656" app_id=Braidbald-Boot scope=dapr.runtime type=log ver=0.11.3 -== DAPR == time="2021-01-06T21:34:33.9770372-08:00" level=info msg="dapr initialized. Status: Running. Init Elapsed 172.9994ms" app_id=Braidbald-Boot scope=dapr. - -Checking if Dapr sidecar is listening on GRPC port 51656 -Dapr sidecar is up and running. -Updating metadata for app command: python pythonState.py -You are up and running! Both Dapr and your app logs will appear here. - -== APP == State has been stored -== APP == Got value: b'myFirstValue' +dapr run --app-id orderprocessing --app-port 6001 --dapr-http-port 3601 --dapr-grpc-port 60001 -- python3 OrderProcessingService.py ``` {{% /codetab %}} + {{% codetab %}} -Save the following in `state-example.php`: +```go +//dependencies -```php -run(function(\Dapr\State\StateManager $stateManager, \Psr\Log\LoggerInterface $logger) { - $stateManager->save_state(store_name: 'statestore', item: new \Dapr\State\StateItem( - key: 'myFirstKey', - value: 'myFirstValue' - )); - $logger->alert('State has been stored'); - $data = $stateManager->load_state(store_name: 'statestore', key: 'myFirstKey')->value; - $logger->alert("Got value: {data}", ['data' => $data]); -}); ``` -Once saved run the following command to launch a Dapr sidecar and run the PHP application: +Once saved run the following command to launch a Dapr sidecar and run the application: ```bash -dapr --app-id myapp run -- php state-example.php +dapr run --app-id orderprocessing --app-port 6001 --dapr-http-port 3601 --dapr-grpc-port 60001 go run OrderProcessingService.go ``` -You should get an output similar to the following, which will show both the Dapr and app logs: +{{% /codetab %}} -```md -✅ You're up and running! Both Dapr and your app logs will appear here. -== APP == [2021-02-12T16:30:11.078777+01:00] APP.ALERT: State has been stored [] [] +{{% codetab %}} + +```javascript +//dependencies + +import { DaprClient, HttpMethod, CommunicationProtocolEnum } from 'dapr-client'; + +//code + +const STATE_STORE_NAME = "statestore"; + +const daprHost = "127.0.0.1"; +const client = new DaprClient(daprHost, process.env.DAPR_HTTP_PORT, CommunicationProtocolEnum.HTTP); +await client.state.save(STATE_STORE_NAME, [ +{ + key: "order_1", + value: orderId.toString() +}]); +var result = await client.state.get(STATE_STORE_NAME, "order_1"); +console.log("Result after get: " + result); + -== APP == [2021-02-12T16:30:11.082620+01:00] APP.ALERT: Got value: myFirstValue {"data":"myFirstValue"} [] ``` +Once saved run the following command to launch a Dapr sidecar and run the application: + +```bash +dapr run --app-id orderprocessing --app-port 6001 --dapr-http-port 3601 --dapr-grpc-port 60001 npm start +``` + +{{% /codetab %}} + + +{{% codetab %}} +Begin by launching a Dapr sidecar: + +```bash +dapr run --app-id orderprocessing --dapr-http-port 3601 +``` + +Then in a separate terminal save a key/value pair into your statestore: +```bash +curl -X POST -H "Content-Type: application/json" -d '[{ "key": "order_1", "value": "250"}]' http://localhost:3601/v1.0/state/statestore +``` + +Now get the state you just saved: +```bash +curl http://localhost:3601/v1.0/state/statestore/order_1 +``` + +You can also restart your sidecar and try retrieving state again to see that state persists separate from the app. +{{% /codetab %}} + +{{% codetab %}} + +Begin by launching a Dapr sidecar: + +```bash +dapr --app-id orderprocessing --port 3601 run +``` + +Then in a separate terminal save a key/value pair into your statestore: +```powershell +Invoke-RestMethod -Method Post -ContentType 'application/json' -Body '[{"key": "order_1", "value": "250"}]' -Uri 'http://localhost:3601/v1.0/state/statestore' +``` + +Now get the state you just saved: +```powershell +Invoke-RestMethod -Uri 'http://localhost:3601/v1.0/state/statestore/order_1' +``` + +You can also restart your sidecar and try retrieving state again to see that state persists separate from the app. + {{% /codetab %}} {{< /tabs >}} @@ -197,14 +283,150 @@ You should get an output similar to the following, which will show both the Dapr ## Step 3: Delete state -The following example shows how to delete an item by using a key with the state management API: +Below are code examples that leverage Dapr SDKs for deleting the state. -{{< tabs "HTTP API (Bash)" "HTTP API (PowerShell)" "Python SDK" "PHP SDK">}} +{{< tabs Dotnet Java Python Go Javascript "HTTP API (Bash)" "HTTP API (PowerShell)">}} + +{{% codetab %}} + +```csharp +//dependencies + +using Dapr.Client; + +//code + +string DAPR_STORE_NAME = "statestore"; + +await client.DeleteStateAsync(DAPR_STORE_NAME, "order_1", cancellationToken: cancellationToken); + +``` + +Once saved run the following command to launch a Dapr sidecar and run the application: + +```bash +dapr run --app-id orderprocessing --app-port 6001 --dapr-http-port 3601 --dapr-grpc-port 60001 dotnet run +``` + +{{% /codetab %}} + + +{{% codetab %}} + +```java +//dependencies + +import io.dapr.client.DaprClient; +import io.dapr.client.DaprClientBuilder; + +//code + +private static final String STATE_STORE_NAME = "statestore"; + +DaprClient client = new DaprClientBuilder().build(); +String storedEtag = client.getState(STATE_STORE_NAME, "order_1", String.class).block().getEtag(); +client.deleteState(STATE_STORE_NAME, "order_1", storedEtag, null).block(); + +``` + +Once saved run the following command to launch a Dapr sidecar and run the application: + +```bash +dapr run --app-id orderprocessing --app-port 6001 --dapr-http-port 3601 --dapr-grpc-port 60001 mvn spring-boot:run +``` + +{{% /codetab %}} + + +{{% codetab %}} + +```python +#dependencies + +from dapr.clients import DaprClient + +#code + +DAPR_STORE_NAME = "statestore" + +with DaprClient() as client: + client.delete_state(store_name=DAPR_STORE_NAME, key="order_1") + +``` + +Once saved run the following command to launch a Dapr sidecar and run the application: + +```bash +dapr run --app-id orderprocessing --app-port 6001 --dapr-http-port 3601 --dapr-grpc-port 60001 -- python3 OrderProcessingService.py +``` + +{{% /codetab %}} + + +{{% codetab %}} + +```go +//dependencies + +import ( + dapr "github.com/dapr/go-sdk/client" +) + +//code + +client, err := dapr.NewClient() +STATE_STORE_NAME := "statestore" + +if err != nil { + panic(err) +} +defer client.Close() +ctx := context.Background() + +if err := client.DeleteState(ctx, store, "order_1"); err != nil { + panic(err) +} + +``` + +Once saved run the following command to launch a Dapr sidecar and run the application: + +```bash +dapr run --app-id orderprocessing --app-port 6001 --dapr-http-port 3601 --dapr-grpc-port 60001 go run OrderProcessingService.go +``` + +{{% /codetab %}} + + +{{% codetab %}} + +```javascript +//dependencies + +import { DaprClient, HttpMethod, CommunicationProtocolEnum } from 'dapr-client'; + +//code + +const STATE_STORE_NAME = "statestore"; + +const client = new DaprClient(daprHost, process.env.DAPR_HTTP_PORT, CommunicationProtocolEnum.HTTP); +await client.state.delete(STATE_STORE_NAME, "order_1"); + + +``` + +Once saved run the following command to launch a Dapr sidecar and run the application: + +```bash +dapr run --app-id orderprocessing --app-port 6001 --dapr-http-port 3601 --dapr-grpc-port 60001 npm start +``` + +{{% /codetab %}} {{% codetab %}} With the same dapr instance running from above run: ```bash -curl -X DELETE 'http://localhost:3500/v1.0/state/statestore/key1' +curl -X DELETE 'http://localhost:3601/v1.0/state/statestore/order_1' ``` Try getting state again and note that no value is returned. {{% /codetab %}} @@ -212,228 +434,134 @@ Try getting state again and note that no value is returned. {{% codetab %}} With the same dapr instance running from above run: ```powershell -Invoke-RestMethod -Method Delete -Uri 'http://localhost:3500/v1.0/state/statestore/key1' +Invoke-RestMethod -Method Delete -Uri 'http://localhost:3601/v1.0/state/statestore/order_1' ``` Try getting state again and note that no value is returned. {{% /codetab %}} -{{% codetab %}} - -Update `pythonState.py` with: - -```python -from dapr.clients import DaprClient - -with DaprClient() as d: - d.save_state(store_name="statestore", key="key1", value="value1" ) - print("State has been stored") - - data = d.get_state(store_name="statestore", key="key1").data - print(f"Got value: {data}") - - d.delete_state(store_name="statestore", key="key1") - - data = d.get_state(store_name="statestore", key="key1").data - print(f"Got value after delete: {data}") -``` - -Now run your program with: - -```bash -dapr --app-id myapp run python pythonState.py -``` - -You should see an output similar to the following: - -```md -Starting Dapr with id Yakchocolate-Lord. HTTP Port: 59457. gRPC Port: 59458 - -== DAPR == time="2021-01-06T22:55:36.5570696-08:00" level=info msg="starting Dapr Runtime -- version 0.11.3 -- commit a1a8e11" app_id=Yakchocolate-Lord scope=dapr.runtime type=log ver=0.11.3 -== DAPR == time="2021-01-06T22:55:36.5690367-08:00" level=info msg="standalone mode configured" app_id=Yakchocolate-Lord scope=dapr.runtime type=log ver=0.11.3 -== DAPR == time="2021-01-06T22:55:36.7220140-08:00" level=info msg="component loaded. name: statestore, type: state.redis" app_id=Yakchocolate-Lord scope=dapr.runtime type=log ver=0.11.3 -== DAPR == time="2021-01-06T22:55:36.7230148-08:00" level=info msg="API gRPC server is running on port 59458" app_id=Yakchocolate-Lord scope=dapr.runtime type=log ver=0.11.3 -== DAPR == time="2021-01-06T22:55:36.7240207-08:00" level=info msg="dapr initialized. Status: Running. Init Elapsed 154.984ms" app_id=Yakchocolate-Lord scope=dapr.runtime type=log ver=0.11.3 - -Checking if Dapr sidecar is listening on GRPC port 59458 -Dapr sidecar is up and running. -Updating metadata for app command: python pythonState.py -You're up and running! Both Dapr and your app logs will appear here. - -== APP == State has been stored -== APP == Got value: b'value1' -== APP == Got value after delete: b'' -``` -{{% /codetab %}} - -{{% codetab %}} - -Update `state-example.php` with the following contents: - -```php -run(function(\Dapr\State\StateManager $stateManager, \Psr\Log\LoggerInterface $logger) { - $stateManager->save_state(store_name: 'statestore', item: new \Dapr\State\StateItem( - key: 'myFirstKey', - value: 'myFirstValue' - )); - $logger->alert('State has been stored'); - - $data = $stateManager->load_state(store_name: 'statestore', key: 'myFirstKey')->value; - $logger->alert("Got value: {data}", ['data' => $data]); - - $stateManager->delete_keys(store_name: 'statestore', keys: ['myFirstKey']); - $data = $stateManager->load_state(store_name: 'statestore', key: 'myFirstKey')->value; - $logger->alert("Got value after delete: {data}", ['data' => $data]); -}); -``` - -Now run it with: - -```bash -dapr --app-id myapp run -- php state-example.php -``` - -You should see something similar the following output: - -```md -✅ You're up and running! Both Dapr and your app logs will appear here. - -== APP == [2021-02-12T16:38:00.839201+01:00] APP.ALERT: State has been stored [] [] - -== APP == [2021-02-12T16:38:00.841997+01:00] APP.ALERT: Got value: myFirstValue {"data":"myFirstValue"} [] - -== APP == [2021-02-12T16:38:00.845721+01:00] APP.ALERT: Got value after delete: {"data":null} [] -``` - -{{% /codetab %}} - {{< /tabs >}} ## Step 4: Save and retrieve multiple states -Dapr also allows you to save and retrieve multiple states in the same call. +Below are code examples that leverage Dapr SDKs for saving and retrieving multiple states. -{{< tabs "HTTP API (Bash)" "HTTP API (PowerShell)" "Python SDK" "PHP SDK">}} +{{< tabs Java Python Javascript "HTTP API (Bash)" "HTTP API (PowerShell)">}} {{% codetab %}} -With the same dapr instance running from above save two key/value pairs into your statestore: + +```java +//dependencies + +import io.dapr.client.DaprClient; +import io.dapr.client.DaprClientBuilder; +import reactor.core.publisher.Mono; +import io.dapr.client.domain.State; +import java.util.List; + +//code + +private static final String STATE_STORE_NAME = "statestore"; + +DaprClient client = new DaprClientBuilder().build(); +Mono>> resultBulk = client.getBulkState(STATE_STORE_NAME, + Arrays.asList("order_1", "order_2"), String.class); +log.info("Result after get bulk" + resultBulk); + +``` + +Once saved run the following command to launch a Dapr sidecar and run the application: + ```bash -curl -X POST -H "Content-Type: application/json" -d '[{ "key": "key1", "value": "value1"}, { "key": "key2", "value": "value2"}]' http://localhost:3500/v1.0/state/statestore -``` - -Now get the states you just saved: -```bash -curl -X POST -H "Content-Type: application/json" -d '{"keys":["key1", "key2"]}' http://localhost:3500/v1.0/state/statestore/bulk -``` -{{% /codetab %}} - -{{% codetab %}} -With the same dapr instance running from above save two key/value pairs into your statestore: -```powershell -Invoke-RestMethod -Method Post -ContentType 'application/json' -Body '[{ "key": "key1", "value": "value1"}, { "key": "key2", "value": "value2"}]' -Uri 'http://localhost:3500/v1.0/state/statestore' -``` - -Now get the states you just saved: -```powershell -Invoke-RestMethod -Method Post -ContentType 'application/json' -Body '{"keys":["key1", "key2"]}' -Uri 'http://localhost:3500/v1.0/state/statestore/bulk' +dapr run --app-id orderprocessing --app-port 6001 --dapr-http-port 3601 --dapr-grpc-port 60001 mvn spring-boot:run ``` {{% /codetab %}} + {{% codetab %}} -The `StateItem` object can be used to store multiple Dapr states with the `save_states` and `get_states` methods. - -Update your `pythonState.py` file with the following code: - ```python +#dependencies + from dapr.clients import DaprClient from dapr.clients.grpc._state import StateItem -with DaprClient() as d: - s1 = StateItem(key="key1", value="value1") - s2 = StateItem(key="key2", value="value2") +#code - d.save_bulk_state(store_name="statestore", states=[s1,s2]) - print("States have been stored") +DAPR_STORE_NAME = "statestore" + +with DaprClient() as client: + client.save_bulk_state(store_name=DAPR_STORE_NAME, states=[StateItem(key="order_2", value=str(orderId))]) + result = client.get_bulk_state(store_name=DAPR_STORE_NAME, keys=["order_1", "order_2"], states_metadata={"metakey": "metavalue"}).items + logging.info('Result after get bulk: ' + str(result)) - items = d.get_bulk_state(store_name="statestore", keys=["key1", "key2"]).items - print(f"Got items: {[i.data for i in items]}") ``` -Now run your program with: +Once saved run the following command to launch a Dapr sidecar and run the application: ```bash -dapr --app-id myapp run python pythonState.py +dapr run --app-id orderprocessing --app-port 6001 --dapr-http-port 3601 --dapr-grpc-port 60001 -- python3 OrderProcessingService.py ``` -You should see an output similar to the following: +{{% /codetab %}} -```md -== DAPR == time="2021-01-06T21:54:56.7262358-08:00" level=info msg="starting Dapr Runtime -- version 0.11.3 -- commit a1a8e11" app_id=Musesequoia-Sprite scope=dapr.runtime type=log ver=0.11.3 -== DAPR == time="2021-01-06T21:54:56.7401933-08:00" level=info msg="standalone mode configured" app_id=Musesequoia-Sprite scope=dapr.runtime type=log ver=0.11.3 -== DAPR == time="2021-01-06T21:54:56.8754240-08:00" level=info msg="Initialized name resolution to standalone" app_id=Musesequoia-Sprite scope=dapr.runtime type=log ver=0.11.3 -== DAPR == time="2021-01-06T21:54:56.8844248-08:00" level=info msg="component loaded. name: statestore, type: state.redis" app_id=Musesequoia-Sprite scope=dapr.runtime type=log ver=0.11.3 -== DAPR == time="2021-01-06T21:54:56.8854273-08:00" level=info msg="API gRPC server is running on port 60614" app_id=Musesequoia-Sprite scope=dapr.runtime type=log ver=0.11.3 -== DAPR == time="2021-01-06T21:54:56.8854273-08:00" level=info msg="dapr initialized. Status: Running. Init Elapsed 145.234ms" app_id=Musesequoia-Sprite scope=dapr.runtime type=log ver=0.11.3 -Checking if Dapr sidecar is listening on GRPC port 60614 -Dapr sidecar is up and running. -Updating metadata for app command: python pythonState.py -You're up and running! Both Dapr and your app logs will appear here. -== APP == States have been stored -== APP == Got items: [b'value1', b'value2'] +{{% codetab %}} + +```javascript +//dependencies + +import { DaprClient, HttpMethod, CommunicationProtocolEnum } from 'dapr-client'; + +//code + +const client = new DaprClient(daprHost, process.env.DAPR_HTTP_PORT, CommunicationProtocolEnum.HTTP); +const STATE_STORE_NAME = "statestore"; +await client.state.save(STATE_STORE_NAME, [ + { + key: "order_1", + value: orderId.toString() + }, + { + key: "order_2", + value: orderId.toString() + } +]); +result = await client.state.getBulk(STATE_STORE_NAME, ["order_1", "order_2"]); +console.log("Result after get bulk: " + result); + +``` + +Once saved run the following command to launch a Dapr sidecar and run the application: + +```bash +dapr run --app-id orderprocessing --app-port 6001 --dapr-http-port 3601 --dapr-grpc-port 60001 npm start ``` {{% /codetab %}} {{% codetab %}} - -To batch load and save state with PHP, just create a "Plain Ole' PHP Object" (POPO) and annotate it with -the StateStore annotation. - -Update the `state-example.php` file: - -```php -run(function(\Dapr\State\StateManager $stateManager, \Psr\Log\LoggerInterface $logger) { - $obj = new MyState(); - $stateManager->save_object(item: $obj); - $logger->alert('States have been stored'); - - $stateManager->load_object(into: $obj); - $logger->alert("Got value: {data}", ['data' => $obj]); -}); -``` - -Run the app: - +With the same dapr instance running from above save two key/value pairs into your statestore: ```bash -dapr --app-id myapp run -- php state-example.php +curl -X POST -H "Content-Type: application/json" -d '[{ "key": "order_1", "value": "250"}, { "key": "order_2", "value": "550"}]' http://localhost:3601/v1.0/state/statestore ``` -And see the following output: +Now get the states you just saved: +```bash +curl -X POST -H "Content-Type: application/json" -d '{"keys":["order_1", "order_2"]}' http://localhost:3601/v1.0/state/statestore/bulk +``` +{{% /codetab %}} -```md -✅ You're up and running! Both Dapr and your app logs will appear here. +{{% codetab %}} +With the same dapr instance running from above save two key/value pairs into your statestore: +```powershell +Invoke-RestMethod -Method Post -ContentType 'application/json' -Body '[{ "key": "order_1", "value": "250"}, { "key": "order_2", "value": "550"}]' -Uri 'http://localhost:3601/v1.0/state/statestore' +``` -== APP == [2021-02-12T16:55:02.913801+01:00] APP.ALERT: States have been stored [] [] - -== APP == [2021-02-12T16:55:02.917850+01:00] APP.ALERT: Got value: [object MyState] {"data":{"MyState":{"key1":"value1","key2":"value2"}}} [] +Now get the states you just saved: +```powershell +Invoke-RestMethod -Method Post -ContentType 'application/json' -Body '{"keys":["order_1", "order_2"]}' -Uri 'http://localhost:3601/v1.0/state/statestore/bulk' ``` {{% /codetab %}} @@ -446,138 +574,172 @@ And see the following output: State transactions require a state store that supports multi-item transactions. Visit the [supported state stores page]({{< ref supported-state-stores >}}) page for a full list. Note that the default Redis container created in a self-hosted environment supports them. {{% /alert %}} -{{< tabs "HTTP API (Bash)" "HTTP API (PowerShell)" "Python SDK" "PHP SDK">}} +Below are code examples that leverage Dapr SDKs for performing state transactions. + +{{< tabs Dotnet Java Python Javascript "HTTP API (Bash)" "HTTP API (PowerShell)">}} + +{{% codetab %}} + +```csharp +//dependencies + +using Dapr.Client; + +//code + +string DAPR_STORE_NAME = "statestore"; +using var client = new DaprClientBuilder().Build(); +var requests = new List() +{ + new StateTransactionRequest("order_3", JsonSerializer.SerializeToUtf8Bytes(orderId.ToString()), StateOperationType.Upsert), + new StateTransactionRequest("order_2", null, StateOperationType.Delete) +}; + +CancellationTokenSource source = new CancellationTokenSource(); +CancellationToken cancellationToken = source.Token; + +await client.ExecuteStateTransactionAsync(DAPR_STORE_NAME, requests, cancellationToken: cancellationToken); + +``` + +Once saved run the following command to launch a Dapr sidecar and run the application: + +```bash +dapr run --app-id orderprocessing --app-port 6001 --dapr-http-port 3601 --dapr-grpc-port 60001 dotnet run +``` + +{{% /codetab %}} + + +{{% codetab %}} + +```java +//dependencies + +import io.dapr.client.DaprClient; +import io.dapr.client.DaprClientBuilder; +import io.dapr.client.domain.State; +import io.dapr.client.domain.TransactionalStateOperation; + +//code + +private static final String STATE_STORE_NAME = "statestore"; + +DaprClient client = new DaprClientBuilder().build(); +List> operationList = new ArrayList<>(); +operationList.add(new TransactionalStateOperation<>(TransactionalStateOperation.OperationType.UPSERT, + new State<>("order_3", Integer.toString(orderId), ""))); +operationList.add(new TransactionalStateOperation<>(TransactionalStateOperation.OperationType.DELETE, + new State<>("order_2"))); +client.executeStateTransaction(STATE_STORE_NAME, operationList).block(); + +``` + +Once saved run the following command to launch a Dapr sidecar and run the application: + +```bash +dapr run --app-id orderprocessing --app-port 6001 --dapr-http-port 3601 --dapr-grpc-port 60001 mvn spring-boot:run +``` + +{{% /codetab %}} + + +{{% codetab %}} + +```python +#dependencies + +from dapr.clients import DaprClient +from dapr.clients.grpc._state import StateItem +from dapr.clients.grpc._request import TransactionalStateOperation, TransactionOperationType + +#code + +DAPR_STORE_NAME = "statestore" +with DaprClient() as client: + client.execute_state_transaction(store_name=DAPR_STORE_NAME, operations=[ + TransactionalStateOperation( + operation_type=TransactionOperationType.upsert, + key="order_3", + data=str(orderId)), + TransactionalStateOperation(key="order_3", data=str(orderId)), + TransactionalStateOperation( + operation_type=TransactionOperationType.delete, + key="order_2", + data=str(orderId)), + TransactionalStateOperation(key="order_2", data=str(orderId)) + ]) + +``` + +Once saved run the following command to launch a Dapr sidecar and run the application: + +```bash +dapr run --app-id orderprocessing --app-port 6001 --dapr-http-port 3601 --dapr-grpc-port 60001 -- python3 OrderProcessingService.py +``` + +{{% /codetab %}} + + +{{% codetab %}} + +```javascript +//dependencies + +import { DaprClient, HttpMethod, CommunicationProtocolEnum } from 'dapr-client'; + + +//code + +const STATE_STORE_NAME = "statestore"; +const client = new DaprClient(daprHost, process.env.DAPR_HTTP_PORT, CommunicationProtocolEnum.HTTP); +await client.state.transaction(STATE_STORE_NAME, [ + { + operation: "upsert", + request: { + key: "order_3", + value: orderId.toString() + } + }, + { + operation: "delete", + request: { + key: "order_2" + } + } +]); + +``` + +Once saved run the following command to launch a Dapr sidecar and run the application: + +```bash +dapr run --app-id orderprocessing --app-port 6001 --dapr-http-port 3601 --dapr-grpc-port 60001 npm start +``` + +{{% /codetab %}} {{% codetab %}} With the same dapr instance running from above perform two state transactions: ```bash -curl -X POST -H "Content-Type: application/json" -d '{"operations": [{"operation":"upsert", "request": {"key": "key1", "value": "newValue1"}}, {"operation":"delete", "request": {"key": "key2"}}]}' http://localhost:3500/v1.0/state/statestore/transaction +curl -X POST -H "Content-Type: application/json" -d '{"operations": [{"operation":"upsert", "request": {"key": "order_1", "value": "250"}}, {"operation":"delete", "request": {"key": "order_2"}}]}' http://localhost:3601/v1.0/state/statestore/transaction ``` Now see the results of your state transactions: ```bash -curl -X POST -H "Content-Type: application/json" -d '{"keys":["key1", "key2"]}' http://localhost:3500/v1.0/state/statestore/bulk +curl -X POST -H "Content-Type: application/json" -d '{"keys":["order_1", "order_2"]}' http://localhost:3601/v1.0/state/statestore/bulk ``` {{% /codetab %}} {{% codetab %}} With the same dapr instance running from above save two key/value pairs into your statestore: ```powershell -Invoke-RestMethod -Method Post -ContentType 'application/json' -Body '{"operations": [{"operation":"upsert", "request": {"key": "key1", "value": "newValue1"}}, {"operation":"delete", "request": {"key": "key2"}}]}' -Uri 'http://localhost:3500/v1.0/state/statestore' +Invoke-RestMethod -Method Post -ContentType 'application/json' -Body '{"operations": [{"operation":"upsert", "request": {"key": "order_1", "value": "250"}}, {"operation":"delete", "request": {"key": "order_2"}}]}' -Uri 'http://localhost:3601/v1.0/state/statestore' ``` Now see the results of your state transactions: ```powershell -Invoke-RestMethod -Method Post -ContentType 'application/json' -Body '{"keys":["key1", "key2"]}' -Uri 'http://localhost:3500/v1.0/state/statestore/bulk' -``` - -{{% /codetab %}} - -{{% codetab %}} - -The `TransactionalStateOperation` can perform a state transaction if your state stores need to be transactional. - -Update your `pythonState.py` file with the following code: - -```python -from dapr.clients import DaprClient -from dapr.clients.grpc._state import StateItem -from dapr.clients.grpc._request import TransactionalStateOperation, TransactionOperationType - -with DaprClient() as d: - s1 = StateItem(key="key1", value="value1") - s2 = StateItem(key="key2", value="value2") - - d.save_bulk_state(store_name="statestore", states=[s1,s2]) - print("States have been stored") - - d.execute_state_transaction( - store_name="statestore", - operations=[ - TransactionalStateOperation(key="key1", data="newValue1", operation_type=TransactionOperationType.upsert), - TransactionalStateOperation(key="key2", data="value2", operation_type=TransactionOperationType.delete) - ] - ) - print("State transactions have been completed") - - items = d.get_bulk_state(store_name="statestore", keys=["key1", "key2"]).items - print(f"Got items: {[i.data for i in items]}") -``` - -Now run your program with: - -```bash -dapr run python pythonState.py -``` - -You should see an output similar to the following: - -```md -Starting Dapr with id Singerchecker-Player. HTTP Port: 59533. gRPC Port: 59534 -== DAPR == time="2021-01-06T22:18:14.1246721-08:00" level=info msg="starting Dapr Runtime -- version 0.11.3 -- commit a1a8e11" app_id=Singerchecker-Player scope=dapr.runtime type=log ver=0.11.3 -== DAPR == time="2021-01-06T22:18:14.1346254-08:00" level=info msg="standalone mode configured" app_id=Singerchecker-Player scope=dapr.runtime type=log ver=0.11.3 -== DAPR == time="2021-01-06T22:18:14.2747063-08:00" level=info msg="component loaded. name: statestore, type: state.redis" app_id=Singerchecker-Player scope=dapr.runtime type=log ver=0.11.3 -== DAPR == time="2021-01-06T22:18:14.2757062-08:00" level=info msg="API gRPC server is running on port 59534" app_id=Singerchecker-Player scope=dapr.runtime type=log ver=0.11.3 -== DAPR == time="2021-01-06T22:18:14.2767059-08:00" level=info msg="dapr initialized. Status: Running. Init Elapsed 142.0805ms" app_id=Singerchecker-Player scope=dapr.runtime type=log ver=0.11.3 - -Checking if Dapr sidecar is listening on GRPC port 59534 -Dapr sidecar is up and running. -Updating metadata for app command: python pythonState.py -You're up and running! Both Dapr and your app logs will appear here. - -== APP == State transactions have been completed -== APP == Got items: [b'value1', b''] -``` - -{{% /codetab %}} - -{{% codetab %}} - -Transactional state is supported by extending `TransactionalState` base object which hooks into your -object via setters and getters to provide a transaction. Before you created your own transactional object, -but now you'll ask the Dependency Injection framework to build one for you. - -Modify the `state-example.php` file again: - -```php -run(function(MyState $obj, \Psr\Log\LoggerInterface $logger, \Dapr\State\StateManager $stateManager) { - $obj->begin(); - $obj->key1 = 'hello world'; - $obj->key2 = 'value3'; - $obj->commit(); - $logger->alert('Transaction committed!'); - - // begin a new transaction which reloads from the store - $obj->begin(); - $logger->alert("Got value: {key1}, {key2}", ['key1' => $obj->key1, 'key2' => $obj->key2]); -}); -``` - -Run the application: - -```bash -dapr --app-id myapp run -- php state-example.php -``` - -Observe the following output: - -```md -✅ You're up and running! Both Dapr and your app logs will appear here. - -== APP == [2021-02-12T17:10:06.837110+01:00] APP.ALERT: Transaction committed! [] [] - -== APP == [2021-02-12T17:10:06.840857+01:00] APP.ALERT: Got value: hello world, value3 {"key1":"hello world","key2":"value3"} [] +Invoke-RestMethod -Method Post -ContentType 'application/json' -Body '{"keys":["order_1", "order_2"]}' -Uri 'http://localhost:3601/v1.0/state/statestore/bulk' ``` {{% /codetab %}} @@ -588,4 +750,4 @@ Observe the following output: - Read the full [State API reference]({{< ref state_api.md >}}) - Try one of the [Dapr SDKs]({{< ref sdks >}}) -- Build a [stateful service]({{< ref howto-stateful-service.md >}}) +- Build a [stateful service]({{< ref howto-stateful-service.md >}}) \ No newline at end of file From ad20a76b321feff8d43ef6cf26553a78c381330e Mon Sep 17 00:00:00 2001 From: Amulya Varote Date: Tue, 23 Nov 2021 03:09:03 -0800 Subject: [PATCH 03/20] Modified TTL page under state management --- .../state-management/state-store-ttl.md | 75 ++++++++----------- 1 file changed, 30 insertions(+), 45 deletions(-) diff --git a/daprdocs/content/en/developing-applications/building-blocks/state-management/state-store-ttl.md b/daprdocs/content/en/developing-applications/building-blocks/state-management/state-store-ttl.md index 14f0034f0..de57e9aa1 100644 --- a/daprdocs/content/en/developing-applications/building-blocks/state-management/state-store-ttl.md +++ b/daprdocs/content/en/developing-applications/building-blocks/state-management/state-store-ttl.md @@ -31,12 +31,38 @@ Please refer to the TTL column in the tables at [state store components]({{< ref State TTL can be set in the metadata as part of the state store set request: -{{< tabs "HTTP API (Bash)" "HTTP API (PowerShell)" "Python SDK" "PHP SDK">}} +{{< tabs Python "HTTP API (Bash)" "HTTP API (PowerShell)">}} + +{{% codetab %}} + +```python +#dependencies + +from dapr.clients import DaprClient + +#code + +DAPR_STORE_NAME = "statestore" + +with DaprClient() as client: + client.save_state(DAPR_STORE_NAME, "order_1", str(orderId), metadata=( + ('ttlInSeconds', '120') + )) + +``` + +Once saved run the following command to launch a Dapr sidecar and run the application: + +```bash +dapr run --app-id orderprocessing --app-port 6001 --dapr-http-port 3601 --dapr-grpc-port 60001 -- python3 OrderProcessingService.py +``` + +{{% /codetab %}} {{% codetab %}} ```bash -curl -X POST -H "Content-Type: application/json" -d '[{ "key": "key1", "value": "value1", "metadata": { "ttlInSeconds": "120" } }]' http://localhost:3500/v1.0/state/statestore +curl -X POST -H "Content-Type: application/json" -d '[{ "key": "order_1", "value": "250", "metadata": { "ttlInSeconds": "120" } }]' http://localhost:3601/v1.0/state/statestore ``` {{% /codetab %}} @@ -44,48 +70,7 @@ curl -X POST -H "Content-Type: application/json" -d '[{ "key": "key1", "value": {{% codetab %}} ```powershell -Invoke-RestMethod -Method Post -ContentType 'application/json' -Body '[{"key": "key1", "value": "value1", "metadata": {"ttlInSeconds": "120"}}]' -Uri 'http://localhost:3500/v1.0/state/statestore' -``` - -{{% /codetab %}} - -{{% codetab %}} - -```python -from dapr.clients import DaprClient - -with DaprClient() as d: - d.save_state( - store_name="statestore", - key="myFirstKey", - value="myFirstValue", - metadata=( - ('ttlInSeconds', '120') - ) - ) - print("State has been stored") - -``` - -{{% /codetab %}} - -{{% codetab %}} - -Save the following in `state-example.php`: - -```php -run(function(\Dapr\State\StateManager $stateManager, \Psr\Log\LoggerInterface $logger) { - $stateManager->save_state(store_name: 'statestore', item: new \Dapr\State\StateItem( - key: 'myFirstKey', - value: 'myFirstValue', - metadata: ['ttlInSeconds' => '120'] - )); - $logger->alert('State has been stored'); -}); +Invoke-RestMethod -Method Post -ContentType 'application/json' -Body '[{"key": "order_1", "value": "250", "metadata": {"ttlInSeconds": "120"}}]' -Uri 'http://localhost:3601/v1.0/state/statestore' ``` {{% /codetab %}} @@ -98,4 +83,4 @@ See [this guide]({{< ref state_api.md >}}) for a reference on the state API. - Learn [how to use key value pairs to persist a state]({{< ref howto-get-save-state.md >}}) - List of [state store components]({{< ref supported-state-stores >}}) -- Read the [API reference]({{< ref state_api.md >}}) +- Read the [API reference]({{< ref state_api.md >}}) \ No newline at end of file From d63571298946c1a149ef964bfb75fff960d9609b Mon Sep 17 00:00:00 2001 From: Amulya Varote Date: Tue, 23 Nov 2021 11:35:59 -0800 Subject: [PATCH 04/20] Added complete code snippets --- .../howto-invoke-discover-services.md | 171 +++-- .../state-management/howto-get-save-state.md | 592 +++++++++++++----- .../supported-pubsub/setup-nats-streaming.md | 2 +- 3 files changed, 566 insertions(+), 199 deletions(-) diff --git a/daprdocs/content/en/developing-applications/building-blocks/service-invocation/howto-invoke-discover-services.md b/daprdocs/content/en/developing-applications/building-blocks/service-invocation/howto-invoke-discover-services.md index 185d718ab..a902fe3de 100644 --- a/daprdocs/content/en/developing-applications/building-blocks/service-invocation/howto-invoke-discover-services.md +++ b/daprdocs/content/en/developing-applications/building-blocks/service-invocation/howto-invoke-discover-services.md @@ -185,17 +185,41 @@ Below are code examples that leverage Dapr SDKs for service invocation. ```csharp //dependencies - -using Dapr.Client; +using System; +using System.Collections.Generic; using System.Net.Http; +using System.Net.Http.Headers; +using System.Threading.Tasks; +using Dapr.Client; +using Microsoft.AspNetCore.Mvc; +using System.Threading; //code +namespace EventService +{ + class Program + { + static async Task Main(string[] args) + { + //Calling service multiple times with 5 seconds gap in between the calls + while(true) { + System.Threading.Thread.Sleep(5000); + Random random = new Random(); + int orderId = random.Next(1,1000); -CancellationTokenSource source = new CancellationTokenSource(); -CancellationToken cancellationToken = source.Token; -using var client = new DaprClientBuilder().Build(); -var result = client.CreateInvokeMethodRequest(HttpMethod.Get, "checkout", "checkout/" + orderId, cancellationToken); -await client.InvokeMethodAsync(result); + CancellationTokenSource source = new CancellationTokenSource(); + CancellationToken cancellationToken = source.Token; + //Using Dapr SDK to invoke a method + using var client = new DaprClientBuilder().Build(); + var result = client.CreateInvokeMethodRequest(HttpMethod.Get, "checkout", "checkout/" + orderId, cancellationToken); + await client.InvokeMethodAsync(result); + + Console.WriteLine("Order requested: " + orderId); + Console.WriteLine("Result: " + result); + } + } + } +} ``` {{% /codetab %}} @@ -205,21 +229,45 @@ await client.InvokeMethodAsync(result); ```java //dependencies - import io.dapr.client.DaprClient; import io.dapr.client.DaprClientBuilder; import io.dapr.client.domain.HttpExtension; +import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.util.Random; +import java.util.concurrent.TimeUnit; //code +@SpringBootApplication +public class OrderProcessingServiceApplication { -DaprClient daprClient = new DaprClientBuilder().build(); -var result = daprClient.invokeMethod( - "checkout", - "checkout/" + orderId, - null, - HttpExtension.GET, - String.class -); + private static final Logger log = LoggerFactory.getLogger(OrderProcessingServiceApplication.class); + + public static void main(String[] args) throws InterruptedException{ + + //Calling service multiple times with 5 seconds gap in between the calls + while(true) { + TimeUnit.MILLISECONDS.sleep(5000); + Random random = new Random(); + int orderId = random.nextInt(1000-1) + 1; + + //Using Dapr SDK to invoke a method + DaprClient client = new DaprClientBuilder().build(); + var result = client.invokeMethod( + "checkout", + "checkout/" + orderId, + null, + HttpExtension.GET, + String.class + ); + + log.info("Order requested: " + orderId); + log.info("Result: " + result); + } + } +} ``` {{% /codetab %}} @@ -228,18 +276,31 @@ var result = daprClient.invokeMethod( ```python #dependencies - +import random +from time import sleep +import requests +import logging from dapr.clients import DaprClient #code +logging.basicConfig(level = logging.INFO) + +#Calling service multiple times with 5 seconds gap in between the calls +while True: + sleep(random.randrange(50, 5000) / 1000) + orderId = random.randint(1, 1000) -with DaprClient() as daprClient: - result = daprClient.invoke_method( - "checkout", - f"checkout/{orderId}", - data=b'', - http_verb="GET" - ) + #Using Dapr SDK to invoke a method + with DaprClient() as client: + result = client.invoke_method( + "checkout", + f"checkout/{orderId}", + data=b'', + http_verb="GET" + ) + logging.basicConfig(level = logging.INFO) + logging.info('Order requested: ' + str(orderId)) + logging.info('Result: ' + str(result)) ``` {{% /codetab %}} @@ -249,19 +310,37 @@ with DaprClient() as daprClient: //dependencies import ( - dapr "github.com/dapr/go-sdk/client" + "context" + "log" + "math/rand" + "time" + "strconv" + dapr "github.com/dapr/go-sdk/client" + ) //code +func main() { -client, err := dapr.NewClient() -if err != nil { - panic(err) + //Calling service multiple times with 5 seconds gap in between the calls + for i := 0; i < 10; i++ { + time.Sleep(5000) + orderId := rand.Intn(1000-1) + 1 + + //Using Dapr SDK to invoke a method + client, err := dapr.NewClient() + if err != nil { + panic(err) + } + defer client.Close() + ctx := context.Background() + result, err := client.InvokeMethod(ctx, "checkout", "checkout/" + strconv.Itoa(orderId), "get") + + log.Println("Order requested: " + strconv.Itoa(orderId)) + log.Println("Result: ") + log.Println(result) + } } -defer client.Close() -ctx := context.Background() - -result, err := client.InvokeMethod(ctx, "checkout", "checkout/" + strconv.Itoa(orderId), "get") ``` {{% /codetab %}} @@ -270,14 +349,32 @@ result, err := client.InvokeMethod(ctx, "checkout", "checkout/" + strconv.Itoa(o ```javascript //dependencies - import { DaprClient, HttpMethod, CommunicationProtocolEnum } from 'dapr-client'; -//code - const daprHost = "127.0.0.1"; -const client = new DaprClient(daprHost, process.env.DAPR_HTTP_PORT, CommunicationProtocolEnum.HTTP); -const result = await client.invoker.invoke('checkout' , "checkout/" + orderId , HttpMethod.GET); + +//code +var main = function() { + + //Calling service multiple times with 5 seconds gap in between the calls + for(var i=0;i<10;i++) { + sleep(5000); + var orderId = Math.floor(Math.random() * (1000 - 1) + 1); + + //Using Dapr SDK to invoke a method + const client = new DaprClient(daprHost, process.env.DAPR_HTTP_PORT, CommunicationProtocolEnum.HTTP); + const result = await client.invoker.invoke('checkout' , "checkout/" + orderId , HttpMethod.GET); + + console.log("Order requested: " + orderId); + console.log("Result: " + result); + } +} + +function sleep(ms) { + return new Promise(resolve => setTimeout(resolve, ms)); +} + +main(); ``` {{% /codetab %}} diff --git a/daprdocs/content/en/developing-applications/building-blocks/state-management/howto-get-save-state.md b/daprdocs/content/en/developing-applications/building-blocks/state-management/howto-get-save-state.md index e3a678037..e096b8c18 100644 --- a/daprdocs/content/en/developing-applications/building-blocks/state-management/howto-get-save-state.md +++ b/daprdocs/content/en/developing-applications/building-blocks/state-management/howto-get-save-state.md @@ -66,7 +66,7 @@ See the instructions [here]({{< ref "setup-state-store" >}}) on how to setup dif ## Step 2: Save and retrieve a single state -The following example shows how to a single key/value pair using the Dapr state building block. +The following example shows how to save and retrieve a single key/value pair using the Dapr state building block. {{% alert title="Note" color="warning" %}} It is important to set an app-id, as the state keys are prefixed with this value. If you don't set it one is generated for you at runtime, and the next time you run the command a new one will be generated and you will no longer be able to access previously saved state. @@ -79,18 +79,43 @@ Below are code examples that leverage Dapr SDKs for saving and retrieving a sing {{% codetab %}} ```csharp -//dependencies +//dependencies +using System; +using System.Collections.Generic; +using System.Net.Http; +using System.Net.Http.Headers; +using System.Threading.Tasks; using Dapr.Client; +using Microsoft.AspNetCore.Mvc; +using System.Threading; +using System.Text.Json; //code +namespace EventService +{ + class Program + { + static async Task Main(string[] args) + { + string DAPR_STORE_NAME = "statestore"; -string DAPR_STORE_NAME = "statestore"; + //Calling service multiple times with 5 seconds gap in between the calls + while(true) { + System.Threading.Thread.Sleep(5000); + Random random = new Random(); + int orderId = random.Next(1,1000); -using var client = new DaprClientBuilder().Build(); -await client.SaveStateAsync(DAPR_STORE_NAME, "order_1", orderId.ToString()); -var result = await client.GetStateAsync(DAPR_STORE_NAME, orderId.ToString()); -Console.WriteLine("Result after get: " + result); + //Using Dapr SDK to save and get state + using var client = new DaprClientBuilder().Build(); + await client.SaveStateAsync(DAPR_STORE_NAME, "order_1", orderId.ToString()); + await client.SaveStateAsync(DAPR_STORE_NAME, "order_2", orderId.ToString()); + var result = await client.GetStateAsync(DAPR_STORE_NAME, orderId.ToString()); + Console.WriteLine("Result after get: " + result); + } + } + } +} ``` @@ -103,23 +128,46 @@ dapr run --app-id orderprocessing --app-port 6001 --dapr-http-port 3601 --dapr-g {{% /codetab %}} - {{% codetab %}} ```java -//dependencies +//dependencies import io.dapr.client.DaprClient; import io.dapr.client.DaprClientBuilder; +import io.dapr.client.domain.State; +import io.dapr.client.domain.TransactionalStateOperation; +import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import reactor.core.publisher.Mono; +import java.util.Random; +import java.util.concurrent.TimeUnit; //code +@SpringBootApplication +public class OrderProcessingServiceApplication { -private static final String STATE_STORE_NAME = "statestore"; + private static final Logger log = LoggerFactory.getLogger(OrderProcessingServiceApplication.class); -DaprClient client = new DaprClientBuilder().build(); -client.saveState(STATE_STORE_NAME, "order_1", Integer.toString(orderId)).block(); -Mono> result = client.getState(STATE_STORE_NAME, "order_1", String.class); -log.info("Result after get" + result); + public static void main(String[] args) throws InterruptedException{ + String STATE_STORE_NAME = "statestore"; + + //Calling service multiple times with 5 seconds gap in between the calls + while(true) { + TimeUnit.MILLISECONDS.sleep(5000); + Random random = new Random(); + int orderId = random.nextInt(1000-1) + 1; + + //Using Dapr SDK to save and get state + DaprClient client = new DaprClientBuilder().build(); + client.saveState(STATE_STORE_NAME, "order_1", Integer.toString(orderId)).block(); + client.saveState(STATE_STORE_NAME, "order_2", Integer.toString(orderId)).block(); + Mono> result = client.getState(STATE_STORE_NAME, "order_1", String.class); + log.info("Result after get" + result); + } + } +} ``` @@ -135,15 +183,28 @@ dapr run --app-id orderprocessing --app-port 6001 --dapr-http-port 3601 --dapr-g {{% codetab %}} ```python -#dependencies +#dependencies +import random +from time import sleep +import requests +import logging from dapr.clients import DaprClient +from dapr.clients.grpc._state import StateItem +from dapr.clients.grpc._request import TransactionalStateOperation, TransactionOperationType #code - +logging.basicConfig(level = logging.INFO) + DAPR_STORE_NAME = "statestore" -with DaprClient() as client: +#Calling service multiple times with 5 seconds gap in between the calls +while True: + sleep(random.randrange(50, 5000) / 1000) + orderId = random.randint(1, 1000) + + #Using Dapr SDK to save and get state + with DaprClient() as client: client.save_state(DAPR_STORE_NAME, "order_1", str(orderId)) result = client.get_state(DAPR_STORE_NAME, "order_1") logging.info('Result after get: ' + str(result)) @@ -162,35 +223,49 @@ dapr run --app-id orderprocessing --app-port 6001 --dapr-http-port 3601 --dapr-g {{% codetab %}} ```go -//dependencies +//dependencies import ( - dapr "github.com/dapr/go-sdk/client" + "context" + "log" + "math/rand" + "time" + "strconv" + dapr "github.com/dapr/go-sdk/client" + ) //code +func main() { -client, err := dapr.NewClient() -STATE_STORE_NAME := "statestore" + STATE_STORE_NAME := "statestore" -if err != nil { - panic(err) + //Calling service multiple times with 5 seconds gap in between the calls + for i := 0; i < 10; i++ { + time.Sleep(5000) + orderId := rand.Intn(1000-1) + 1 + + //Using Dapr SDK to save and get state + client, err := dapr.NewClient() + if err != nil { + panic(err) + } + defer client.Close() + ctx := context.Background() + + if err := client.SaveState(ctx, STATE_STORE_NAME, "order_1", []byte(strconv.Itoa(orderId))); err != nil { + panic(err) + } + + result, err := client.GetState(ctx, STATE_STORE_NAME, "order_1") + if err != nil { + panic(err) + } + + log.Println("Result after get: ") + log.Println(result) + } } -defer client.Close() -ctx := context.Background() - -if err := client.SaveState(ctx, store, "order_1", []byte(strconv.Itoa(orderId))); err != nil { - panic(err) -} - -result, err := client.GetState(ctx, store, "order_2") -if err != nil { - panic(err) -} - -log.Println("Result after get: ") -log.Println(result) - ``` @@ -206,24 +281,43 @@ dapr run --app-id orderprocessing --app-port 6001 --dapr-http-port 3601 --dapr-g {{% codetab %}} ```javascript -//dependencies +//dependencies import { DaprClient, HttpMethod, CommunicationProtocolEnum } from 'dapr-client'; //code - -const STATE_STORE_NAME = "statestore"; - const daprHost = "127.0.0.1"; -const client = new DaprClient(daprHost, process.env.DAPR_HTTP_PORT, CommunicationProtocolEnum.HTTP); -await client.state.save(STATE_STORE_NAME, [ -{ - key: "order_1", - value: orderId.toString() -}]); -var result = await client.state.get(STATE_STORE_NAME, "order_1"); -console.log("Result after get: " + result); +var main = function() { + const STATE_STORE_NAME = "statestore"; + + //Calling service multiple times with 5 seconds gap in between the calls + for(var i=0;i<10;i++) { + sleep(5000); + var orderId = Math.floor(Math.random() * (1000 - 1) + 1); + + //Using Dapr SDK to save and get state + const client = new DaprClient(daprHost, process.env.DAPR_HTTP_PORT, CommunicationProtocolEnum.HTTP); + await client.state.save(STATE_STORE_NAME, [ + { + key: "order_1", + value: orderId.toString() + }, + { + key: "order_2", + value: orderId.toString() + } + ]); + var result = await client.state.get(STATE_STORE_NAME, "order_1"); + console.log("Result after get: " + result); + } +} + +function sleep(ms) { + return new Promise(resolve => setTimeout(resolve, ms)); +} + +main(); ``` @@ -290,15 +384,32 @@ Below are code examples that leverage Dapr SDKs for deleting the state. {{% codetab %}} ```csharp -//dependencies +//dependencies +using System; +using System.Collections.Generic; +using System.Net.Http; +using System.Net.Http.Headers; +using System.Threading.Tasks; using Dapr.Client; +using Microsoft.AspNetCore.Mvc; +using System.Threading; +using System.Text.Json; //code - -string DAPR_STORE_NAME = "statestore"; - -await client.DeleteStateAsync(DAPR_STORE_NAME, "order_1", cancellationToken: cancellationToken); +namespace EventService +{ + class Program + { + static async Task Main(string[] args) + { + string DAPR_STORE_NAME = "statestore"; + //Using Dapr SDK to delete the state + using var client = new DaprClientBuilder().Build(); + await client.DeleteStateAsync(DAPR_STORE_NAME, "order_1", cancellationToken: cancellationToken); + } + } +} ``` @@ -314,18 +425,33 @@ dapr run --app-id orderprocessing --app-port 6001 --dapr-http-port 3601 --dapr-g {{% codetab %}} ```java -//dependencies +//dependencies import io.dapr.client.DaprClient; import io.dapr.client.DaprClientBuilder; +import io.dapr.client.domain.State; +import io.dapr.client.domain.TransactionalStateOperation; +import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import java.util.Random; +import java.util.concurrent.TimeUnit; //code +@SpringBootApplication +public class OrderProcessingServiceApplication { -private static final String STATE_STORE_NAME = "statestore"; + private static final Logger log = LoggerFactory.getLogger(OrderProcessingServiceApplication.class); -DaprClient client = new DaprClientBuilder().build(); -String storedEtag = client.getState(STATE_STORE_NAME, "order_1", String.class).block().getEtag(); -client.deleteState(STATE_STORE_NAME, "order_1", storedEtag, null).block(); + public static void main(String[] args) throws InterruptedException{ + String STATE_STORE_NAME = "statestore"; + + //Using Dapr SDK to delete the state + DaprClient client = new DaprClientBuilder().build(); + String storedEtag = client.getState(STATE_STORE_NAME, "order_1", String.class).block().getEtag(); + client.deleteState(STATE_STORE_NAME, "order_1", storedEtag, null).block(); + } +} ``` @@ -341,16 +467,24 @@ dapr run --app-id orderprocessing --app-port 6001 --dapr-http-port 3601 --dapr-g {{% codetab %}} ```python -#dependencies +#dependencies +import random +from time import sleep +import requests +import logging from dapr.clients import DaprClient +from dapr.clients.grpc._state import StateItem +from dapr.clients.grpc._request import TransactionalStateOperation, TransactionOperationType #code - +logging.basicConfig(level = logging.INFO) + DAPR_STORE_NAME = "statestore" +#Using Dapr SDK to delete the state with DaprClient() as client: - client.delete_state(store_name=DAPR_STORE_NAME, key="order_1") + client.delete_state(store_name=DAPR_STORE_NAME, key="order_1") ``` @@ -366,25 +500,30 @@ dapr run --app-id orderprocessing --app-port 6001 --dapr-http-port 3601 --dapr-g {{% codetab %}} ```go -//dependencies +//dependencies import ( - dapr "github.com/dapr/go-sdk/client" + "context" + dapr "github.com/dapr/go-sdk/client" + ) //code +func main() { -client, err := dapr.NewClient() -STATE_STORE_NAME := "statestore" + STATE_STORE_NAME := "statestore" -if err != nil { - panic(err) -} -defer client.Close() -ctx := context.Background() + //Using Dapr SDK to delete the state + client, err := dapr.NewClient() + if err != nil { + panic(err) + } + defer client.Close() + ctx := context.Background() -if err := client.DeleteState(ctx, store, "order_1"); err != nil { - panic(err) + if err := client.DeleteState(ctx, STATE_STORE_NAME, "order_1"); err != nil { + panic(err) + } } ``` @@ -401,17 +540,26 @@ dapr run --app-id orderprocessing --app-port 6001 --dapr-http-port 3601 --dapr-g {{% codetab %}} ```javascript -//dependencies +//dependencies import { DaprClient, HttpMethod, CommunicationProtocolEnum } from 'dapr-client'; //code +const daprHost = "127.0.0.1"; -const STATE_STORE_NAME = "statestore"; +var main = function() { + const STATE_STORE_NAME = "statestore"; -const client = new DaprClient(daprHost, process.env.DAPR_HTTP_PORT, CommunicationProtocolEnum.HTTP); -await client.state.delete(STATE_STORE_NAME, "order_1"); + //Using Dapr SDK to save and get state + const client = new DaprClient(daprHost, process.env.DAPR_HTTP_PORT, CommunicationProtocolEnum.HTTP); + await client.state.delete(STATE_STORE_NAME, "order_1"); +} +function sleep(ms) { + return new Promise(resolve => setTimeout(resolve, ms)); +} + +main(); ``` @@ -450,22 +598,41 @@ Below are code examples that leverage Dapr SDKs for saving and retrieving multip {{% codetab %}} ```java -//dependencies +//dependencies import io.dapr.client.DaprClient; import io.dapr.client.DaprClientBuilder; -import reactor.core.publisher.Mono; import io.dapr.client.domain.State; -import java.util.List; +import io.dapr.client.domain.TransactionalStateOperation; +import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import reactor.core.publisher.Mono; +import java.util.Random; +import java.util.concurrent.TimeUnit; //code +@SpringBootApplication +public class OrderProcessingServiceApplication { -private static final String STATE_STORE_NAME = "statestore"; + private static final Logger log = LoggerFactory.getLogger(OrderProcessingServiceApplication.class); -DaprClient client = new DaprClientBuilder().build(); -Mono>> resultBulk = client.getBulkState(STATE_STORE_NAME, - Arrays.asList("order_1", "order_2"), String.class); -log.info("Result after get bulk" + resultBulk); + public static void main(String[] args) throws InterruptedException{ + String STATE_STORE_NAME = "statestore"; + + //Calling service multiple times with 5 seconds gap in between the calls + while(true) { + TimeUnit.MILLISECONDS.sleep(5000); + Random random = new Random(); + int orderId = random.nextInt(1000-1) + 1; + + //Using Dapr SDK to retrieve multiple states + DaprClient client = new DaprClientBuilder().build(); + Mono>> resultBulk = client.getBulkState(STATE_STORE_NAME, + Arrays.asList("order_1", "order_2"), String.class); + } + } +} ``` @@ -481,19 +648,31 @@ dapr run --app-id orderprocessing --app-port 6001 --dapr-http-port 3601 --dapr-g {{% codetab %}} ```python -#dependencies +#dependencies +import random +from time import sleep +import requests +import logging from dapr.clients import DaprClient from dapr.clients.grpc._state import StateItem +from dapr.clients.grpc._request import TransactionalStateOperation, TransactionOperationType #code - +logging.basicConfig(level = logging.INFO) + DAPR_STORE_NAME = "statestore" -with DaprClient() as client: - client.save_bulk_state(store_name=DAPR_STORE_NAME, states=[StateItem(key="order_2", value=str(orderId))]) - result = client.get_bulk_state(store_name=DAPR_STORE_NAME, keys=["order_1", "order_2"], states_metadata={"metakey": "metavalue"}).items - logging.info('Result after get bulk: ' + str(result)) +#Calling service multiple times with 5 seconds gap in between the calls +while True: + sleep(random.randrange(50, 5000) / 1000) + orderId = random.randint(1, 1000) + + #Using Dapr SDK to save and retrieve multiple states + with DaprClient() as client: + client.save_bulk_state(store_name=DAPR_STORE_NAME, states=[StateItem(key="order_2", value=str(orderId))]) + result = client.get_bulk_state(store_name=DAPR_STORE_NAME, keys=["order_1", "order_2"], states_metadata={"metakey": "metavalue"}).items + logging.info('Result after get bulk: ' + str(result)) ``` @@ -510,26 +689,43 @@ dapr run --app-id orderprocessing --app-port 6001 --dapr-http-port 3601 --dapr-g {{% codetab %}} ```javascript -//dependencies +//dependencies import { DaprClient, HttpMethod, CommunicationProtocolEnum } from 'dapr-client'; //code +const daprHost = "127.0.0.1"; -const client = new DaprClient(daprHost, process.env.DAPR_HTTP_PORT, CommunicationProtocolEnum.HTTP); -const STATE_STORE_NAME = "statestore"; -await client.state.save(STATE_STORE_NAME, [ - { - key: "order_1", - value: orderId.toString() - }, - { - key: "order_2", - value: orderId.toString() - } -]); -result = await client.state.getBulk(STATE_STORE_NAME, ["order_1", "order_2"]); -console.log("Result after get bulk: " + result); +var main = function() { + const STATE_STORE_NAME = "statestore"; + + //Calling service multiple times with 5 seconds gap in between the calls + for(var i=0;i<10;i++) { + sleep(5000); + var orderId = Math.floor(Math.random() * (1000 - 1) + 1); + + //Using Dapr SDK to save and retrieve multiple states + const client = new DaprClient(daprHost, process.env.DAPR_HTTP_PORT, CommunicationProtocolEnum.HTTP); + await client.state.save(STATE_STORE_NAME, [ + { + key: "order_1", + value: orderId.toString() + }, + { + key: "order_2", + value: orderId.toString() + } + ]); + result = await client.state.getBulk(STATE_STORE_NAME, ["order_1", "order_2"]); + console.log("Result after get bulk: " + result); + } +} + +function sleep(ms) { + return new Promise(resolve => setTimeout(resolve, ms)); +} + +main(); ``` @@ -581,24 +777,46 @@ Below are code examples that leverage Dapr SDKs for performing state transaction {{% codetab %}} ```csharp -//dependencies +//dependencies +using System; +using System.Collections.Generic; +using System.Net.Http; +using System.Net.Http.Headers; +using System.Threading.Tasks; using Dapr.Client; +using Microsoft.AspNetCore.Mvc; +using System.Threading; +using System.Text.Json; //code - -string DAPR_STORE_NAME = "statestore"; -using var client = new DaprClientBuilder().Build(); -var requests = new List() +namespace EventService { - new StateTransactionRequest("order_3", JsonSerializer.SerializeToUtf8Bytes(orderId.ToString()), StateOperationType.Upsert), - new StateTransactionRequest("order_2", null, StateOperationType.Delete) -}; + class Program + { + static async Task Main(string[] args) + { + string DAPR_STORE_NAME = "statestore"; + //Calling service multiple times with 5 seconds gap in between the calls + while(true) { + System.Threading.Thread.Sleep(5000); + Random random = new Random(); + int orderId = random.Next(1,1000); -CancellationTokenSource source = new CancellationTokenSource(); -CancellationToken cancellationToken = source.Token; - -await client.ExecuteStateTransactionAsync(DAPR_STORE_NAME, requests, cancellationToken: cancellationToken); + //Using Dapr SDK to perform the state transactions + using var client = new DaprClientBuilder().Build(); + var requests = new List() + { + new StateTransactionRequest("order_3", JsonSerializer.SerializeToUtf8Bytes(orderId.ToString()), StateOperationType.Upsert), + new StateTransactionRequest("order_2", null, StateOperationType.Delete) + }; + CancellationTokenSource source = new CancellationTokenSource(); + CancellationToken cancellationToken = source.Token; + await client.ExecuteStateTransactionAsync(DAPR_STORE_NAME, requests, cancellationToken: cancellationToken); + } + } + } +} ``` @@ -614,24 +832,48 @@ dapr run --app-id orderprocessing --app-port 6001 --dapr-http-port 3601 --dapr-g {{% codetab %}} ```java -//dependencies +//dependencies import io.dapr.client.DaprClient; import io.dapr.client.DaprClientBuilder; import io.dapr.client.domain.State; import io.dapr.client.domain.TransactionalStateOperation; +import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; +import reactor.core.publisher.Mono; +import java.util.Random; +import java.util.concurrent.TimeUnit; //code +@SpringBootApplication +public class OrderProcessingServiceApplication { -private static final String STATE_STORE_NAME = "statestore"; + private static final Logger log = LoggerFactory.getLogger(OrderProcessingServiceApplication.class); -DaprClient client = new DaprClientBuilder().build(); -List> operationList = new ArrayList<>(); -operationList.add(new TransactionalStateOperation<>(TransactionalStateOperation.OperationType.UPSERT, - new State<>("order_3", Integer.toString(orderId), ""))); -operationList.add(new TransactionalStateOperation<>(TransactionalStateOperation.OperationType.DELETE, - new State<>("order_2"))); -client.executeStateTransaction(STATE_STORE_NAME, operationList).block(); + public static void main(String[] args) throws InterruptedException{ + String STATE_STORE_NAME = "statestore"; + + //Calling service multiple times with 5 seconds gap in between the calls + while(true) { + TimeUnit.MILLISECONDS.sleep(5000); + Random random = new Random(); + int orderId = random.nextInt(1000-1) + 1; + + //Using Dapr SDK to perform the state transactions + DaprClient client = new DaprClientBuilder().build(); + List> operationList = new ArrayList<>(); + operationList.add(new TransactionalStateOperation<>(TransactionalStateOperation.OperationType.UPSERT, + new State<>("order_3", Integer.toString(orderId), ""))); + operationList.add(new TransactionalStateOperation<>(TransactionalStateOperation.OperationType.DELETE, + new State<>("order_2"))); + client.executeStateTransaction(STATE_STORE_NAME, operationList).block(); + } + } +} ``` @@ -647,28 +889,40 @@ dapr run --app-id orderprocessing --app-port 6001 --dapr-http-port 3601 --dapr-g {{% codetab %}} ```python -#dependencies +#dependencies +import random +from time import sleep +import requests +import logging from dapr.clients import DaprClient from dapr.clients.grpc._state import StateItem from dapr.clients.grpc._request import TransactionalStateOperation, TransactionOperationType #code - +logging.basicConfig(level = logging.INFO) + DAPR_STORE_NAME = "statestore" -with DaprClient() as client: - client.execute_state_transaction(store_name=DAPR_STORE_NAME, operations=[ - TransactionalStateOperation( - operation_type=TransactionOperationType.upsert, - key="order_3", - data=str(orderId)), - TransactionalStateOperation(key="order_3", data=str(orderId)), - TransactionalStateOperation( - operation_type=TransactionOperationType.delete, - key="order_2", - data=str(orderId)), - TransactionalStateOperation(key="order_2", data=str(orderId)) - ]) + +#Calling service multiple times with 5 seconds gap in between the calls +while True: + sleep(random.randrange(50, 5000) / 1000) + orderId = random.randint(1, 1000) + + #Using Dapr SDK to perform the state transactions + with DaprClient() as client: + client.execute_state_transaction(store_name=DAPR_STORE_NAME, operations=[ + TransactionalStateOperation( + operation_type=TransactionOperationType.upsert, + key="order_3", + data=str(orderId)), + TransactionalStateOperation(key="order_3", data=str(orderId)), + TransactionalStateOperation( + operation_type=TransactionOperationType.delete, + key="order_2", + data=str(orderId)), + TransactionalStateOperation(key="order_2", data=str(orderId)) + ]) ``` @@ -684,30 +938,46 @@ dapr run --app-id orderprocessing --app-port 6001 --dapr-http-port 3601 --dapr-g {{% codetab %}} ```javascript -//dependencies +//dependencies import { DaprClient, HttpMethod, CommunicationProtocolEnum } from 'dapr-client'; - //code +const daprHost = "127.0.0.1"; -const STATE_STORE_NAME = "statestore"; -const client = new DaprClient(daprHost, process.env.DAPR_HTTP_PORT, CommunicationProtocolEnum.HTTP); -await client.state.transaction(STATE_STORE_NAME, [ - { - operation: "upsert", - request: { - key: "order_3", - value: orderId.toString() +var main = function() { + const STATE_STORE_NAME = "statestore"; + + //Calling service multiple times with 5 seconds gap in between the calls + for(var i=0;i<10;i++) { + sleep(5000); + var orderId = Math.floor(Math.random() * (1000 - 1) + 1); + + //Using Dapr SDK to save and retrieve multiple states + const client = new DaprClient(daprHost, process.env.DAPR_HTTP_PORT, CommunicationProtocolEnum.HTTP); + await client.state.transaction(STATE_STORE_NAME, [ + { + operation: "upsert", + request: { + key: "order_3", + value: orderId.toString() + } + }, + { + operation: "delete", + request: { + key: "order_2" + } + } + ]); } - }, - { - operation: "delete", - request: { - key: "order_2" - } - } -]); +} + +function sleep(ms) { + return new Promise(resolve => setTimeout(resolve, ms)); +} + +main(); ``` diff --git a/daprdocs/content/en/reference/components-reference/supported-pubsub/setup-nats-streaming.md b/daprdocs/content/en/reference/components-reference/supported-pubsub/setup-nats-streaming.md index 38c281b4f..e9f7f4079 100644 --- a/daprdocs/content/en/reference/components-reference/supported-pubsub/setup-nats-streaming.md +++ b/daprdocs/content/en/reference/components-reference/supported-pubsub/setup-nats-streaming.md @@ -92,7 +92,7 @@ You can then interact with the server using the client port: `localhost:4222`. {{% /codetab %}} {{% codetab %}} -Install NATS on Kubernetes by using the [kubectl](https://docs.nats.io/nats-on-kubernetes/minimal-setup): +Install NATS on Kubernetes by using the [kubectl](https://docs.nats.io/running-a-nats-service/introduction/running/nats-kubernetes/minimal-setup#minimal-nats-setup): ```bash # Single server NATS From b1e0921761eac297dea07bceb88b818a156a60bb Mon Sep 17 00:00:00 2001 From: Amulya Varote Date: Wed, 24 Nov 2021 07:14:06 -0800 Subject: [PATCH 05/20] Modification based on the review comment - 1 --- .../building-blocks/state-management/howto-get-save-state.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/daprdocs/content/en/developing-applications/building-blocks/state-management/howto-get-save-state.md b/daprdocs/content/en/developing-applications/building-blocks/state-management/howto-get-save-state.md index e096b8c18..ab784b559 100644 --- a/daprdocs/content/en/developing-applications/building-blocks/state-management/howto-get-save-state.md +++ b/daprdocs/content/en/developing-applications/building-blocks/state-management/howto-get-save-state.md @@ -21,7 +21,7 @@ In this guide we'll start of with the basics: Using the key/value state API to a ## Example: -The below code examples loosely describe an application that processes orders. In the examples, there is one service - an order processing service. This service has Dapr sidecar and the order processing service uses Dapr to store state in redis state store. +The below code examples loosely describe an application that processes orders. In the examples, there is an order processing service which has a Dapr sidecar. The order processing service uses Dapr to store state in a Redis state store. Diagram showing state management of example service From 10853723253f65fa87e44ef0ecf21d7ddcf3f27e Mon Sep 17 00:00:00 2001 From: Amulya Varote Date: Wed, 24 Nov 2021 07:21:07 -0800 Subject: [PATCH 06/20] Fixed external validation checks --- .../components-reference/supported-pubsub/setup-jetstream.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/daprdocs/content/en/reference/components-reference/supported-pubsub/setup-jetstream.md b/daprdocs/content/en/reference/components-reference/supported-pubsub/setup-jetstream.md index 61e4711cb..791a2f02b 100644 --- a/daprdocs/content/en/reference/components-reference/supported-pubsub/setup-jetstream.md +++ b/daprdocs/content/en/reference/components-reference/supported-pubsub/setup-jetstream.md @@ -86,7 +86,7 @@ with NATS, find the service with: `kubectl get svc my-nats`. - [Basic schema for a Dapr component]({{< ref component-schema >}}) - Read [this guide]({{< ref "howto-publish-subscribe.md#step-2-publish-a-topic" >}}) for instructions on configuring pub/sub components - [Pub/Sub building block]({{< ref pubsub >}}) -- [JetStream Documentation](https://docs.nats.io/jetstream/jetstream) +- [JetStream Documentation](https://docs.nats.io/nats-concepts/jetstream) - [NATS CLI](https://github.com/nats-io/natscli) From 2367621c8529abcc6b13c086fcbcf136ec9f7954 Mon Sep 17 00:00:00 2001 From: greenie-msft <56556602+greenie-msft@users.noreply.github.com> Date: Wed, 24 Nov 2021 15:41:43 -0800 Subject: [PATCH 07/20] Update setup-nats-streaming.md --- .../supported-pubsub/setup-nats-streaming.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/daprdocs/content/en/reference/components-reference/supported-pubsub/setup-nats-streaming.md b/daprdocs/content/en/reference/components-reference/supported-pubsub/setup-nats-streaming.md index 38c281b4f..e9f7f4079 100644 --- a/daprdocs/content/en/reference/components-reference/supported-pubsub/setup-nats-streaming.md +++ b/daprdocs/content/en/reference/components-reference/supported-pubsub/setup-nats-streaming.md @@ -92,7 +92,7 @@ You can then interact with the server using the client port: `localhost:4222`. {{% /codetab %}} {{% codetab %}} -Install NATS on Kubernetes by using the [kubectl](https://docs.nats.io/nats-on-kubernetes/minimal-setup): +Install NATS on Kubernetes by using the [kubectl](https://docs.nats.io/running-a-nats-service/introduction/running/nats-kubernetes/minimal-setup#minimal-nats-setup): ```bash # Single server NATS From 3f307294574d07dee3af4e52342d049c089a4806 Mon Sep 17 00:00:00 2001 From: Massimo Crippa Date: Fri, 26 Nov 2021 16:43:45 +0100 Subject: [PATCH 08/20] fix: small typo replace semicolons with colons there are a couple of semicolons that should be colons --- .../en/operations/configuration/configuration-overview.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/daprdocs/content/en/operations/configuration/configuration-overview.md b/daprdocs/content/en/operations/configuration/configuration-overview.md index a9b941a30..55f5742a1 100644 --- a/daprdocs/content/en/operations/configuration/configuration-overview.md +++ b/daprdocs/content/en/operations/configuration/configuration-overview.md @@ -16,7 +16,7 @@ In self hosted mode the Dapr configuration is a configuration file, for example A Dapr sidecar can also apply a configuration by using a ```--config``` flag to the file path with ```dapr run``` CLI command. #### Kubernetes sidecar -In Kubernetes mode the Dapr configuration is a Configuration CRD, that is applied to the cluster. For example; +In Kubernetes mode the Dapr configuration is a Configuration CRD, that is applied to the cluster. For example: ```bash kubectl apply -f myappconfig.yaml @@ -41,7 +41,7 @@ Note: There are more [Kubernetes annotations]({{< ref "arguments-annotations-ove ### Sidecar configuration settings -The following configuration settings can be applied to Dapr application sidecars; +The following configuration settings can be applied to Dapr application sidecars: - [Tracing](#tracing) - [Metrics](#metrics) - [Middleware](#middleware) From bdba7a680063296481cee81d9babde0eeb3d0e7c Mon Sep 17 00:00:00 2001 From: Amulya Varote Date: Mon, 29 Nov 2021 12:39:52 -0800 Subject: [PATCH 09/20] Changes based on review comments - 2 --- .../howto-invoke-discover-services.md | 159 ++---- .../state-management/howto-get-save-state.md | 465 ++++++------------ .../state-management/state-store-ttl.md | 2 +- 3 files changed, 196 insertions(+), 430 deletions(-) diff --git a/daprdocs/content/en/developing-applications/building-blocks/service-invocation/howto-invoke-discover-services.md b/daprdocs/content/en/developing-applications/building-blocks/service-invocation/howto-invoke-discover-services.md index a902fe3de..d7ec89069 100644 --- a/daprdocs/content/en/developing-applications/building-blocks/service-invocation/howto-invoke-discover-services.md +++ b/daprdocs/content/en/developing-applications/building-blocks/service-invocation/howto-invoke-discover-services.md @@ -185,14 +185,7 @@ Below are code examples that leverage Dapr SDKs for service invocation. ```csharp //dependencies -using System; -using System.Collections.Generic; -using System.Net.Http; -using System.Net.Http.Headers; -using System.Threading.Tasks; using Dapr.Client; -using Microsoft.AspNetCore.Mvc; -using System.Threading; //code namespace EventService @@ -201,22 +194,13 @@ namespace EventService { static async Task Main(string[] args) { - //Calling service multiple times with 5 seconds gap in between the calls - while(true) { - System.Threading.Thread.Sleep(5000); - Random random = new Random(); - int orderId = random.Next(1,1000); - - CancellationTokenSource source = new CancellationTokenSource(); - CancellationToken cancellationToken = source.Token; - //Using Dapr SDK to invoke a method - using var client = new DaprClientBuilder().Build(); - var result = client.CreateInvokeMethodRequest(HttpMethod.Get, "checkout", "checkout/" + orderId, cancellationToken); - await client.InvokeMethodAsync(result); - - Console.WriteLine("Order requested: " + orderId); - Console.WriteLine("Result: " + result); - } + int orderId = 100; + CancellationTokenSource source = new CancellationTokenSource(); + CancellationToken cancellationToken = source.Token; + //Using Dapr SDK to invoke a method + using var client = new DaprClientBuilder().Build(); + var result = client.CreateInvokeMethodRequest(HttpMethod.Get, "checkout", "checkout/" + orderId, cancellationToken); + await client.InvokeMethodAsync(result); } } } @@ -232,40 +216,21 @@ namespace EventService import io.dapr.client.DaprClient; import io.dapr.client.DaprClientBuilder; import io.dapr.client.domain.HttpExtension; -import org.springframework.boot.autoconfigure.SpringBootApplication; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import java.util.Random; -import java.util.concurrent.TimeUnit; //code @SpringBootApplication public class OrderProcessingServiceApplication { - - private static final Logger log = LoggerFactory.getLogger(OrderProcessingServiceApplication.class); - - public static void main(String[] args) throws InterruptedException{ - - //Calling service multiple times with 5 seconds gap in between the calls - while(true) { - TimeUnit.MILLISECONDS.sleep(5000); - Random random = new Random(); - int orderId = random.nextInt(1000-1) + 1; - - //Using Dapr SDK to invoke a method - DaprClient client = new DaprClientBuilder().build(); - var result = client.invokeMethod( - "checkout", - "checkout/" + orderId, - null, - HttpExtension.GET, - String.class - ); - - log.info("Order requested: " + orderId); - log.info("Result: " + result); - } + public static void main(String[] args) throws InterruptedException { + int orderId = 100; + //Using Dapr SDK to invoke a method + DaprClient client = new DaprClientBuilder().build(); + var result = client.invokeMethod( + "checkout", + "checkout/" + orderId, + null, + HttpExtension.GET, + String.class + ); } } @@ -276,31 +241,18 @@ public class OrderProcessingServiceApplication { ```python #dependencies -import random -from time import sleep -import requests -import logging from dapr.clients import DaprClient #code -logging.basicConfig(level = logging.INFO) - -#Calling service multiple times with 5 seconds gap in between the calls -while True: - sleep(random.randrange(50, 5000) / 1000) - orderId = random.randint(1, 1000) - - #Using Dapr SDK to invoke a method - with DaprClient() as client: - result = client.invoke_method( - "checkout", - f"checkout/{orderId}", - data=b'', - http_verb="GET" - ) - logging.basicConfig(level = logging.INFO) - logging.info('Order requested: ' + str(orderId)) - logging.info('Result: ' + str(result)) +orderId = 100 +#Using Dapr SDK to invoke a method +with DaprClient() as client: + result = client.invoke_method( + "checkout", + f"checkout/{orderId}", + data=b'', + http_verb="GET" + ) ``` {{% /codetab %}} @@ -310,10 +262,6 @@ while True: //dependencies import ( - "context" - "log" - "math/rand" - "time" "strconv" dapr "github.com/dapr/go-sdk/client" @@ -321,25 +269,15 @@ import ( //code func main() { - - //Calling service multiple times with 5 seconds gap in between the calls - for i := 0; i < 10; i++ { - time.Sleep(5000) - orderId := rand.Intn(1000-1) + 1 - - //Using Dapr SDK to invoke a method - client, err := dapr.NewClient() - if err != nil { - panic(err) - } - defer client.Close() - ctx := context.Background() - result, err := client.InvokeMethod(ctx, "checkout", "checkout/" + strconv.Itoa(orderId), "get") - - log.Println("Order requested: " + strconv.Itoa(orderId)) - log.Println("Result: ") - log.Println(result) - } + orderId := 100 + //Using Dapr SDK to invoke a method + client, err := dapr.NewClient() + if err != nil { + panic(err) + } + defer client.Close() + ctx := context.Background() + result, err := client.InvokeMethod(ctx, "checkout", "checkout/" + strconv.Itoa(orderId), "get") } ``` @@ -351,27 +289,14 @@ func main() { //dependencies import { DaprClient, HttpMethod, CommunicationProtocolEnum } from 'dapr-client'; -const daprHost = "127.0.0.1"; - //code +const daprHost = "127.0.0.1"; + var main = function() { - - //Calling service multiple times with 5 seconds gap in between the calls - for(var i=0;i<10;i++) { - sleep(5000); - var orderId = Math.floor(Math.random() * (1000 - 1) + 1); - - //Using Dapr SDK to invoke a method - const client = new DaprClient(daprHost, process.env.DAPR_HTTP_PORT, CommunicationProtocolEnum.HTTP); - const result = await client.invoker.invoke('checkout' , "checkout/" + orderId , HttpMethod.GET); - - console.log("Order requested: " + orderId); - console.log("Result: " + result); - } -} - -function sleep(ms) { - return new Promise(resolve => setTimeout(resolve, ms)); + var orderId = 100; + //Using Dapr SDK to invoke a method + const client = new DaprClient(daprHost, process.env.DAPR_HTTP_PORT, CommunicationProtocolEnum.HTTP); + const result = await client.invoker.invoke('checkout' , "checkout/" + orderId , HttpMethod.GET); } main(); diff --git a/daprdocs/content/en/developing-applications/building-blocks/state-management/howto-get-save-state.md b/daprdocs/content/en/developing-applications/building-blocks/state-management/howto-get-save-state.md index ab784b559..89839db12 100644 --- a/daprdocs/content/en/developing-applications/building-blocks/state-management/howto-get-save-state.md +++ b/daprdocs/content/en/developing-applications/building-blocks/state-management/howto-get-save-state.md @@ -81,15 +81,7 @@ Below are code examples that leverage Dapr SDKs for saving and retrieving a sing ```csharp //dependencies -using System; -using System.Collections.Generic; -using System.Net.Http; -using System.Net.Http.Headers; -using System.Threading.Tasks; using Dapr.Client; -using Microsoft.AspNetCore.Mvc; -using System.Threading; -using System.Text.Json; //code namespace EventService @@ -100,26 +92,19 @@ namespace EventService { string DAPR_STORE_NAME = "statestore"; - //Calling service multiple times with 5 seconds gap in between the calls - while(true) { - System.Threading.Thread.Sleep(5000); - Random random = new Random(); - int orderId = random.Next(1,1000); - - //Using Dapr SDK to save and get state - using var client = new DaprClientBuilder().Build(); - await client.SaveStateAsync(DAPR_STORE_NAME, "order_1", orderId.ToString()); - await client.SaveStateAsync(DAPR_STORE_NAME, "order_2", orderId.ToString()); - var result = await client.GetStateAsync(DAPR_STORE_NAME, orderId.ToString()); - Console.WriteLine("Result after get: " + result); - } + int orderId = 100; + //Using Dapr SDK to save and get state + using var client = new DaprClientBuilder().Build(); + await client.SaveStateAsync(DAPR_STORE_NAME, "order_1", orderId.ToString()); + await client.SaveStateAsync(DAPR_STORE_NAME, "order_2", orderId.ToString()); + var result = await client.GetStateAsync(DAPR_STORE_NAME, orderId.ToString()); } } } ``` -Once saved run the following command to launch a Dapr sidecar and run the application: +Navigate to the directory containing the above code, then run the following command to launch a Dapr sidecar and run the application: ```bash dapr run --app-id orderprocessing --app-port 6001 --dapr-http-port 3601 --dapr-grpc-port 60001 dotnet run @@ -136,13 +121,7 @@ dapr run --app-id orderprocessing --app-port 6001 --dapr-http-port 3601 --dapr-g import io.dapr.client.DaprClient; import io.dapr.client.DaprClientBuilder; import io.dapr.client.domain.State; -import io.dapr.client.domain.TransactionalStateOperation; -import org.springframework.boot.autoconfigure.SpringBootApplication; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; import reactor.core.publisher.Mono; -import java.util.Random; -import java.util.concurrent.TimeUnit; //code @SpringBootApplication @@ -150,28 +129,21 @@ public class OrderProcessingServiceApplication { private static final Logger log = LoggerFactory.getLogger(OrderProcessingServiceApplication.class); - public static void main(String[] args) throws InterruptedException{ + public static void main(String[] args) throws InterruptedException { String STATE_STORE_NAME = "statestore"; - //Calling service multiple times with 5 seconds gap in between the calls - while(true) { - TimeUnit.MILLISECONDS.sleep(5000); - Random random = new Random(); - int orderId = random.nextInt(1000-1) + 1; - - //Using Dapr SDK to save and get state - DaprClient client = new DaprClientBuilder().build(); - client.saveState(STATE_STORE_NAME, "order_1", Integer.toString(orderId)).block(); - client.saveState(STATE_STORE_NAME, "order_2", Integer.toString(orderId)).block(); - Mono> result = client.getState(STATE_STORE_NAME, "order_1", String.class); - log.info("Result after get" + result); - } + int orderId = 100; + //Using Dapr SDK to save and get state + DaprClient client = new DaprClientBuilder().build(); + client.saveState(STATE_STORE_NAME, "order_1", Integer.toString(orderId)).block(); + client.saveState(STATE_STORE_NAME, "order_2", Integer.toString(orderId)).block(); + Mono> result = client.getState(STATE_STORE_NAME, "order_1", String.class); } } ``` -Once saved run the following command to launch a Dapr sidecar and run the application: +Navigate to the directory containing the above code, then run the following command to launch a Dapr sidecar and run the application: ```bash dapr run --app-id orderprocessing --app-port 6001 --dapr-http-port 3601 --dapr-grpc-port 60001 mvn spring-boot:run @@ -185,33 +157,23 @@ dapr run --app-id orderprocessing --app-port 6001 --dapr-http-port 3601 --dapr-g ```python #dependencies -import random -from time import sleep -import requests -import logging from dapr.clients import DaprClient -from dapr.clients.grpc._state import StateItem -from dapr.clients.grpc._request import TransactionalStateOperation, TransactionOperationType #code logging.basicConfig(level = logging.INFO) DAPR_STORE_NAME = "statestore" -#Calling service multiple times with 5 seconds gap in between the calls -while True: - sleep(random.randrange(50, 5000) / 1000) - orderId = random.randint(1, 1000) - - #Using Dapr SDK to save and get state - with DaprClient() as client: - client.save_state(DAPR_STORE_NAME, "order_1", str(orderId)) - result = client.get_state(DAPR_STORE_NAME, "order_1") - logging.info('Result after get: ' + str(result)) +orderId = 100 +#Using Dapr SDK to save and get state +with DaprClient() as client: + client.save_state(DAPR_STORE_NAME, "order_1", str(orderId)) + result = client.get_state(DAPR_STORE_NAME, "order_1") + logging.info('Result after get: ' + str(result)) ``` -Once saved run the following command to launch a Dapr sidecar and run the application: +Navigate to the directory containing the above code, then run the following command to launch a Dapr sidecar and run the application: ```bash dapr run --app-id orderprocessing --app-port 6001 --dapr-http-port 3601 --dapr-grpc-port 60001 -- python3 OrderProcessingService.py @@ -240,36 +202,29 @@ func main() { STATE_STORE_NAME := "statestore" - //Calling service multiple times with 5 seconds gap in between the calls - for i := 0; i < 10; i++ { - time.Sleep(5000) - orderId := rand.Intn(1000-1) + 1 + orderId := 100 - //Using Dapr SDK to save and get state - client, err := dapr.NewClient() - if err != nil { - panic(err) - } - defer client.Close() - ctx := context.Background() + //Using Dapr SDK to save and get state + client, err := dapr.NewClient() + if err != nil { + panic(err) + } + defer client.Close() + ctx := context.Background() - if err := client.SaveState(ctx, STATE_STORE_NAME, "order_1", []byte(strconv.Itoa(orderId))); err != nil { - panic(err) - } - - result, err := client.GetState(ctx, STATE_STORE_NAME, "order_1") - if err != nil { - panic(err) - } - - log.Println("Result after get: ") - log.Println(result) - } + if err := client.SaveState(ctx, STATE_STORE_NAME, "order_1", []byte(strconv.Itoa(orderId))); err != nil { + panic(err) + } + + result, err := client.GetState(ctx, STATE_STORE_NAME, "order_1") + if err != nil { + panic(err) + } } ``` -Once saved run the following command to launch a Dapr sidecar and run the application: +Navigate to the directory containing the above code, then run the following command to launch a Dapr sidecar and run the application: ```bash dapr run --app-id orderprocessing --app-port 6001 --dapr-http-port 3601 --dapr-grpc-port 60001 go run OrderProcessingService.go @@ -291,37 +246,27 @@ const daprHost = "127.0.0.1"; var main = function() { const STATE_STORE_NAME = "statestore"; - //Calling service multiple times with 5 seconds gap in between the calls - for(var i=0;i<10;i++) { - sleep(5000); - var orderId = Math.floor(Math.random() * (1000 - 1) + 1); - - //Using Dapr SDK to save and get state - const client = new DaprClient(daprHost, process.env.DAPR_HTTP_PORT, CommunicationProtocolEnum.HTTP); - await client.state.save(STATE_STORE_NAME, [ - { - key: "order_1", - value: orderId.toString() - }, - { - key: "order_2", - value: orderId.toString() - } - ]); - var result = await client.state.get(STATE_STORE_NAME, "order_1"); - console.log("Result after get: " + result); - } -} - -function sleep(ms) { - return new Promise(resolve => setTimeout(resolve, ms)); + var orderId = 100; + //Using Dapr SDK to save and get state + const client = new DaprClient(daprHost, process.env.DAPR_HTTP_PORT, CommunicationProtocolEnum.HTTP); + await client.state.save(STATE_STORE_NAME, [ + { + key: "order_1", + value: orderId.toString() + }, + { + key: "order_2", + value: orderId.toString() + } + ]); + var result = await client.state.get(STATE_STORE_NAME, "order_1"); } main(); ``` -Once saved run the following command to launch a Dapr sidecar and run the application: +Navigate to the directory containing the above code, then run the following command to launch a Dapr sidecar and run the application: ```bash dapr run --app-id orderprocessing --app-port 6001 --dapr-http-port 3601 --dapr-grpc-port 60001 npm start @@ -347,7 +292,7 @@ Now get the state you just saved: curl http://localhost:3601/v1.0/state/statestore/order_1 ``` -You can also restart your sidecar and try retrieving state again to see that state persists separate from the app. +Restart your sidecar and try retrieving state again to observe that state persists separately from the app. {{% /codetab %}} {{% codetab %}} @@ -368,7 +313,7 @@ Now get the state you just saved: Invoke-RestMethod -Uri 'http://localhost:3601/v1.0/state/statestore/order_1' ``` -You can also restart your sidecar and try retrieving state again to see that state persists separate from the app. +Restart your sidecar and try retrieving state again to observe that state persists separately from the app. {{% /codetab %}} @@ -386,15 +331,7 @@ Below are code examples that leverage Dapr SDKs for deleting the state. ```csharp //dependencies -using System; -using System.Collections.Generic; -using System.Net.Http; -using System.Net.Http.Headers; -using System.Threading.Tasks; using Dapr.Client; -using Microsoft.AspNetCore.Mvc; -using System.Threading; -using System.Text.Json; //code namespace EventService @@ -413,7 +350,7 @@ namespace EventService ``` -Once saved run the following command to launch a Dapr sidecar and run the application: +Navigate to the directory containing the above code, then run the following command to launch a Dapr sidecar and run the application: ```bash dapr run --app-id orderprocessing --app-port 6001 --dapr-http-port 3601 --dapr-grpc-port 60001 dotnet run @@ -429,20 +366,10 @@ dapr run --app-id orderprocessing --app-port 6001 --dapr-http-port 3601 --dapr-g //dependencies import io.dapr.client.DaprClient; import io.dapr.client.DaprClientBuilder; -import io.dapr.client.domain.State; -import io.dapr.client.domain.TransactionalStateOperation; -import org.springframework.boot.autoconfigure.SpringBootApplication; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import java.util.Random; -import java.util.concurrent.TimeUnit; //code @SpringBootApplication public class OrderProcessingServiceApplication { - - private static final Logger log = LoggerFactory.getLogger(OrderProcessingServiceApplication.class); - public static void main(String[] args) throws InterruptedException{ String STATE_STORE_NAME = "statestore"; @@ -455,7 +382,7 @@ public class OrderProcessingServiceApplication { ``` -Once saved run the following command to launch a Dapr sidecar and run the application: +Navigate to the directory containing the above code, then run the following command to launch a Dapr sidecar and run the application: ```bash dapr run --app-id orderprocessing --app-port 6001 --dapr-http-port 3601 --dapr-grpc-port 60001 mvn spring-boot:run @@ -469,12 +396,6 @@ dapr run --app-id orderprocessing --app-port 6001 --dapr-http-port 3601 --dapr-g ```python #dependencies -import random -from time import sleep -import requests -import logging -from dapr.clients import DaprClient -from dapr.clients.grpc._state import StateItem from dapr.clients.grpc._request import TransactionalStateOperation, TransactionOperationType #code @@ -488,7 +409,7 @@ with DaprClient() as client: ``` -Once saved run the following command to launch a Dapr sidecar and run the application: +Navigate to the directory containing the above code, then run the following command to launch a Dapr sidecar and run the application: ```bash dapr run --app-id orderprocessing --app-port 6001 --dapr-http-port 3601 --dapr-grpc-port 60001 -- python3 OrderProcessingService.py @@ -528,7 +449,7 @@ func main() { ``` -Once saved run the following command to launch a Dapr sidecar and run the application: +Navigate to the directory containing the above code, then run the following command to launch a Dapr sidecar and run the application: ```bash dapr run --app-id orderprocessing --app-port 6001 --dapr-http-port 3601 --dapr-grpc-port 60001 go run OrderProcessingService.go @@ -555,15 +476,11 @@ var main = function() { await client.state.delete(STATE_STORE_NAME, "order_1"); } -function sleep(ms) { - return new Promise(resolve => setTimeout(resolve, ms)); -} - main(); ``` -Once saved run the following command to launch a Dapr sidecar and run the application: +Navigate to the directory containing the above code, then run the following command to launch a Dapr sidecar and run the application: ```bash dapr run --app-id orderprocessing --app-port 6001 --dapr-http-port 3601 --dapr-grpc-port 60001 npm start @@ -572,7 +489,7 @@ dapr run --app-id orderprocessing --app-port 6001 --dapr-http-port 3601 --dapr-g {{% /codetab %}} {{% codetab %}} -With the same dapr instance running from above run: +With the same Dapr instance running from above run: ```bash curl -X DELETE 'http://localhost:3601/v1.0/state/statestore/order_1' ``` @@ -580,7 +497,7 @@ Try getting state again and note that no value is returned. {{% /codetab %}} {{% codetab %}} -With the same dapr instance running from above run: +With the same Dapr instance running from above run: ```powershell Invoke-RestMethod -Method Delete -Uri 'http://localhost:3601/v1.0/state/statestore/order_1' ``` @@ -603,13 +520,6 @@ Below are code examples that leverage Dapr SDKs for saving and retrieving multip import io.dapr.client.DaprClient; import io.dapr.client.DaprClientBuilder; import io.dapr.client.domain.State; -import io.dapr.client.domain.TransactionalStateOperation; -import org.springframework.boot.autoconfigure.SpringBootApplication; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import reactor.core.publisher.Mono; -import java.util.Random; -import java.util.concurrent.TimeUnit; //code @SpringBootApplication @@ -620,23 +530,17 @@ public class OrderProcessingServiceApplication { public static void main(String[] args) throws InterruptedException{ String STATE_STORE_NAME = "statestore"; - //Calling service multiple times with 5 seconds gap in between the calls - while(true) { - TimeUnit.MILLISECONDS.sleep(5000); - Random random = new Random(); - int orderId = random.nextInt(1000-1) + 1; - - //Using Dapr SDK to retrieve multiple states - DaprClient client = new DaprClientBuilder().build(); - Mono>> resultBulk = client.getBulkState(STATE_STORE_NAME, - Arrays.asList("order_1", "order_2"), String.class); - } + int orderId = 100; + //Using Dapr SDK to retrieve multiple states + DaprClient client = new DaprClientBuilder().build(); + Mono>> resultBulk = client.getBulkState(STATE_STORE_NAME, + Arrays.asList("order_1", "order_2"), String.class); } } ``` -Once saved run the following command to launch a Dapr sidecar and run the application: +Navigate to the directory containing the above code, then run the following command to launch a Dapr sidecar and run the application: ```bash dapr run --app-id orderprocessing --app-port 6001 --dapr-http-port 3601 --dapr-grpc-port 60001 mvn spring-boot:run @@ -650,33 +554,24 @@ dapr run --app-id orderprocessing --app-port 6001 --dapr-http-port 3601 --dapr-g ```python #dependencies -import random -from time import sleep -import requests -import logging from dapr.clients import DaprClient from dapr.clients.grpc._state import StateItem -from dapr.clients.grpc._request import TransactionalStateOperation, TransactionOperationType #code logging.basicConfig(level = logging.INFO) DAPR_STORE_NAME = "statestore" -#Calling service multiple times with 5 seconds gap in between the calls -while True: - sleep(random.randrange(50, 5000) / 1000) - orderId = random.randint(1, 1000) - - #Using Dapr SDK to save and retrieve multiple states - with DaprClient() as client: - client.save_bulk_state(store_name=DAPR_STORE_NAME, states=[StateItem(key="order_2", value=str(orderId))]) - result = client.get_bulk_state(store_name=DAPR_STORE_NAME, keys=["order_1", "order_2"], states_metadata={"metakey": "metavalue"}).items - logging.info('Result after get bulk: ' + str(result)) +orderId = 100 +#Using Dapr SDK to save and retrieve multiple states +with DaprClient() as client: + client.save_bulk_state(store_name=DAPR_STORE_NAME, states=[StateItem(key="order_2", value=str(orderId))]) + result = client.get_bulk_state(store_name=DAPR_STORE_NAME, keys=["order_1", "order_2"], states_metadata={"metakey": "metavalue"}).items + logging.info('Result after get bulk: ' + str(result)) ``` -Once saved run the following command to launch a Dapr sidecar and run the application: +Navigate to the directory containing the above code, then run the following command to launch a Dapr sidecar and run the application: ```bash dapr run --app-id orderprocessing --app-port 6001 --dapr-http-port 3601 --dapr-grpc-port 60001 -- python3 OrderProcessingService.py @@ -699,37 +594,27 @@ const daprHost = "127.0.0.1"; var main = function() { const STATE_STORE_NAME = "statestore"; - //Calling service multiple times with 5 seconds gap in between the calls - for(var i=0;i<10;i++) { - sleep(5000); - var orderId = Math.floor(Math.random() * (1000 - 1) + 1); - - //Using Dapr SDK to save and retrieve multiple states - const client = new DaprClient(daprHost, process.env.DAPR_HTTP_PORT, CommunicationProtocolEnum.HTTP); - await client.state.save(STATE_STORE_NAME, [ - { - key: "order_1", - value: orderId.toString() - }, - { - key: "order_2", - value: orderId.toString() - } - ]); - result = await client.state.getBulk(STATE_STORE_NAME, ["order_1", "order_2"]); - console.log("Result after get bulk: " + result); - } -} - -function sleep(ms) { - return new Promise(resolve => setTimeout(resolve, ms)); + var orderId = 100; + //Using Dapr SDK to save and retrieve multiple states + const client = new DaprClient(daprHost, process.env.DAPR_HTTP_PORT, CommunicationProtocolEnum.HTTP); + await client.state.save(STATE_STORE_NAME, [ + { + key: "order_1", + value: orderId.toString() + }, + { + key: "order_2", + value: orderId.toString() + } + ]); + result = await client.state.getBulk(STATE_STORE_NAME, ["order_1", "order_2"]); } main(); ``` -Once saved run the following command to launch a Dapr sidecar and run the application: +Navigate to the directory containing the above code, then run the following command to launch a Dapr sidecar and run the application: ```bash dapr run --app-id orderprocessing --app-port 6001 --dapr-http-port 3601 --dapr-grpc-port 60001 npm start @@ -738,7 +623,7 @@ dapr run --app-id orderprocessing --app-port 6001 --dapr-http-port 3601 --dapr-g {{% /codetab %}} {{% codetab %}} -With the same dapr instance running from above save two key/value pairs into your statestore: +With the same Dapr instance running from above save two key/value pairs into your statestore: ```bash curl -X POST -H "Content-Type: application/json" -d '[{ "key": "order_1", "value": "250"}, { "key": "order_2", "value": "550"}]' http://localhost:3601/v1.0/state/statestore ``` @@ -750,7 +635,7 @@ curl -X POST -H "Content-Type: application/json" -d '{"keys":["order_1", "order_ {{% /codetab %}} {{% codetab %}} -With the same dapr instance running from above save two key/value pairs into your statestore: +With the same Dapr instance running from above save two key/value pairs into your statestore: ```powershell Invoke-RestMethod -Method Post -ContentType 'application/json' -Body '[{ "key": "order_1", "value": "250"}, { "key": "order_2", "value": "550"}]' -Uri 'http://localhost:3601/v1.0/state/statestore' ``` @@ -779,15 +664,7 @@ Below are code examples that leverage Dapr SDKs for performing state transaction ```csharp //dependencies -using System; -using System.Collections.Generic; -using System.Net.Http; -using System.Net.Http.Headers; -using System.Threading.Tasks; using Dapr.Client; -using Microsoft.AspNetCore.Mvc; -using System.Threading; -using System.Text.Json; //code namespace EventService @@ -797,30 +674,25 @@ namespace EventService static async Task Main(string[] args) { string DAPR_STORE_NAME = "statestore"; - //Calling service multiple times with 5 seconds gap in between the calls - while(true) { - System.Threading.Thread.Sleep(5000); - Random random = new Random(); - int orderId = random.Next(1,1000); - - //Using Dapr SDK to perform the state transactions - using var client = new DaprClientBuilder().Build(); - var requests = new List() - { - new StateTransactionRequest("order_3", JsonSerializer.SerializeToUtf8Bytes(orderId.ToString()), StateOperationType.Upsert), - new StateTransactionRequest("order_2", null, StateOperationType.Delete) - }; - CancellationTokenSource source = new CancellationTokenSource(); - CancellationToken cancellationToken = source.Token; - await client.ExecuteStateTransactionAsync(DAPR_STORE_NAME, requests, cancellationToken: cancellationToken); - } + + int orderId = 100; + //Using Dapr SDK to perform the state transactions + using var client = new DaprClientBuilder().Build(); + var requests = new List() + { + new StateTransactionRequest("order_3", JsonSerializer.SerializeToUtf8Bytes(orderId.ToString()), StateOperationType.Upsert), + new StateTransactionRequest("order_2", null, StateOperationType.Delete) + }; + CancellationTokenSource source = new CancellationTokenSource(); + CancellationToken cancellationToken = source.Token; + await client.ExecuteStateTransactionAsync(DAPR_STORE_NAME, requests, cancellationToken: cancellationToken); } } } ``` -Once saved run the following command to launch a Dapr sidecar and run the application: +Navigate to the directory containing the above code, then run the following command to launch a Dapr sidecar and run the application: ```bash dapr run --app-id orderprocessing --app-port 6001 --dapr-http-port 3601 --dapr-grpc-port 60001 dotnet run @@ -838,15 +710,7 @@ import io.dapr.client.DaprClient; import io.dapr.client.DaprClientBuilder; import io.dapr.client.domain.State; import io.dapr.client.domain.TransactionalStateOperation; -import org.springframework.boot.autoconfigure.SpringBootApplication; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.List; -import reactor.core.publisher.Mono; -import java.util.Random; -import java.util.concurrent.TimeUnit; + //code @SpringBootApplication @@ -857,27 +721,21 @@ public class OrderProcessingServiceApplication { public static void main(String[] args) throws InterruptedException{ String STATE_STORE_NAME = "statestore"; - //Calling service multiple times with 5 seconds gap in between the calls - while(true) { - TimeUnit.MILLISECONDS.sleep(5000); - Random random = new Random(); - int orderId = random.nextInt(1000-1) + 1; - - //Using Dapr SDK to perform the state transactions - DaprClient client = new DaprClientBuilder().build(); - List> operationList = new ArrayList<>(); - operationList.add(new TransactionalStateOperation<>(TransactionalStateOperation.OperationType.UPSERT, - new State<>("order_3", Integer.toString(orderId), ""))); - operationList.add(new TransactionalStateOperation<>(TransactionalStateOperation.OperationType.DELETE, - new State<>("order_2"))); - client.executeStateTransaction(STATE_STORE_NAME, operationList).block(); - } + int orderId = 100; + //Using Dapr SDK to perform the state transactions + DaprClient client = new DaprClientBuilder().build(); + List> operationList = new ArrayList<>(); + operationList.add(new TransactionalStateOperation<>(TransactionalStateOperation.OperationType.UPSERT, + new State<>("order_3", Integer.toString(orderId), ""))); + operationList.add(new TransactionalStateOperation<>(TransactionalStateOperation.OperationType.DELETE, + new State<>("order_2"))); + client.executeStateTransaction(STATE_STORE_NAME, operationList).block(); } } ``` -Once saved run the following command to launch a Dapr sidecar and run the application: +Navigate to the directory containing the above code, then run the following command to launch a Dapr sidecar and run the application: ```bash dapr run --app-id orderprocessing --app-port 6001 --dapr-http-port 3601 --dapr-grpc-port 60001 mvn spring-boot:run @@ -891,10 +749,6 @@ dapr run --app-id orderprocessing --app-port 6001 --dapr-http-port 3601 --dapr-g ```python #dependencies -import random -from time import sleep -import requests -import logging from dapr.clients import DaprClient from dapr.clients.grpc._state import StateItem from dapr.clients.grpc._request import TransactionalStateOperation, TransactionOperationType @@ -904,29 +758,25 @@ logging.basicConfig(level = logging.INFO) DAPR_STORE_NAME = "statestore" -#Calling service multiple times with 5 seconds gap in between the calls -while True: - sleep(random.randrange(50, 5000) / 1000) - orderId = random.randint(1, 1000) - - #Using Dapr SDK to perform the state transactions - with DaprClient() as client: - client.execute_state_transaction(store_name=DAPR_STORE_NAME, operations=[ - TransactionalStateOperation( - operation_type=TransactionOperationType.upsert, - key="order_3", - data=str(orderId)), - TransactionalStateOperation(key="order_3", data=str(orderId)), - TransactionalStateOperation( - operation_type=TransactionOperationType.delete, - key="order_2", - data=str(orderId)), - TransactionalStateOperation(key="order_2", data=str(orderId)) - ]) +orderId = 100 +#Using Dapr SDK to perform the state transactions +with DaprClient() as client: + client.execute_state_transaction(store_name=DAPR_STORE_NAME, operations=[ + TransactionalStateOperation( + operation_type=TransactionOperationType.upsert, + key="order_3", + data=str(orderId)), + TransactionalStateOperation(key="order_3", data=str(orderId)), + TransactionalStateOperation( + operation_type=TransactionOperationType.delete, + key="order_2", + data=str(orderId)), + TransactionalStateOperation(key="order_2", data=str(orderId)) + ]) ``` -Once saved run the following command to launch a Dapr sidecar and run the application: +Navigate to the directory containing the above code, then run the following command to launch a Dapr sidecar and run the application: ```bash dapr run --app-id orderprocessing --app-port 6001 --dapr-http-port 3601 --dapr-grpc-port 60001 -- python3 OrderProcessingService.py @@ -948,40 +798,31 @@ const daprHost = "127.0.0.1"; var main = function() { const STATE_STORE_NAME = "statestore"; - //Calling service multiple times with 5 seconds gap in between the calls - for(var i=0;i<10;i++) { - sleep(5000); - var orderId = Math.floor(Math.random() * (1000 - 1) + 1); - - //Using Dapr SDK to save and retrieve multiple states - const client = new DaprClient(daprHost, process.env.DAPR_HTTP_PORT, CommunicationProtocolEnum.HTTP); - await client.state.transaction(STATE_STORE_NAME, [ - { - operation: "upsert", - request: { - key: "order_3", - value: orderId.toString() - } - }, - { - operation: "delete", - request: { - key: "order_2" - } + var orderId = 100; + //Using Dapr SDK to save and retrieve multiple states + const client = new DaprClient(daprHost, process.env.DAPR_HTTP_PORT, CommunicationProtocolEnum.HTTP); + await client.state.transaction(STATE_STORE_NAME, [ + { + operation: "upsert", + request: { + key: "order_3", + value: orderId.toString() } - ]); - } -} - -function sleep(ms) { - return new Promise(resolve => setTimeout(resolve, ms)); + }, + { + operation: "delete", + request: { + key: "order_2" + } + } + ]); } main(); ``` -Once saved run the following command to launch a Dapr sidecar and run the application: +Navigate to the directory containing the above code, then run the following command to launch a Dapr sidecar and run the application: ```bash dapr run --app-id orderprocessing --app-port 6001 --dapr-http-port 3601 --dapr-grpc-port 60001 npm start @@ -990,7 +831,7 @@ dapr run --app-id orderprocessing --app-port 6001 --dapr-http-port 3601 --dapr-g {{% /codetab %}} {{% codetab %}} -With the same dapr instance running from above perform two state transactions: +With the same Dapr instance running from above perform two state transactions: ```bash curl -X POST -H "Content-Type: application/json" -d '{"operations": [{"operation":"upsert", "request": {"key": "order_1", "value": "250"}}, {"operation":"delete", "request": {"key": "order_2"}}]}' http://localhost:3601/v1.0/state/statestore/transaction ``` @@ -1002,7 +843,7 @@ curl -X POST -H "Content-Type: application/json" -d '{"keys":["order_1", "order_ {{% /codetab %}} {{% codetab %}} -With the same dapr instance running from above save two key/value pairs into your statestore: +With the same Dapr instance running from above save two key/value pairs into your statestore: ```powershell Invoke-RestMethod -Method Post -ContentType 'application/json' -Body '{"operations": [{"operation":"upsert", "request": {"key": "order_1", "value": "250"}}, {"operation":"delete", "request": {"key": "order_2"}}]}' -Uri 'http://localhost:3601/v1.0/state/statestore' ``` diff --git a/daprdocs/content/en/developing-applications/building-blocks/state-management/state-store-ttl.md b/daprdocs/content/en/developing-applications/building-blocks/state-management/state-store-ttl.md index de57e9aa1..22a8e8293 100644 --- a/daprdocs/content/en/developing-applications/building-blocks/state-management/state-store-ttl.md +++ b/daprdocs/content/en/developing-applications/building-blocks/state-management/state-store-ttl.md @@ -51,7 +51,7 @@ with DaprClient() as client: ``` -Once saved run the following command to launch a Dapr sidecar and run the application: +Navigate to the directory containing the above code, then run the following command to launch a Dapr sidecar and run the application: ```bash dapr run --app-id orderprocessing --app-port 6001 --dapr-http-port 3601 --dapr-grpc-port 60001 -- python3 OrderProcessingService.py From e4af246e04f968fb969826d1ff81f8cad5024494 Mon Sep 17 00:00:00 2001 From: Will Tsai Date: Tue, 30 Nov 2021 09:30:11 -0800 Subject: [PATCH 10/20] Deleting redundant page app-configuration-overview.md --- .../app-configuration-overview.md | 18 ------------------ 1 file changed, 18 deletions(-) delete mode 100644 daprdocs/content/en/developing-applications/building-blocks/configuration/app-configuration-overview.md diff --git a/daprdocs/content/en/developing-applications/building-blocks/configuration/app-configuration-overview.md b/daprdocs/content/en/developing-applications/building-blocks/configuration/app-configuration-overview.md deleted file mode 100644 index 5fd34c9bb..000000000 --- a/daprdocs/content/en/developing-applications/building-blocks/configuration/app-configuration-overview.md +++ /dev/null @@ -1,18 +0,0 @@ ---- -type: docs -title: "Configuration overview" -linkTitle: "Configuration overview" -weight: 1000 -description: "Use Dapr to get and watch application configuration" ---- - -Consuming application configuration is a common task when writing applications and frequently configuration stores are used to manage this configuration data. A configuration item is often dynamic in nature and is tightly coupled to the needs of the application that consumes it. For example, common uses for application configuration include names of secrets that need to be retrieved, different identifiers, partition or consumer IDs, names of databased to connect to etc. These configuration items are typically stored as key-value items in a database. - -Dapr provides a [State Management API]({{}}) that is based on key-value stores. However, application configuration can be changed by either developers or operators at runtime and the developer needs to be notified of these changes in order to take the required action and load the new configuration. Also the configuration data may want to be read only. Dapr's Configuration API allows developers to consume configuration items that are returned as key/value pair and subscribe to changes whenever a configuration item changes. - -*This API is currently in `Alpha state` and only available on gRPC. An HTTP1.1 supported version with this URL `/v1.0/configuration` will be available before the API becomes stable.* - -## References - -- [How-To: Manage application configuration]({{< ref howto-manage-configuration.md >}}) - From dc460db372e759cac613cca05dcef5edb0fefdd6 Mon Sep 17 00:00:00 2001 From: Will Tsai Date: Tue, 30 Nov 2021 11:21:25 -0800 Subject: [PATCH 11/20] fix Jetstream broken link --- .../components-reference/supported-pubsub/setup-jetstream.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/daprdocs/content/en/reference/components-reference/supported-pubsub/setup-jetstream.md b/daprdocs/content/en/reference/components-reference/supported-pubsub/setup-jetstream.md index 61e4711cb..791a2f02b 100644 --- a/daprdocs/content/en/reference/components-reference/supported-pubsub/setup-jetstream.md +++ b/daprdocs/content/en/reference/components-reference/supported-pubsub/setup-jetstream.md @@ -86,7 +86,7 @@ with NATS, find the service with: `kubectl get svc my-nats`. - [Basic schema for a Dapr component]({{< ref component-schema >}}) - Read [this guide]({{< ref "howto-publish-subscribe.md#step-2-publish-a-topic" >}}) for instructions on configuring pub/sub components - [Pub/Sub building block]({{< ref pubsub >}}) -- [JetStream Documentation](https://docs.nats.io/jetstream/jetstream) +- [JetStream Documentation](https://docs.nats.io/nats-concepts/jetstream) - [NATS CLI](https://github.com/nats-io/natscli) From dc878f6ca968aba1b44368de0cdc3aa8bcb573c5 Mon Sep 17 00:00:00 2001 From: Bernd Verst <4535280+berndverst@users.noreply.github.com> Date: Tue, 30 Nov 2021 17:08:02 -0800 Subject: [PATCH 12/20] Cosmos DB components production guidance --- .../supported-bindings/cosmosdb.md | 27 ++++++++++++++++++- .../setup-azure-cosmosdb.md | 11 ++++++++ 2 files changed, 37 insertions(+), 1 deletion(-) diff --git a/daprdocs/content/en/reference/components-reference/supported-bindings/cosmosdb.md b/daprdocs/content/en/reference/components-reference/supported-bindings/cosmosdb.md index 163fe8d3d..aaa41d2de 100644 --- a/daprdocs/content/en/reference/components-reference/supported-bindings/cosmosdb.md +++ b/daprdocs/content/en/reference/components-reference/supported-bindings/cosmosdb.md @@ -46,7 +46,7 @@ The above example uses secrets as plain strings. It is recommended to use a secr | masterKey | Y | Output | The CosmosDB account master key | `"master-key"` | | database | Y | Output | The name of the CosmosDB database | `"OrderDb"` | | collection | Y | Output | The name of the container inside the database. | `"Orders"` | -| partitionKey | Y | Output | The name of the partitionKey to extract from the payload and is used in the container | `"OrderId"`, `"message"` | +| partitionKey | Y | Output | The name of the key to extract from the payload (document to be created) that is used as the partition key. This name must match the partition key specified upon creation of the Cosmos DB container. | `"OrderId"`, `"message"` | For more information see [Azure Cosmos DB resource model](https://docs.microsoft.com/azure/cosmos-db/account-databases-containers-items). @@ -56,6 +56,31 @@ This component supports **output binding** with the following operations: - `create` +## Best Practices for Production Use + +Azure Cosmos DB shares a strict metadata request rate limit across all databases in a single Azure Cosmos DB account. New connections to Azure Cosmos DB assume a large percentage of the allowable request rate limit. (See the [CosmosDB documentation](https://docs.microsoft.com/azure/cosmos-db/sql/troubleshoot-request-rate-too-large#recommended-solution-3)) + +Therefore several strategies must be applied to avoid simultaneous new connections to Azure Cosmos DB: + +- Ensure sidecars of applications only load the Azure Cosmos DB component when they require it to avoid unnecessary database connections. This can be done by [scoping your components to specific applications]({{< ref component-scopes.md#application-access-to-components-with-scopes >}}). +- Choose deployment strategies that sequentially deploy or start all applications to minimize bursts in new connections to your Azure Cosmos DB accounts. +- Avoid reusing the same Azure Cosmos DB account for unrelated databases or systems (even outside of Dapr). Distinct Azure Cosmos DB accounts have distinct rate limits. +- Increase the `initTimeout` value to allow the component to retry connecting to Azure Cosmos DB during side car initialization for up to 5 minutes. The default value is `5s` and should be increased. Using Kubernetes increasing this value may also require an update to your [Readiness and Liveness probes](https://kubernetes.io/docs/tasks/configure-pod-container/configure-liveness-readiness-startup-probes/). + +```yaml +spec: + type: bindings.azure.cosmosdb + version: v1 + initTimeout: 5m + metadata: +``` + +## Data format + +The **output binding** `create` operation requires the following keys to exist in the payload of every document to be created: +- `id`: a unique ID for the document to be created +- ``: the name of the partition key specified via the `spec.partitionKey` in the component definition. This must also match the partition key specified upon creation of the Cosmos DB container. + ## Related links - [Basic schema for a Dapr component]({{< ref component-schema >}}) diff --git a/daprdocs/content/en/reference/components-reference/supported-state-stores/setup-azure-cosmosdb.md b/daprdocs/content/en/reference/components-reference/supported-state-stores/setup-azure-cosmosdb.md index db53ee78b..a69ad8b7f 100644 --- a/daprdocs/content/en/reference/components-reference/supported-state-stores/setup-azure-cosmosdb.md +++ b/daprdocs/content/en/reference/components-reference/supported-state-stores/setup-azure-cosmosdb.md @@ -64,6 +64,17 @@ In order to setup CosmosDB as a state store, you need the following properties: - **Database**: The name of the database - **Collection**: The name of the collection +## Best Practices for Production Use + +Azure Cosmos DB shares a strict metadata request rate limit across all databases in a single Azure Cosmos DB account. New connections to Azure Cosmos DB assume a large percentage of the allowable request rate limit. (See the [CosmosDB documentation](https://docs.microsoft.com/azure/cosmos-db/sql/troubleshoot-request-rate-too-large#recommended-solution-3)) + +Therefore several strategies must be applied to avoid simultaneous new connections to Azure Cosmos DB: + +- Ensure sidecars of applications only load the Azure Cosmos DB component when they require it to avoid unnecessary database connections. This can be done by [scoping your components to specific applications]({{< ref component-scopes.md#application-access-to-components-with-scopes >}}). +- Choose deployment strategies that sequentially deploy or start all applications to minimize bursts in new connections to your Azure Cosmos DB accounts. +- Avoid reusing the same Azure Cosmos DB account for unrelated databases or systems (even outside of Dapr). Distinct Azure Cosmos DB accounts have distinct rate limits. +- Increase the `initTimeout` value to allow the component to retry connecting to Azure Cosmos DB during side car initialization for up to 5 minutes. The default value is `5s` and should be increased. Using Kubernetes increasing this value may also require an update to your [Readiness and Liveness probes](https://kubernetes.io/docs/tasks/configure-pod-container/configure-liveness-readiness-startup-probes/). + ## Data format To use the CosmosDB state store, your data must be sent to Dapr in JSON-serialized. Having it just JSON *serializable* will not work. From 4dd9ea88b082636936d3222b9f560d4437f9b4d6 Mon Sep 17 00:00:00 2001 From: Bernd Verst <4535280+berndverst@users.noreply.github.com> Date: Tue, 30 Nov 2021 17:24:14 -0800 Subject: [PATCH 13/20] Fix link --- .../components-reference/supported-bindings/cosmosdb.md | 2 +- .../supported-state-stores/setup-azure-cosmosdb.md | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/daprdocs/content/en/reference/components-reference/supported-bindings/cosmosdb.md b/daprdocs/content/en/reference/components-reference/supported-bindings/cosmosdb.md index aaa41d2de..e35d2ea66 100644 --- a/daprdocs/content/en/reference/components-reference/supported-bindings/cosmosdb.md +++ b/daprdocs/content/en/reference/components-reference/supported-bindings/cosmosdb.md @@ -62,7 +62,7 @@ Azure Cosmos DB shares a strict metadata request rate limit across all databases Therefore several strategies must be applied to avoid simultaneous new connections to Azure Cosmos DB: -- Ensure sidecars of applications only load the Azure Cosmos DB component when they require it to avoid unnecessary database connections. This can be done by [scoping your components to specific applications]({{< ref component-scopes.md#application-access-to-components-with-scopes >}}). +- Ensure sidecars of applications only load the Azure Cosmos DB component when they require it to avoid unnecessary database connections. This can be done by [scoping your components to specific applications]({{< ref component-scopes.md >}}#application-access-to-components-with-scopes). - Choose deployment strategies that sequentially deploy or start all applications to minimize bursts in new connections to your Azure Cosmos DB accounts. - Avoid reusing the same Azure Cosmos DB account for unrelated databases or systems (even outside of Dapr). Distinct Azure Cosmos DB accounts have distinct rate limits. - Increase the `initTimeout` value to allow the component to retry connecting to Azure Cosmos DB during side car initialization for up to 5 minutes. The default value is `5s` and should be increased. Using Kubernetes increasing this value may also require an update to your [Readiness and Liveness probes](https://kubernetes.io/docs/tasks/configure-pod-container/configure-liveness-readiness-startup-probes/). diff --git a/daprdocs/content/en/reference/components-reference/supported-state-stores/setup-azure-cosmosdb.md b/daprdocs/content/en/reference/components-reference/supported-state-stores/setup-azure-cosmosdb.md index a69ad8b7f..3b4965deb 100644 --- a/daprdocs/content/en/reference/components-reference/supported-state-stores/setup-azure-cosmosdb.md +++ b/daprdocs/content/en/reference/components-reference/supported-state-stores/setup-azure-cosmosdb.md @@ -70,7 +70,7 @@ Azure Cosmos DB shares a strict metadata request rate limit across all databases Therefore several strategies must be applied to avoid simultaneous new connections to Azure Cosmos DB: -- Ensure sidecars of applications only load the Azure Cosmos DB component when they require it to avoid unnecessary database connections. This can be done by [scoping your components to specific applications]({{< ref component-scopes.md#application-access-to-components-with-scopes >}}). +- Ensure sidecars of applications only load the Azure Cosmos DB component when they require it to avoid unnecessary database connections. This can be done by [scoping your components to specific applications]({{< ref component-scopes.md >}}#application-access-to-components-with-scopes). - Choose deployment strategies that sequentially deploy or start all applications to minimize bursts in new connections to your Azure Cosmos DB accounts. - Avoid reusing the same Azure Cosmos DB account for unrelated databases or systems (even outside of Dapr). Distinct Azure Cosmos DB accounts have distinct rate limits. - Increase the `initTimeout` value to allow the component to retry connecting to Azure Cosmos DB during side car initialization for up to 5 minutes. The default value is `5s` and should be increased. Using Kubernetes increasing this value may also require an update to your [Readiness and Liveness probes](https://kubernetes.io/docs/tasks/configure-pod-container/configure-liveness-readiness-startup-probes/). From b53373c10e875aeab33c62af13f5911113c03809 Mon Sep 17 00:00:00 2001 From: Bernd Verst <4535280+berndverst@users.noreply.github.com> Date: Tue, 30 Nov 2021 17:32:02 -0800 Subject: [PATCH 14/20] Grammar change --- .../components-reference/supported-bindings/cosmosdb.md | 2 +- .../supported-state-stores/setup-azure-cosmosdb.md | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/daprdocs/content/en/reference/components-reference/supported-bindings/cosmosdb.md b/daprdocs/content/en/reference/components-reference/supported-bindings/cosmosdb.md index e35d2ea66..590cb3fc7 100644 --- a/daprdocs/content/en/reference/components-reference/supported-bindings/cosmosdb.md +++ b/daprdocs/content/en/reference/components-reference/supported-bindings/cosmosdb.md @@ -65,7 +65,7 @@ Therefore several strategies must be applied to avoid simultaneous new connectio - Ensure sidecars of applications only load the Azure Cosmos DB component when they require it to avoid unnecessary database connections. This can be done by [scoping your components to specific applications]({{< ref component-scopes.md >}}#application-access-to-components-with-scopes). - Choose deployment strategies that sequentially deploy or start all applications to minimize bursts in new connections to your Azure Cosmos DB accounts. - Avoid reusing the same Azure Cosmos DB account for unrelated databases or systems (even outside of Dapr). Distinct Azure Cosmos DB accounts have distinct rate limits. -- Increase the `initTimeout` value to allow the component to retry connecting to Azure Cosmos DB during side car initialization for up to 5 minutes. The default value is `5s` and should be increased. Using Kubernetes increasing this value may also require an update to your [Readiness and Liveness probes](https://kubernetes.io/docs/tasks/configure-pod-container/configure-liveness-readiness-startup-probes/). +- Increase the `initTimeout` value to allow the component to retry connecting to Azure Cosmos DB during side car initialization for up to 5 minutes. The default value is `5s` and should be increased. When using Kubernetes, increasing this value may also require an update to your [Readiness and Liveness probes](https://kubernetes.io/docs/tasks/configure-pod-container/configure-liveness-readiness-startup-probes/). ```yaml spec: diff --git a/daprdocs/content/en/reference/components-reference/supported-state-stores/setup-azure-cosmosdb.md b/daprdocs/content/en/reference/components-reference/supported-state-stores/setup-azure-cosmosdb.md index 3b4965deb..5656383b9 100644 --- a/daprdocs/content/en/reference/components-reference/supported-state-stores/setup-azure-cosmosdb.md +++ b/daprdocs/content/en/reference/components-reference/supported-state-stores/setup-azure-cosmosdb.md @@ -73,7 +73,7 @@ Therefore several strategies must be applied to avoid simultaneous new connectio - Ensure sidecars of applications only load the Azure Cosmos DB component when they require it to avoid unnecessary database connections. This can be done by [scoping your components to specific applications]({{< ref component-scopes.md >}}#application-access-to-components-with-scopes). - Choose deployment strategies that sequentially deploy or start all applications to minimize bursts in new connections to your Azure Cosmos DB accounts. - Avoid reusing the same Azure Cosmos DB account for unrelated databases or systems (even outside of Dapr). Distinct Azure Cosmos DB accounts have distinct rate limits. -- Increase the `initTimeout` value to allow the component to retry connecting to Azure Cosmos DB during side car initialization for up to 5 minutes. The default value is `5s` and should be increased. Using Kubernetes increasing this value may also require an update to your [Readiness and Liveness probes](https://kubernetes.io/docs/tasks/configure-pod-container/configure-liveness-readiness-startup-probes/). +- Increase the `initTimeout` value to allow the component to retry connecting to Azure Cosmos DB during side car initialization for up to 5 minutes. The default value is `5s` and should be increased. When using Kubernetes, increasing this value may also require an update to your [Readiness and Liveness probes](https://kubernetes.io/docs/tasks/configure-pod-container/configure-liveness-readiness-startup-probes/). ## Data format From 873bcb27c18978c87f34ffd9461313c500af9e6c Mon Sep 17 00:00:00 2001 From: Bernd Verst <4535280+berndverst@users.noreply.github.com> Date: Tue, 30 Nov 2021 17:40:45 -0800 Subject: [PATCH 15/20] minor wording --- .../components-reference/supported-bindings/cosmosdb.md | 2 +- .../supported-state-stores/setup-azure-cosmosdb.md | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/daprdocs/content/en/reference/components-reference/supported-bindings/cosmosdb.md b/daprdocs/content/en/reference/components-reference/supported-bindings/cosmosdb.md index 590cb3fc7..57d71e8b9 100644 --- a/daprdocs/content/en/reference/components-reference/supported-bindings/cosmosdb.md +++ b/daprdocs/content/en/reference/components-reference/supported-bindings/cosmosdb.md @@ -63,7 +63,7 @@ Azure Cosmos DB shares a strict metadata request rate limit across all databases Therefore several strategies must be applied to avoid simultaneous new connections to Azure Cosmos DB: - Ensure sidecars of applications only load the Azure Cosmos DB component when they require it to avoid unnecessary database connections. This can be done by [scoping your components to specific applications]({{< ref component-scopes.md >}}#application-access-to-components-with-scopes). -- Choose deployment strategies that sequentially deploy or start all applications to minimize bursts in new connections to your Azure Cosmos DB accounts. +- Choose deployment strategies that sequentially deploy or start your applications to minimize bursts in new connections to your Azure Cosmos DB accounts. - Avoid reusing the same Azure Cosmos DB account for unrelated databases or systems (even outside of Dapr). Distinct Azure Cosmos DB accounts have distinct rate limits. - Increase the `initTimeout` value to allow the component to retry connecting to Azure Cosmos DB during side car initialization for up to 5 minutes. The default value is `5s` and should be increased. When using Kubernetes, increasing this value may also require an update to your [Readiness and Liveness probes](https://kubernetes.io/docs/tasks/configure-pod-container/configure-liveness-readiness-startup-probes/). diff --git a/daprdocs/content/en/reference/components-reference/supported-state-stores/setup-azure-cosmosdb.md b/daprdocs/content/en/reference/components-reference/supported-state-stores/setup-azure-cosmosdb.md index 5656383b9..de940ef02 100644 --- a/daprdocs/content/en/reference/components-reference/supported-state-stores/setup-azure-cosmosdb.md +++ b/daprdocs/content/en/reference/components-reference/supported-state-stores/setup-azure-cosmosdb.md @@ -71,7 +71,7 @@ Azure Cosmos DB shares a strict metadata request rate limit across all databases Therefore several strategies must be applied to avoid simultaneous new connections to Azure Cosmos DB: - Ensure sidecars of applications only load the Azure Cosmos DB component when they require it to avoid unnecessary database connections. This can be done by [scoping your components to specific applications]({{< ref component-scopes.md >}}#application-access-to-components-with-scopes). -- Choose deployment strategies that sequentially deploy or start all applications to minimize bursts in new connections to your Azure Cosmos DB accounts. +- Choose deployment strategies that sequentially deploy or start your applications to minimize bursts in new connections to your Azure Cosmos DB accounts. - Avoid reusing the same Azure Cosmos DB account for unrelated databases or systems (even outside of Dapr). Distinct Azure Cosmos DB accounts have distinct rate limits. - Increase the `initTimeout` value to allow the component to retry connecting to Azure Cosmos DB during side car initialization for up to 5 minutes. The default value is `5s` and should be increased. When using Kubernetes, increasing this value may also require an update to your [Readiness and Liveness probes](https://kubernetes.io/docs/tasks/configure-pod-container/configure-liveness-readiness-startup-probes/). From ea8d9ef06f61e238c5951222f8531c54d324ce79 Mon Sep 17 00:00:00 2001 From: Bernd Verst <4535280+berndverst@users.noreply.github.com> Date: Tue, 30 Nov 2021 17:44:59 -0800 Subject: [PATCH 16/20] Add initTimeout example for cosmosdb state --- .../supported-state-stores/setup-azure-cosmosdb.md | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/daprdocs/content/en/reference/components-reference/supported-state-stores/setup-azure-cosmosdb.md b/daprdocs/content/en/reference/components-reference/supported-state-stores/setup-azure-cosmosdb.md index de940ef02..f1d3c3450 100644 --- a/daprdocs/content/en/reference/components-reference/supported-state-stores/setup-azure-cosmosdb.md +++ b/daprdocs/content/en/reference/components-reference/supported-state-stores/setup-azure-cosmosdb.md @@ -75,6 +75,14 @@ Therefore several strategies must be applied to avoid simultaneous new connectio - Avoid reusing the same Azure Cosmos DB account for unrelated databases or systems (even outside of Dapr). Distinct Azure Cosmos DB accounts have distinct rate limits. - Increase the `initTimeout` value to allow the component to retry connecting to Azure Cosmos DB during side car initialization for up to 5 minutes. The default value is `5s` and should be increased. When using Kubernetes, increasing this value may also require an update to your [Readiness and Liveness probes](https://kubernetes.io/docs/tasks/configure-pod-container/configure-liveness-readiness-startup-probes/). +```yaml +spec: + type: state.azure.cosmosdb + version: v1 + initTimeout: 5m + metadata: +``` + ## Data format To use the CosmosDB state store, your data must be sent to Dapr in JSON-serialized. Having it just JSON *serializable* will not work. From 0ea7939195169bec188b98b46c1ecaddc8e3341b Mon Sep 17 00:00:00 2001 From: greenie-msft <56556602+greenie-msft@users.noreply.github.com> Date: Tue, 30 Nov 2021 18:06:30 -0800 Subject: [PATCH 17/20] Update howto-invoke-discover-services.md --- .../service-invocation/howto-invoke-discover-services.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/daprdocs/content/en/developing-applications/building-blocks/service-invocation/howto-invoke-discover-services.md b/daprdocs/content/en/developing-applications/building-blocks/service-invocation/howto-invoke-discover-services.md index d7ec89069..f0110e05b 100644 --- a/daprdocs/content/en/developing-applications/building-blocks/service-invocation/howto-invoke-discover-services.md +++ b/daprdocs/content/en/developing-applications/building-blocks/service-invocation/howto-invoke-discover-services.md @@ -10,7 +10,7 @@ This article describe how to deploy services each with an unique application ID, ## Example: -The below code examples loosely describe an application that processes orders. In the examples, there are two services - an order processing service and a checkout service. Both services have Dapr sidecars and the order processing service uses Dapr to invoke the checkout method in the checkout service. +The below code examples loosely describes an application that processes orders. In the examples, there are two services - an order processing service and a checkout service. Both services have Dapr sidecars and the order processing service uses Dapr to invoke the checkout method in the checkout service. Diagram showing service invocation of example service @@ -363,4 +363,4 @@ For more information on tracing and logs see the [observability]({{< ref observa ## Related Links * [Service invocation overview]({{< ref service-invocation-overview.md >}}) -* [Service invocation API specification]({{< ref service_invocation_api.md >}}) \ No newline at end of file +* [Service invocation API specification]({{< ref service_invocation_api.md >}}) From 93e096a663e06d2f9aa1fb0d9d7f86e1052ce378 Mon Sep 17 00:00:00 2001 From: Amulya Varote Date: Tue, 30 Nov 2021 19:21:42 -0800 Subject: [PATCH 18/20] Renamed example image files --- ...building-block-state-management-example.png | Bin 0 -> 123199 bytes ...lding_block_service_invocation_example.png} | Bin 2 files changed, 0 insertions(+), 0 deletions(-) create mode 100644 daprdocs/static/images/building-block-state-management-example.png rename daprdocs/static/images/{service_invocation_eg.png => building_block_service_invocation_example.png} (100%) diff --git a/daprdocs/static/images/building-block-state-management-example.png b/daprdocs/static/images/building-block-state-management-example.png new file mode 100644 index 0000000000000000000000000000000000000000..87b3b9b326111244ffff390df68ff79b0198757c GIT binary patch literal 123199 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

MXw*t7 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 Date: Tue, 30 Nov 2021 19:28:00 -0800 Subject: [PATCH 19/20] Changed image name in the docs --- .../howto-invoke-discover-services.md | 2 +- .../state-management/howto-get-save-state.md | 2 +- ...> building-block-service-invocation-example.png} | Bin 3 files changed, 2 insertions(+), 2 deletions(-) rename daprdocs/static/images/{building_block_service_invocation_example.png => building-block-service-invocation-example.png} (100%) diff --git a/daprdocs/content/en/developing-applications/building-blocks/service-invocation/howto-invoke-discover-services.md b/daprdocs/content/en/developing-applications/building-blocks/service-invocation/howto-invoke-discover-services.md index f0110e05b..4f0cb2127 100644 --- a/daprdocs/content/en/developing-applications/building-blocks/service-invocation/howto-invoke-discover-services.md +++ b/daprdocs/content/en/developing-applications/building-blocks/service-invocation/howto-invoke-discover-services.md @@ -12,7 +12,7 @@ This article describe how to deploy services each with an unique application ID, The below code examples loosely describes an application that processes orders. In the examples, there are two services - an order processing service and a checkout service. Both services have Dapr sidecars and the order processing service uses Dapr to invoke the checkout method in the checkout service. -Diagram showing service invocation of example service +Diagram showing service invocation of example service ## Step 1: Choose an ID for your service diff --git a/daprdocs/content/en/developing-applications/building-blocks/state-management/howto-get-save-state.md b/daprdocs/content/en/developing-applications/building-blocks/state-management/howto-get-save-state.md index 89839db12..1684b7ddb 100644 --- a/daprdocs/content/en/developing-applications/building-blocks/state-management/howto-get-save-state.md +++ b/daprdocs/content/en/developing-applications/building-blocks/state-management/howto-get-save-state.md @@ -23,7 +23,7 @@ In this guide we'll start of with the basics: Using the key/value state API to a The below code examples loosely describe an application that processes orders. In the examples, there is an order processing service which has a Dapr sidecar. The order processing service uses Dapr to store state in a Redis state store. -Diagram showing state management of example service +Diagram showing state management of example service ## Step 1: Setup a state store diff --git a/daprdocs/static/images/building_block_service_invocation_example.png b/daprdocs/static/images/building-block-service-invocation-example.png similarity index 100% rename from daprdocs/static/images/building_block_service_invocation_example.png rename to daprdocs/static/images/building-block-service-invocation-example.png From 5b7b4ff61672805061f0e7c5ee6590e06cec20e9 Mon Sep 17 00:00:00 2001 From: greenie-msft <56556602+greenie-msft@users.noreply.github.com> Date: Tue, 30 Nov 2021 19:44:11 -0800 Subject: [PATCH 20/20] Update state-store-ttl.md (#2016) Co-authored-by: Mark Fussell --- .../building-blocks/state-management/state-store-ttl.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/daprdocs/content/en/developing-applications/building-blocks/state-management/state-store-ttl.md b/daprdocs/content/en/developing-applications/building-blocks/state-management/state-store-ttl.md index 14f0034f0..a87587b15 100644 --- a/daprdocs/content/en/developing-applications/building-blocks/state-management/state-store-ttl.md +++ b/daprdocs/content/en/developing-applications/building-blocks/state-management/state-store-ttl.md @@ -1,7 +1,7 @@ --- type: docs -title: "State Time-to-Live (TTL)" -linkTitle: "State TTL" +title: "How-To: Set state Time-to-Live (TTL)" +linkTitle: "How-To: Set state TTL" weight: 500 description: "Manage state with time-to-live." ---