From 1240240c7ed89623a5b96629affe301290e158a2 Mon Sep 17 00:00:00 2001 From: Aaron Crawfis Date: Thu, 21 Jan 2021 13:27:45 -0800 Subject: [PATCH 1/7] Fix page reference --- daprdocs/content/en/contributing/contributing-docs.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/daprdocs/content/en/contributing/contributing-docs.md b/daprdocs/content/en/contributing/contributing-docs.md index 39d6afc3e..9fd6b65ac 100644 --- a/daprdocs/content/en/contributing/contributing-docs.md +++ b/daprdocs/content/en/contributing/contributing-docs.md @@ -39,7 +39,7 @@ These conventions should be followed throughout all Dapr documentation to ensure ## Contributing a new docs page - Make sure the documentation you are writing is in the correct place in the hierarchy. - Avoid creating new sections where possible, there is a good chance a proper place in the docs hierarchy already exists. -- Make sure to include a complete [Hugo front-matter](front-matter). +- Make sure to include a complete [Hugo front-matter](#front-matter). ### Contributing a new concept doc - Ensure the reader can understand why they should care about this feature. What problems does it help them solve? From ba069daec883111f72dc059e520d7957ea8040af Mon Sep 17 00:00:00 2001 From: Aaron Crawfis Date: Thu, 21 Jan 2021 16:13:58 -0800 Subject: [PATCH 2/7] Move applicable changes into 0.11 --- .gitmodules | 4 + daprdocs/config.toml | 30 +- .../actors/actors-background.md | 104 ----- .../building-blocks/actors/actors-overview.md | 185 ++++----- .../building-blocks/actors/howto-actors.md | 137 +++++++ .../state-management/howto-get-save-state.md | 368 ++++++++++++++---- .../state-management/howto-share-state.md | 95 +++++ .../query-state-store/_index.md | 2 +- .../state-management-overview.md | 63 +-- .../en/developing-applications/sdks/_index.md | 44 ++- ...{serialization.md => sdk-serialization.md} | 5 +- .../operations/components/component-schema.md | 52 +++ .../operations/components/component-scopes.md | 2 +- .../components/component-secrets.md | 2 +- .../troubleshooting/common_issues.md | 2 +- sdkdocs/python | 1 + 16 files changed, 760 insertions(+), 336 deletions(-) delete mode 100644 daprdocs/content/en/developing-applications/building-blocks/actors/actors-background.md create mode 100644 daprdocs/content/en/developing-applications/building-blocks/actors/howto-actors.md create mode 100644 daprdocs/content/en/developing-applications/building-blocks/state-management/howto-share-state.md rename daprdocs/content/en/developing-applications/sdks/{serialization.md => sdk-serialization.md} (97%) create mode 100644 daprdocs/content/en/operations/components/component-schema.md create mode 160000 sdkdocs/python diff --git a/.gitmodules b/.gitmodules index 9d1226e9f..5101acdce 100644 --- a/.gitmodules +++ b/.gitmodules @@ -1,3 +1,7 @@ [submodule "daprdocs/themes/docsy"] path = daprdocs/themes/docsy url = https://github.com/google/docsy.git +[submodule "sdkdocs/python"] + path = sdkdocs/python + url = https://github.com/dapr/python-sdk.git + \ No newline at end of file diff --git a/daprdocs/config.toml b/daprdocs/config.toml index 382c325b9..1fea789e1 100644 --- a/daprdocs/config.toml +++ b/daprdocs/config.toml @@ -2,14 +2,13 @@ baseURL = "https://docs.dapr.io/" title = "Dapr Docs" theme = "docsy" +disableFastRender = true enableRobotsTXT = true enableGitInfo = true # Language Configuration languageCode = "en-us" -contentDir = "content/en" -defaultContentLanguage = "en" # Disable categories & tags disableKinds = ["taxonomy", "term"] @@ -18,6 +17,33 @@ disableKinds = ["taxonomy", "term"] [services.googleAnalytics] id = "UA-149338238-3" +# Mounts +[module] + [[module.mounts]] + source = "content/en" + target = "content" + [[module.mounts]] + source = "static" + target = "static" + [[module.mounts]] + source = "layouts" + target = "layouts" + [[module.mounts]] + source = "data" + target = "data" + [[module.mounts]] + source = "assets" + target = "assets" + [[module.mounts]] + source = "archetypes" + target = "archetypes" + [[module.mounts]] + source = "../sdkdocs/python/daprdocs/content/en/python-sdk-docs" + target = "content/developing-applications/sdks/python" + [[module.mounts]] + source = "../sdkdocs/python/daprdocs/content/en/python-sdk-contributing" + target = "content/contributing/" + # Markdown Engine - Allow inline html [markup] [markup.goldmark] diff --git a/daprdocs/content/en/developing-applications/building-blocks/actors/actors-background.md b/daprdocs/content/en/developing-applications/building-blocks/actors/actors-background.md deleted file mode 100644 index b2c5f3a3f..000000000 --- a/daprdocs/content/en/developing-applications/building-blocks/actors/actors-background.md +++ /dev/null @@ -1,104 +0,0 @@ ---- -type: docs -title: "Introduction to actors" -linkTitle: "Actors background" -weight: 20 -description: Learn more about the actor pattern ---- - -The [actor pattern](https://en.wikipedia.org/wiki/Actor_model) describes **actors** as the lowest-level "unit of computation". In other words, you write your code in a self-contained unit (called an actor) that receives messages and processes them one at a time, without any kind of concurrency or threading. - -While your code processes a message, it can send one or more messages to other actors, or create new actors. An underlying **runtime** manages how, when and where each actor runs, and also routes messages between actors. - -A large number of actors can execute simultaneously, and actors execute independently from each other. - -Dapr includes a runtime that specifically implements the [Virtual Actor pattern](https://www.microsoft.com/en-us/research/project/orleans-virtual-actors/). With Dapr's implementation, you write your Dapr actors according to the Actor model, and Dapr leverages the scalability and reliability guarantees that the underlying platform provides. - -## Quick links - -- [Dapr Actor Features]({{< ref actors-overview.md >}}) -- [Dapr Actor API Spec]({{< ref actors_api.md >}} ) - -### When to use actors - -As with any other technology decision, you should decide whether to use actors based on the problem you're trying to solve. - -The actor design pattern can be a good fit to a number of distributed systems problems and scenarios, but the first thing you should consider are the constraints of the pattern. Generally speaking, consider the actor pattern to model your problem or scenario if: - -* Your problem space involves a large number (thousands or more) of small, independent, and isolated units of state and logic. -* You want to work with single-threaded objects that do not require significant interaction from external components, including querying state across a set of actors. -* Your actor instances won't block callers with unpredictable delays by issuing I/O operations. - -## Actors in dapr - -Every actor is defined as an instance of an actor type, identical to the way an object is an instance of a class. For example, there may be an actor type that implements the functionality of a calculator and there could be many actors of that type that are distributed on various nodes across a cluster. Each such actor is uniquely identified by an actor ID. - - - -## Actor lifetime - -Dapr actors are virtual, meaning that their lifetime is not tied to their in-memory representation. As a result, they do not need to be explicitly created or destroyed. The Dapr actors runtime automatically activates an actor the first time it receives a request for that actor ID. If an actor is not used for a period of time, the Dapr Actors runtime garbage-collects the in-memory object. It will also maintain knowledge of the actor's existence should it need to be reactivated later. - -Invocation of actor methods and reminders reset the idle time, e.g. reminder firing will keep the actor active. Actor reminders fire whether an actor is active or inactive, if fired for inactive actor, it will activate the actor first. Actor timers do not reset the idle time, so timer firing will not keep the actor active. Timers only fire while the actor is active. - -The idle timeout and scan interval Dapr runtime uses to see if an actor can be garbage-collected is configurable. This information can be passed when Dapr runtime calls into the actor service to get supported actor types. - -This virtual actor lifetime abstraction carries some caveats as a result of the virtual actor model, and in fact the Dapr Actors implementation deviates at times from this model. - -An actor is automatically activated (causing an actor object to be constructed) the first time a message is sent to its actor ID. After some period of time, the actor object is garbage collected. In the future, using the actor ID again, causes a new actor object to be constructed. An actor's state outlives the object's lifetime as state is stored in configured state provider for Dapr runtime. - -## Distribution and failover - -To provide scalability and reliability, actors instances are distributed throughout the cluster and Dapr automatically migrates them from failed nodes to healthy ones as required. - -Actors are distributed across the instances of the actor service, and those instance are distributed across the nodes in a cluster. Each service instance contains a set of actors for a given actor type. - -### Actor placement service -The Dapr actor runtime manages distribution scheme and key range settings for you. This is done by the actor `Placement` service. When a new instance of a service is created, the corresponding Dapr runtime registers the actor types it can create and the `Placement` service calculates the partitioning across all the instances for a given actor type. This table of partition information for each actor type is updated and stored in each Dapr instance running in the environment and can change dynamically as new instance of actor services are created and destroyed. This is shown in the diagram below. - - - -When a client calls an actor with a particular id (for example, actor id 123), the Dapr instance for the client hashes the actor type and id, and uses the information to call onto the corresponding Dapr instance that can serve the requests for that particular actor id. As a result, the same partition (or service instance) is always called for any given actor id. This is shown in the diagram below. - - - - This simplifies some choices but also carries some consideration: - -* By default, actors are randomly placed into pods resulting in uniform distribution. -* Because actors are randomly placed, it should be expected that actor operations always require network communication, including serialization and deserialization of method call data, incurring latency and overhead. - -Note: The Dapr actor Placement service is only used for actor placement and therefore is not needed if your services are not using Dapr actors. The Placement service can run in all [hosting environments]({{< ref hosting >}}), including self-hosted and Kubernetes. - -## Actor communication - -You can interact with Dapr to invoke the actor method by calling HTTP/gRPC endpoint. - -```bash -POST/GET/PUT/DELETE http://localhost:3500/v1.0/actors/// -``` - -You can provide any data for the actor method in the request body, and the response for the request would be in the response body which is the data from actor call. - -Refer to [Dapr Actor Features]({{< ref actors-overview.md >}}) for more details. - -### Concurrency - -The Dapr Actors runtime provides a simple turn-based access model for accessing actor methods. This means that no more than one thread can be active inside an actor object's code at any time. Turn-based access greatly simplifies concurrent systems as there is no need for synchronization mechanisms for data access. It also means systems must be designed with special considerations for the single-threaded access nature of each actor instance. - -A single actor instance cannot process more than one request at a time. An actor instance can cause a throughput bottleneck if it is expected to handle concurrent requests. - -Actors can deadlock on each other if there is a circular request between two actors while an external request is made to one of the actors simultaneously. The Dapr actor runtime automatically times out on actor calls and throw an exception to the caller to interrupt possible deadlock situations. - - - - -### Turn-based access - -A turn consists of the complete execution of an actor method in response to a request from other actors or clients, or the complete execution of a timer/reminder callback. Even though these methods and callbacks are asynchronous, the Dapr Actors runtime does not interleave them. A turn must be fully finished before a new turn is allowed. In other words, an actor method or timer/reminder callback that is currently executing must be fully finished before a new call to a method or callback is allowed. A method or callback is considered to have finished if the execution has returned from the method or callback and the task returned by the method or callback has finished. It is worth emphasizing that turn-based concurrency is respected even across different methods, timers, and callbacks. - -The Dapr actors runtime enforces turn-based concurrency by acquiring a per-actor lock at the beginning of a turn and releasing the lock at the end of the turn. Thus, turn-based concurrency is enforced on a per-actor basis and not across actors. Actor methods and timer/reminder callbacks can execute simultaneously on behalf of different actors. - -The following example illustrates the above concepts. Consider an actor type that implements two asynchronous methods (say, Method1 and Method2), a timer, and a reminder. The diagram below shows an example of a timeline for the execution of these methods and callbacks on behalf of two actors (ActorId1 and ActorId2) that belong to this actor type. - - - diff --git a/daprdocs/content/en/developing-applications/building-blocks/actors/actors-overview.md b/daprdocs/content/en/developing-applications/building-blocks/actors/actors-overview.md index a342db3d3..587873d99 100644 --- a/daprdocs/content/en/developing-applications/building-blocks/actors/actors-overview.md +++ b/daprdocs/content/en/developing-applications/building-blocks/actors/actors-overview.md @@ -4,134 +4,99 @@ title: "Dapr actors overview" linkTitle: "Overview" weight: 10 description: Overview of Dapr support for actors +aliases: + - "/developing-applications/building-blocks/actors/actors-background" --- -The Dapr actors runtime provides support for [virtual actors]({{< ref actors-background.md >}}) through following capabilities: +## Background +The [actor pattern](https://en.wikipedia.org/wiki/Actor_model) describes actors as the lowest-level "unit of computation". In other words, you write your code in a self-contained unit (called an actor) that receives messages and processes them one at a time, without any kind of concurrency or threading. -## Actor method invocation +While your code processes a message, it can send one or more messages to other actors, or create new actors. An underlying runtime manages how, when and where each actor runs, and also routes messages between actors. -You can interact with Dapr to invoke the actor method by calling HTTP/gRPC endpoint +A large number of actors can execute simultaneously, and actors execute independently from each other. + +Dapr includes a runtime that specifically implements the [Virtual Actor pattern](https://www.microsoft.com/en-us/research/project/orleans-virtual-actors/). With Dapr's implementation, you write your Dapr actors according to the Actor model, and Dapr leverages the scalability and reliability guarantees that the underlying platform provides. + +### When to use actors + +As with any other technology decision, you should decide whether to use actors based on the problem you're trying to solve. + +The actor design pattern can be a good fit to a number of distributed systems problems and scenarios, but the first thing you should consider are the constraints of the pattern. Generally speaking, consider the actor pattern to model your problem or scenario if: + +* Your problem space involves a large number (thousands or more) of small, independent, and isolated units of state and logic. +* You want to work with single-threaded objects that do not require significant interaction from external components, including querying state across a set of actors. +* Your actor instances won't block callers with unpredictable delays by issuing I/O operations. + +## Actors in dapr + +Every actor is defined as an instance of an actor type, identical to the way an object is an instance of a class. For example, there may be an actor type that implements the functionality of a calculator and there could be many actors of that type that are distributed on various nodes across a cluster. Each such actor is uniquely identified by an actor ID. + + + +## Actor lifetime + +Dapr actors are virtual, meaning that their lifetime is not tied to their in-memory representation. As a result, they do not need to be explicitly created or destroyed. The Dapr actors runtime automatically activates an actor the first time it receives a request for that actor ID. If an actor is not used for a period of time, the Dapr Actors runtime garbage-collects the in-memory object. It will also maintain knowledge of the actor's existence should it need to be reactivated later. + +Invocation of actor methods and reminders reset the idle time, e.g. reminder firing will keep the actor active. Actor reminders fire whether an actor is active or inactive, if fired for inactive actor, it will activate the actor first. Actor timers do not reset the idle time, so timer firing will not keep the actor active. Timers only fire while the actor is active. + +The idle timeout and scan interval Dapr runtime uses to see if an actor can be garbage-collected is configurable. This information can be passed when Dapr runtime calls into the actor service to get supported actor types. + +This virtual actor lifetime abstraction carries some caveats as a result of the virtual actor model, and in fact the Dapr Actors implementation deviates at times from this model. + +An actor is automatically activated (causing an actor object to be constructed) the first time a message is sent to its actor ID. After some period of time, the actor object is garbage collected. In the future, using the actor ID again, causes a new actor object to be constructed. An actor's state outlives the object's lifetime as state is stored in configured state provider for Dapr runtime. + +## Distribution and failover + +To provide scalability and reliability, actors instances are distributed throughout the cluster and Dapr automatically migrates them from failed nodes to healthy ones as required. + +Actors are distributed across the instances of the actor service, and those instance are distributed across the nodes in a cluster. Each service instance contains a set of actors for a given actor type. + +### Actor placement service +The Dapr actor runtime manages distribution scheme and key range settings for you. This is done by the actor `Placement` service. When a new instance of a service is created, the corresponding Dapr runtime registers the actor types it can create and the `Placement` service calculates the partitioning across all the instances for a given actor type. This table of partition information for each actor type is updated and stored in each Dapr instance running in the environment and can change dynamically as new instance of actor services are created and destroyed. This is shown in the diagram below. + + + +When a client calls an actor with a particular id (for example, actor id 123), the Dapr instance for the client hashes the actor type and id, and uses the information to call onto the corresponding Dapr instance that can serve the requests for that particular actor id. As a result, the same partition (or service instance) is always called for any given actor id. This is shown in the diagram below. + + + + This simplifies some choices but also carries some consideration: + +* By default, actors are randomly placed into pods resulting in uniform distribution. +* Because actors are randomly placed, it should be expected that actor operations always require network communication, including serialization and deserialization of method call data, incurring latency and overhead. + +Note: The Dapr actor Placement service is only used for actor placement and therefore is not needed if your services are not using Dapr actors. The Placement service can run in all [hosting environments]({{< ref hosting >}}), including self-hosted and Kubernetes. + +## Actor communication + +You can interact with Dapr to invoke the actor method by calling HTTP/gRPC endpoint. ```bash -POST/GET/PUT/DELETE http://localhost:3500/v1.0/actors///method/ +POST/GET/PUT/DELETE http://localhost:3500/v1.0/actors/// ``` -You can provide any data for the actor method in the request body and the response for the request is in response body which is data from actor method call. +You can provide any data for the actor method in the request body, and the response for the request would be in the response body which is the data from actor call. -Refer [api spec]({{< ref "actors_api.md#invoke-actor-method" >}}) for more details. +Refer to [Dapr Actor Features]({{< ref actors-overview.md >}}) for more details. -## Actor state management +### Concurrency -Actors can save state reliably using state management capability. +The Dapr Actors runtime provides a simple turn-based access model for accessing actor methods. This means that no more than one thread can be active inside an actor object's code at any time. Turn-based access greatly simplifies concurrent systems as there is no need for synchronization mechanisms for data access. It also means systems must be designed with special considerations for the single-threaded access nature of each actor instance. -You can interact with Dapr through HTTP/gRPC endpoints for state management. +A single actor instance cannot process more than one request at a time. An actor instance can cause a throughput bottleneck if it is expected to handle concurrent requests. -To use actors, your state store must support multi-item transactions. This means your state store [component](https://github.com/dapr/components-contrib/tree/master/state) must implement the [TransactionalStore](https://github.com/dapr/components-contrib/blob/master/state/transactional_store.go) interface. The following state stores implement this interface: +Actors can deadlock on each other if there is a circular request between two actors while an external request is made to one of the actors simultaneously. The Dapr actor runtime automatically times out on actor calls and throw an exception to the caller to interrupt possible deadlock situations. -- Redis -- MongoDB -- PostgreSQL -- SQL Server -- Azure CosmosDB + -## Actor timers and reminders -Actors can schedule periodic work on themselves by registering either timers or reminders. +### Turn-based access -### Actor timers +A turn consists of the complete execution of an actor method in response to a request from other actors or clients, or the complete execution of a timer/reminder callback. Even though these methods and callbacks are asynchronous, the Dapr Actors runtime does not interleave them. A turn must be fully finished before a new turn is allowed. In other words, an actor method or timer/reminder callback that is currently executing must be fully finished before a new call to a method or callback is allowed. A method or callback is considered to have finished if the execution has returned from the method or callback and the task returned by the method or callback has finished. It is worth emphasizing that turn-based concurrency is respected even across different methods, timers, and callbacks. -You can register a callback on actor to be executed based on a timer. +The Dapr actors runtime enforces turn-based concurrency by acquiring a per-actor lock at the beginning of a turn and releasing the lock at the end of the turn. Thus, turn-based concurrency is enforced on a per-actor basis and not across actors. Actor methods and timer/reminder callbacks can execute simultaneously on behalf of different actors. -The Dapr actor runtime ensures that the callback methods respect the turn-based concurrency guarantees.This means that no other actor methods or timer/reminder callbacks will be in progress until this callback completes execution. +The following example illustrates the above concepts. Consider an actor type that implements two asynchronous methods (say, Method1 and Method2), a timer, and a reminder. The diagram below shows an example of a timeline for the execution of these methods and callbacks on behalf of two actors (ActorId1 and ActorId2) that belong to this actor type. -The next period of the timer starts after the callback completes execution. This implies that the timer is stopped while the callback is executing and is started when the callback finishes. + -The Dapr actors runtime saves changes made to the actor's state when the callback finishes. If an error occurs in saving the state, that actor object is deactivated and a new instance will be activated. - -All timers are stopped when the actor is deactivated as part of garbage collection. No timer callbacks are invoked after that. Also, the Dapr actors runtime does not retain any information about the timers that were running before deactivation. It is up to the actor to register any timers that it needs when it is reactivated in the future. - -You can create a timer for an actor by calling the HTTP/gRPC request to Dapr. - -```http -POST/PUT http://localhost:3500/v1.0/actors///timers/ -``` - -The timer `duetime` and callback are specified in the request body. The due time represents when the timer will first fire after registration. The `period` represents how often the timer fires after that. A due time of 0 means to fire immediately. Negative due times and negative periods are invalid. - -The following request body configures a timer with a `dueTime` of 9 seconds and a `period` of 3 seconds. This means it will first fire after 9 seconds, then every 3 seconds after that. -```json -{ - "dueTime":"0h0m9s0ms", - "period":"0h0m3s0ms" -} -``` - -The following request body configures a timer with a `dueTime` 0 seconds and a `period` of 3 seconds. This means it fires immediately after registration, then every 3 seconds after that. -```json -{ - "dueTime":"0h0m0s0ms", - "period":"0h0m3s0ms" -} -``` - -You can remove the actor timer by calling - -```http -DELETE http://localhost:3500/v1.0/actors///timers/ -``` - -Refer [api spec]({{< ref "actors_api.md#invoke-timer" >}}) for more details. - -### Actor reminders - -Reminders are a mechanism to trigger *persistent* callbacks on an actor at specified times. Their functionality is similar to timers. But unlike timers, reminders are triggered under all circumstances until the actor explicitly unregisters them or the actor is explicitly deleted. Specifically, reminders are triggered across actor deactivations and failovers because the Dapr actors runtime persists the information about the actors' reminders using Dapr actor state provider. - -You can create a persistent reminder for an actor by calling the Http/gRPC request to Dapr. - -```http -POST/PUT http://localhost:3500/v1.0/actors///reminders/ -``` - -The reminder `duetime` and callback can be specified in the request body. The due time represents when the reminder first fires after registration. The `period` represents how often the reminder will fire after that. A due time of 0 means to fire immediately. Negative due times and negative periods are invalid. To register a reminder that fires only once, set the period to an empty string. - -The following request body configures a reminder with a `dueTime` 9 seconds and a `period` of 3 seconds. This means it will first fire after 9 seconds, then every 3 seconds after that. -```json -{ - "dueTime":"0h0m9s0ms", - "period":"0h0m3s0ms" -} -``` - -The following request body configures a reminder with a `dueTime` 0 seconds and a `period` of 3 seconds. This means it will fire immediately after registration, then every 3 seconds after that. -```json -{ - "dueTime":"0h0m0s0ms", - "period":"0h0m3s0ms" -} -``` - -The following request body configures a reminder with a `dueTime` 15 seconds and a `period` of empty string. This means it will first fire after 15 seconds, then never fire again. -```json -{ - "dueTime":"0h0m15s0ms", - "period":"" -} -``` - -#### Retrieve actor reminder - -You can retrieve the actor reminder by calling - -```http -GET http://localhost:3500/v1.0/actors///reminders/ -``` - -#### Remove the actor reminder - -You can remove the actor reminder by calling - -```http -DELETE http://localhost:3500/v1.0/actors///reminders/ -``` - -Refer [api spec]({{< ref "actors_api.md#invoke-reminder" >}}) for more details. diff --git a/daprdocs/content/en/developing-applications/building-blocks/actors/howto-actors.md b/daprdocs/content/en/developing-applications/building-blocks/actors/howto-actors.md new file mode 100644 index 000000000..82fb6e97a --- /dev/null +++ b/daprdocs/content/en/developing-applications/building-blocks/actors/howto-actors.md @@ -0,0 +1,137 @@ +--- +type: docs +title: "How-to: Use virtual actors in Dapr" +linkTitle: "How-To: Virtual actors" +weight: 20 +description: Learn more about the actor pattern +--- + +The Dapr actors runtime provides support for [virtual actors]({{< ref actors-overview.md >}}) through following capabilities: + +## Actor method invocation + +You can interact with Dapr to invoke the actor method by calling HTTP/gRPC endpoint + +```html +POST/GET/PUT/DELETE http://localhost:3500/v1.0/actors///method/ +``` + +You can provide any data for the actor method in the request body and the response for the request is in response body which is data from actor method call. + +Refer [api spec]({{< ref "actors_api.md#invoke-actor-method" >}}) for more details. + +## Actor state management + +Actors can save state reliably using state management capability. + +You can interact with Dapr through HTTP/gRPC endpoints for state management. + +To use actors, your state store must support multi-item transactions. This means your state store [component](https://github.com/dapr/components-contrib/tree/master/state) must implement the [TransactionalStore](https://github.com/dapr/components-contrib/blob/master/state/transactional_store.go) interface. The following state stores implement this interface: + +- Redis +- MongoDB +- PostgreSQL +- SQL Server +- Azure CosmosDB + +## Actor timers and reminders + +Actors can schedule periodic work on themselves by registering either timers or reminders. + +### Actor timers + +You can register a callback on actor to be executed based on a timer. + +The Dapr actor runtime ensures that the callback methods respect the turn-based concurrency guarantees.This means that no other actor methods or timer/reminder callbacks will be in progress until this callback completes execution. + +The next period of the timer starts after the callback completes execution. This implies that the timer is stopped while the callback is executing and is started when the callback finishes. + +The Dapr actors runtime saves changes made to the actor's state when the callback finishes. If an error occurs in saving the state, that actor object is deactivated and a new instance will be activated. + +All timers are stopped when the actor is deactivated as part of garbage collection. No timer callbacks are invoked after that. Also, the Dapr actors runtime does not retain any information about the timers that were running before deactivation. It is up to the actor to register any timers that it needs when it is reactivated in the future. + +You can create a timer for an actor by calling the HTTP/gRPC request to Dapr. + +```md +POST/PUT http://localhost:3500/v1.0/actors///timers/ +``` + +The timer `duetime` and callback are specified in the request body. The due time represents when the timer will first fire after registration. The `period` represents how often the timer fires after that. A due time of 0 means to fire immediately. Negative due times and negative periods are invalid. + +The following request body configures a timer with a `dueTime` of 9 seconds and a `period` of 3 seconds. This means it will first fire after 9 seconds, then every 3 seconds after that. +```json +{ + "dueTime":"0h0m9s0ms", + "period":"0h0m3s0ms" +} +``` + +The following request body configures a timer with a `dueTime` 0 seconds and a `period` of 3 seconds. This means it fires immediately after registration, then every 3 seconds after that. +```json +{ + "dueTime":"0h0m0s0ms", + "period":"0h0m3s0ms" +} +``` + +You can remove the actor timer by calling + +```md +DELETE http://localhost:3500/v1.0/actors///timers/ +``` + +Refer [api spec]({{< ref "actors_api.md#invoke-timer" >}}) for more details. + +### Actor reminders + +Reminders are a mechanism to trigger *persistent* callbacks on an actor at specified times. Their functionality is similar to timers. But unlike timers, reminders are triggered under all circumstances until the actor explicitly unregisters them or the actor is explicitly deleted. Specifically, reminders are triggered across actor deactivations and failovers because the Dapr actors runtime persists the information about the actors' reminders using Dapr actor state provider. + +You can create a persistent reminder for an actor by calling the Http/gRPC request to Dapr. + +```md +POST/PUT http://localhost:3500/v1.0/actors///reminders/ +``` + +The reminder `duetime` and callback can be specified in the request body. The due time represents when the reminder first fires after registration. The `period` represents how often the reminder will fire after that. A due time of 0 means to fire immediately. Negative due times and negative periods are invalid. To register a reminder that fires only once, set the period to an empty string. + +The following request body configures a reminder with a `dueTime` 9 seconds and a `period` of 3 seconds. This means it will first fire after 9 seconds, then every 3 seconds after that. +```json +{ + "dueTime":"0h0m9s0ms", + "period":"0h0m3s0ms" +} +``` + +The following request body configures a reminder with a `dueTime` 0 seconds and a `period` of 3 seconds. This means it will fire immediately after registration, then every 3 seconds after that. +```json +{ + "dueTime":"0h0m0s0ms", + "period":"0h0m3s0ms" +} +``` + +The following request body configures a reminder with a `dueTime` 15 seconds and a `period` of empty string. This means it will first fire after 15 seconds, then never fire again. +```json +{ + "dueTime":"0h0m15s0ms", + "period":"" +} +``` + +#### Retrieve actor reminder + +You can retrieve the actor reminder by calling + +```md +GET http://localhost:3500/v1.0/actors///reminders/ +``` + +#### Remove the actor reminder + +You can remove the actor reminder by calling + +```md +DELETE http://localhost:3500/v1.0/actors///reminders/ +``` + +Refer [api spec]({{< ref "actors_api.md#invoke-reminder" >}}) for more details. 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 6e821fcc8..3e5f5bcf8 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 @@ -14,17 +14,23 @@ Dealing with different databases libraries, testing them, handling retries and f Dapr provides state management capabilities that include consistency and concurrency options. In this guide we'll start of with the basics: Using the key/value state API to allow an application to save, get and delete state. +## Pre-requisites + +- [Dapr CLI]({{< ref install-dapr-cli.md >}}) +- Initialized [Dapr environment]({{< ref install-dapr-selfhost.md >}}) + ## Step 1: Setup a state store A state store component represents a resource that Dapr uses to communicate with a database. -For the purpose of this how to we'll use a Redis state store, but any state store from the [supported list]({{< ref supported-state-stores >}}) will work. + +For the purpose of this guide we'll use a Redis state store, but any state store from the [supported list]({{< ref supported-state-stores >}}) will work. {{< tabs "Self-Hosted (CLI)" Kubernetes>}} {{% codetab %}} When using `dapr init` in Standalone mode, the Dapr CLI automatically provisions a state store (Redis) and creates the relevant YAML in a `components` directory, which for Linux/MacOS is `$HOME/.dapr/components` and for Windows is `%USERPROFILE%\.dapr\components` -To change the state store being used, replace the YAML under `/components` with the file of your choice. +To optionally change the state store being used, replace the YAML file `statestore.yaml` under `/components` with the file of your choice. {{% /codetab %}} {{% codetab %}} @@ -33,104 +39,105 @@ See the instructions [here]({{< ref "setup-state-store" >}}) on how to setup dif {{< /tabs >}} -## Step 2: Save state +## Step 2: Save and retrieve a single state -The following example shows how to save two key/value pairs in a single call using the state management API. +The following example shows how to 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. +{{% /alert %}} {{< tabs "HTTP API (Bash)" "HTTP API (PowerShell)" "Python SDK">}} {{% codetab %}} -Begin by ensuring a Dapr sidecar is running: +Begin by launching a Dapr sidecar: + ```bash dapr run --app-id myapp --dapr-http-port 3500 ``` -{{% alert title="Note" color="info" %}} -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 %}} - -Then in a separate terminal run: +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"}, { "key": "key2", "value": "value2"}]' http://localhost:3500/v1.0/state/statestore +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 %}} {{% codetab %}} -Begin by ensuring a Dapr sidecar is running: + +Begin by launching a Dapr sidecar: + ```bash dapr --app-id myapp --port 3500 run ``` -{{% alert title="Note" color="info" %}} -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 %}} - -Then in a separate terminal 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"}, { "key": "key2", "value": "value2"}]' -Uri 'http://localhost:3500/v1.0/state/statestore' -``` -{{% /codetab %}} - -{{% codetab %}} -Make sure to install the Dapr Python SDK with `pip3 install dapr`. Then create a file named `state.py` with: -```python -from dapr.clients import DaprClient -from dapr.clients.grpc._state import StateItem - -with DaprClient() as d: - d.save_states(store_name="statestore", - states=[ - StateItem(key="key1", value="value1"), - StateItem(key="key2", value="value2") - ]) - +Invoke-RestMethod -Method Post -ContentType 'application/json' -Body '{"key": "key1", "value": "value1"}' -Uri 'http://localhost:3500/v1.0/state/statestore' ``` -Run with `dapr run --app-id myapp run python state.py` - -{{% alert title="Note" color="info" %}} -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 %}} - -{{% /codetab %}} - -{{< /tabs >}} - -## Step 3: Get state - -The following example shows how to get an item by using a key with the state management API: - -{{< tabs "HTTP API (Bash)" "HTTP API (PowerShell)" "Python SDK">}} - -{{% codetab %}} -With the same dapr instance running from above run: -```bash -curl http://localhost:3500/v1.0/state/statestore/key1 -``` -{{% /codetab %}} - -{{% codetab %}} -With the same dapr instance running from above run: +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 %}} -Add the following code to `state.py` from above and run again: + +Save the following to a file named `pythonState.py`: + ```python - data = d.get_state(store_name="statestore", - key="key1", - state_metadata={"metakey": "metavalue"}).data +from dapr.clients import DaprClient + +with DaprClient() as d: + d.save_state(store_name="statestore", key="myFirstKey", value="myFirstValue" ) + print("State has been stored") + + data = d.get_state(store_name="statestore", key="myFirstKey").data print(f"Got value: {data}") + ``` + +Once saved run the following command to launch a Dapr sidecar and run the Python 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' +``` + {{% /codetab %}} {{< /tabs >}} -## Step 4: Delete state + +## Step 3: Delete state The following example shows how to delete an item by using a key with the state management API: @@ -153,16 +160,231 @@ Try getting state again and note that no value is returned. {{% /codetab %}} {{% codetab %}} -Add the following code to `state.py` from above and run again: + +Update `pythonState.py` with: + ```python - d.delete_state(store_name="statestore"", - key="key1", - state_metadata={"metakey": "metavalue"}) - data = d.get_state(store_name="statestore", - key="key1", - state_metadata={"metakey": "metavalue"}).data +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 %}} {{< /tabs >}} + +## Step 4: Save and retrieve multiple states + +Dapr also allows you to save and retrieve multiple states in the same call. + +{{< tabs "HTTP API (Bash)" "HTTP API (PowerShell)" "Python SDK">}} + +{{% codetab %}} +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": "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' +``` + +{{% /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 +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") + + d.save_states(store_name="statestore", states=[s1,s2]) + print("States have been stored") + + items = d.get_states(store_name="statestore", keys=["key1", "key2"]).items + print(f"Got items: {[i.data for i in items]}") +``` + +Now run your program with: + +```bash +dapr --app-id myapp run python pythonState.py +``` + +You should see an output similar to the following: + +```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 %}} + +{{< /tabs >}} + +## Step 5: Perform state transactions + +{{% alert title="Note" color="warning" %}} +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" >}} + +{{% 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 +``` + +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 +``` +{{% /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' +``` + +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_states(store_name="statestore", states=[s1,s2]) + print("States have been stored") + + d.execute_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_states(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 %}} + +{{< /tabs >}} + +## Next steps + +- 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 >}}) \ No newline at end of file diff --git a/daprdocs/content/en/developing-applications/building-blocks/state-management/howto-share-state.md b/daprdocs/content/en/developing-applications/building-blocks/state-management/howto-share-state.md new file mode 100644 index 000000000..bfe44dcfe --- /dev/null +++ b/daprdocs/content/en/developing-applications/building-blocks/state-management/howto-share-state.md @@ -0,0 +1,95 @@ +--- +type: docs +title: "How-To: Share state between applications" +linkTitle: "How-To: Share state between applications" +weight: 400 +description: "Choose different strategies for sharing state between different applications" +--- + +## Introduction + +Dapr offers developers different ways to share state between applications. + +Different architectures might have different needs when it comes to sharing state. For example, in one scenario you may want to encapsulate all state within a given application and have Dapr manage the access for you. In a different scenario, you may need to have two applications working on the same state be able to get and save the same keys. + +To enable state sharing, Dapr supports the following key prefixes strategies: + +* **`appid`** - This is the default strategy. the `appid` prefix allows state to be managed only by the app with the specified `appid`. All state keys will be prefixed with the `appid`, and are scoped for the application. + +* **`name`** - This setting uses the name of the state store component as the prefix. Multiple applications can share the same state for a given state store. + +* **`none`** - This setting uses no prefixing. Multiple applications share state across different state stores. + +## Specifying a state prefix strategy + +To specify a prefix strategy, add a metadata key named `keyPrefix` on a state component: + +```yaml +apiVersion: dapr.io/v1alpha1 +kind: Component +metadata: + name: statestore + namespace: production +spec: + type: state.redis + version: v1 + metadata: + - name: keyPrefix + value: +``` + +## Examples + +The following examples will show you how state retrieval looks like with each of the supported prefix strategies: + +### `appid` (default) + +A Dapr application with app id `myApp` is saving state into a state store named `redis`: + +```shell +curl -X POST http://localhost:3500/v1.0/state/redis \ + -H "Content-Type: application/json" + -d '[ + { + "key": "darth", + "value": "nihilus" + } + ]' +``` + +The key will be saved as `myApp||darth`. + +### `name` + +A Dapr application with app id `myApp` is saving state into a state store named `redis`: + +```shell +curl -X POST http://localhost:3500/v1.0/state/redis \ + -H "Content-Type: application/json" + -d '[ + { + "key": "darth", + "value": "nihilus" + } + ]' +``` + +The key will be saved as `redis||darth`. + +### `none` + +A Dapr application with app id `myApp` is saving state into a state store named `redis`: + +```shell +curl -X POST http://localhost:3500/v1.0/state/redis \ + -H "Content-Type: application/json" + -d '[ + { + "key": "darth", + "value": "nihilus" + } + ]' +``` + +The key will be saved as `darth`. + diff --git a/daprdocs/content/en/developing-applications/building-blocks/state-management/query-state-store/_index.md b/daprdocs/content/en/developing-applications/building-blocks/state-management/query-state-store/_index.md index 542bd4b9c..489c77675 100644 --- a/daprdocs/content/en/developing-applications/building-blocks/state-management/query-state-store/_index.md +++ b/daprdocs/content/en/developing-applications/building-blocks/state-management/query-state-store/_index.md @@ -2,7 +2,7 @@ type: docs title: "Work with backend state stores" linkTitle: "Backend stores" -weight: 400 +weight: 500 description: "Guides for working with specific backend states stores" --- diff --git a/daprdocs/content/en/developing-applications/building-blocks/state-management/state-management-overview.md b/daprdocs/content/en/developing-applications/building-blocks/state-management/state-management-overview.md index e684823a3..6c0344431 100644 --- a/daprdocs/content/en/developing-applications/building-blocks/state-management/state-management-overview.md +++ b/daprdocs/content/en/developing-applications/building-blocks/state-management/state-management-overview.md @@ -8,6 +8,8 @@ description: "Overview of the state management building block" ## Introduction + + Dapr offers key/value storage APIs for state management. If a microservice uses state management, it can use these APIs to leverage any of the [supported state stores]({{< ref supported-state-stores.md >}}), without adding or learning a third party SDK. When using state management your application can leverage several features that would otherwise be complicated and error-prone to build yourself such as: @@ -16,39 +18,30 @@ When using state management your application can leverage several features that - Retry policies - Bulk [CRUD](https://en.wikipedia.org/wiki/Create,_read,_update_and_delete) operations -See below for a diagram of state management's high level architecture. - - - ## Features -- [State management API](#state-management-api) -- [State store behaviors](#state-store-behaviors) -- [Concurrency](#concurrency) -- [Consistency](#consistency) -- [Retry policies](#retry-policies) -- [Bulk operations](#bulk-operations) -- [Querying state store directly](#querying-state-store-directly) - ### State management API -Developers can use the state management API to retrieve, save and delete state values by providing keys. +Developers can use the [state management API]({{< ref state_api.md >}}) to retrieve, save and delete state values by providing keys. -Dapr data stores are components. Dapr ships with [Redis](https://redis.io) out-of-box for local development in self hosted mode. Dapr allows you to plug in other data stores as components such as [Azure CosmosDB](https://azure.microsoft.com/services/cosmos-db/), [SQL Server](https://azure.microsoft.com/services/sql-database/), [AWS DynamoDB](https://aws.amazon.com/DynamoDB), [GCP Cloud Spanner](https://cloud.google.com/spanner) and [Cassandra](http://cassandra.apache.org/). +### Pluggable state stores -Visit [State API]({{< ref state_api.md >}}) for more information. +Dapr data stores are modeled as pluggable components, which can be swapped out without any changes to your service code. Check out the [full list of state stores]({{< ref supported-state-stores >}}) to see what Dapr supports. -> **NOTE:** Dapr prefixes state keys with the ID of the current Dapr instance. This allows multiple Dapr instances to share the same state store. +### Configurable state store behavior -### State store behaviors +Dapr allows developers to attach additional metadata to a state operation request that describes how the request is expected to be handled. -Dapr allows developers to attach to a state operation request additional metadata that describes how the request is expected to be handled. For example, you can attach concurrency requirements, consistency requirements, and retry policy to any state operation requests. +For example, you can attach: +- Concurrency requirements +- Consistency requirements +- Retry policies -By default, your application should assume a data store is **eventually consistent** and uses a **last-write-wins** concurrency pattern. On the other hand, if you do attach metadata to your requests, Dapr passes the metadata along with the requests to the state store and expects the data store to fulfill the requests. +By default, your application should assume a data store is **eventually consistent** and uses a **last-write-wins** concurrency pattern. -Not all stores are created equal. To ensure portability of your application, you can query the capabilities of the store and make your code adaptive to different store capabilities. +Not all stores are created equal. To ensure portability of your application you can query the capabilities of the store and make your code adaptive to different store capabilities. -The following table summarizes the capabilities of existing data store implementations. +The following table gives examples of capabilities of popular data store implementations. | Store | Strong consistent write | Strong consistent read | ETag | |-------------------|-------------------------|------------------------|------| @@ -60,13 +53,15 @@ The following table summarizes the capabilities of existing data store implement ### Concurrency -Dapr supports optimistic concurrency control (OCC) using ETags. When a state is requested, Dapr always attaches an **ETag** property to the returned state. And when the user code tries to update or delete a state, it's expected to attach the ETag through the **If-Match** header. The write operation can succeed only when the provided ETag matches with the ETag in the state store. +Dapr supports optimistic concurrency control (OCC) using ETags. When a state is requested, Dapr always attaches an **ETag** property to the returned state. When the user code tries to update or delete a state, it's expected to attach the ETag through the **If-Match** header. The write operation can succeed only when the provided ETag matches with the ETag in the state store. -Dapr chooses OCC because in many applications, data update conflicts are rare because clients are naturally partitioned by business contexts to operate on different data. However, if your application chooses to use ETags, a request may get rejected because of mismatched ETags. It's recommended that you use a [retry policy](#Retry-Policies) to compensate for such conflicts when using ETags. +Dapr chooses OCC because in many applications, data update conflicts are rare because clients are naturally partitioned by business contexts to operate on different data. However, if your application chooses to use ETags, a request may get rejected because of mismatched ETags. It's recommended that you use a [retry policy](#retry-policies) to compensate for such conflicts when using ETags. If your application omits ETags in writing requests, Dapr skips ETag checks while handling the requests. This essentially enables the **last-write-wins** pattern, compared to the **first-write-wins** pattern with ETags. -> **NOTE:** For stores that don't natively support ETags, it's expected that the corresponding Dapr state store implementation simulates ETags and follows the Dapr state management API specification when handling states. Because Dapr state store implementations are technically clients to the underlying data store, such simulation should be straightforward using the concurrency control mechanisms provided by the store. +{{% alert title="Note on ETags" color="primary" %}} +For stores that don't natively support ETags, it's expected that the corresponding Dapr state store implementation simulates ETags and follows the Dapr state management API specification when handling states. Because Dapr state store implementations are technically clients to the underlying data store, such simulation should be straightforward using the concurrency control mechanisms provided by the store. +{{% /alert %}} ### Consistency @@ -74,15 +69,21 @@ Dapr supports both **strong consistency** and **eventual consistency**, with eve When strong consistency is used, Dapr waits for all replicas (or designated quorums) to acknowledge before it acknowledges a write request. When eventual consistency is used, Dapr returns as soon as the write request is accepted by the underlying data store, even if this is a single replica. +Visit the [API reference]({{< ref state_api.md >}}) to learn how to set consistency options. + ### Retry policies Dapr allows you to attach a retry policy to any write request. A policy is described by an **retryInterval**, a **retryPattern** and a **retryThreshold**. Dapr keeps retrying the request at the given interval up to the specified threshold. You can choose between a **linear** retry pattern or an **exponential** (backoff) pattern. When the **exponential** pattern is used, the retry interval is doubled after each attempt. +Visit the [API reference]({{< ref state_api.md >}}) to learn how to set retry policy options. + ### Bulk operations Dapr supports two types of bulk operations - **bulk** or **multi**. You can group several requests of the same type into a bulk (or a batch). Dapr submits requests in the bulk as individual requests to the underlying data store. In other words, bulk operations are not transactional. On the other hand, you can group requests of different types into a multi-operation, which is handled as an atomic transaction. -### Querying state store directly +Visit the [API reference]({{< ref state_api.md >}}) to learn how use bulk and multi options. + +### Query state store directly Dapr saves and retrieves state values without any transformation. You can query and aggregate state directly from the [underlying state store]({{< ref query-state-store >}}). @@ -92,8 +93,6 @@ For example, to get all state keys associated with an application ID "myApp" in KEYS "myApp*" ``` -> **NOTE:** See [How to query Redis store]({{< ref query-redis-store.md >}} ) for details on how to query a Redis store. - #### Querying actor state If the data store supports SQL queries, you can query an actor's state using SQL queries. For example use: @@ -108,10 +107,12 @@ You can also perform aggregate queries across actor instances, avoiding the comm SELECT AVG(value) FROM StateTable WHERE Id LIKE '||||*||temperature' ``` -> **NOTE:** Direct queries of the state store are not governed by Dapr concurrency control, since you are not calling through the Dapr runtime. What you see are snapshots of committed data which are acceptable for read-only queries across multiple actors, however writes should be done via the actor instances. +{{% alert title="Note on direct queries" color="primary" %}} +Direct queries of the state store are not governed by Dapr concurrency control, since you are not calling through the Dapr runtime. What you see are snapshots of committed data which are acceptable for read-only queries across multiple actors, however writes should be done via the actor instances. +{{% /alert %}} ## Next steps -* Follow the [state store setup guides]({{< ref setup-state-store >}}) -* Read the [state management API specification]({{< ref state_api.md >}}) -* Read the [actors API specification]({{< ref actors_api.md >}}) +- Follow the [state store setup guides]({{< ref setup-state-store >}}) +- Read the [state management API specification]({{< ref state_api.md >}}) +- Read the [actors API specification]({{< ref actors_api.md >}}) diff --git a/daprdocs/content/en/developing-applications/sdks/_index.md b/daprdocs/content/en/developing-applications/sdks/_index.md index a905eaabf..c051aba74 100644 --- a/daprdocs/content/en/developing-applications/sdks/_index.md +++ b/daprdocs/content/en/developing-applications/sdks/_index.md @@ -1,22 +1,44 @@ --- type: docs -title: "SDKs" +title: "Dapr Software Development Kits (SDKs)" linkTitle: "SDKs" weight: 20 description: "Use your favorite languages with Dapr" +no_list: true --- -### .NET -See the [.NET SDK repository](https://github.com/dapr/dotnet-sdk) +The Dapr SDKs are the easiest way for you to get Dapr into your application. Choose your favorite language and get up and running with Dapr in minutes. -### Java -See the [Java SDK repository](https://github.com/dapr/java-sdk) +## SDK packages -### Go -See the [Go SDK repository](https://github.com/dapr/go-sdk) +- **Client SDK**: The Dapr client allows you to invoke Dapr building block APIs and perform actions such as: + - [Invoke]({{< ref service-invocation >}}) methods on other services + - Store and get [state]({{< ref state-management >}}) + - [Publish and subscribe]({{< ref pubsub >}}) to message topics + - Interact with external resources through input and output [bindings]({{< ref bindings >}}) + - Get [secrets]({{< ref secrets >}}) from secret stores + - Interact with [virtual actors]({{< ref actors >}}) +- **Service extensions**: The Dapr service extensions allow you to create services that can: + - Be [invoked]({{< ref service-invocation >}}) by other services + - [Subscribe]({{< ref pubsub >}}) to topics +- **Actor SDK**: The Dapr Actor SDK allows you to build virtual actors with: + - Methods that can be [invoked]({{< ref "howto-actors.md#actor-method-invocation" >}}) by other services + - [State]({{< ref "howto-actors.md#actor-state-management" >}}) that can be stored and retrieved + - [Timers]({{< ref "howto-actors.md#actor-timers" >}}) with callbacks + - Persistent [reminders]({{< ref "howto-actors.md#actor-reminders" >}}) -### Python -See the [Python SDK repository](https://github.com/dapr/python-sdk) +## SDK languages -### Javascript -See the [Javascript SDK repository](https://github.com/dapr/js-sdk) \ No newline at end of file +| Language | State | Client SDK | Service Extensions | Actor SDK | +|----------|:-----:|:----------:|:-----------:|:---------:| +| [.NET](https://github.com/dapr/dotnet-sdk) | In Development | ✔ | ASP.NET Core | ✔ | +| [Python]({{< ref python >}}) | In Development | ✔ | [gRPC]({{< ref python-grpc.md >}}) | [FastAPI]({{< ref python-fastapi.md >}})
[Flask]({{< ref python-flask.md >}}) | +| [Java](https://github.com/dapr/java-sdk) | In Development | ✔ | Spring Boot | ✔ | +| [Go](https://github.com/dapr/go-sdk) | In Development | ✔ | ✔ | | +| [C++](https://github.com/dapr/cpp-sdk) | Backlog | ✔ | | +| [Rust]() | Backlog | ✔ | | | +| [Javascript]() | Backlog | ✔ | | + +## Further reading + +- [Serialization in the Dapr SDKs]({{< ref sdk-serialization.md >}}) \ No newline at end of file diff --git a/daprdocs/content/en/developing-applications/sdks/serialization.md b/daprdocs/content/en/developing-applications/sdks/sdk-serialization.md similarity index 97% rename from daprdocs/content/en/developing-applications/sdks/serialization.md rename to daprdocs/content/en/developing-applications/sdks/sdk-serialization.md index ae7b30e60..0e8b1f522 100644 --- a/daprdocs/content/en/developing-applications/sdks/serialization.md +++ b/daprdocs/content/en/developing-applications/sdks/sdk-serialization.md @@ -2,7 +2,10 @@ type: docs title: "Serialization in Dapr's SDKs" linkTitle: "Serialization" -weight: 1000 +description: "How Dapr serializes data within the SDKs" +weight: 2000 +aliases: + - '/developing-applications/sdks/serialization/' --- An SDK for Dapr should provide serialization for two use cases. First, for API objects sent through request and response payloads. Second, for objects to be persisted. For both these use cases, a default serialization is provided. In the Java SDK, it is the [DefaultObjectSerializer](https://dapr.github.io/java-sdk/io/dapr/serializer/DefaultObjectSerializer.html) class, providing JSON serialization. diff --git a/daprdocs/content/en/operations/components/component-schema.md b/daprdocs/content/en/operations/components/component-schema.md new file mode 100644 index 000000000..b577174d9 --- /dev/null +++ b/daprdocs/content/en/operations/components/component-schema.md @@ -0,0 +1,52 @@ +--- +type: docs +title: "Component schema" +linkTitle: "Component schema" +weight: 100 +description: "The basic schema for a Dapr component" +--- + +Dapr defines and registers components using a [CustomResourceDefinition](https://kubernetes.io/docs/tasks/extend-kubernetes/custom-resources/custom-resource-definitions/). All components are defined as a CRD and can be applied to any hosting environment where Dapr is running, not just Kubernetes. + +## Format + +```yaml +apiVersion: dapr.io/v1alpha1 +kind: Component +metadata: + name: [COMPONENT-NAME] + namespace: [COMPONENT-NAMESPACE] +spec: + type: [COMPONENT-TYPE] + version: v1 + initTimeout: [TIMEOUT-DURATION] + ignoreErrors: [BOOLEAN] + metadata: + - name: [METADATA-NAME] + value: [METADATA-VALUE] +``` + +## Fields + +| Field | Required | Details | Example | +|--------------------|:--------:|---------|---------| +| apiVersion | Y | The version of the Dapr (and Kubernetes if applicable) API you are calling | `dapr.io/v1alpha1` +| kind | Y | The type of CRD. For components is must always be `Component` | `Component` +| **metadata** | - | **Information about the component registration** | +| metadata.name | Y | The name of the component | `prod-statestore` +| metadata.namespace | N | The namespace for the component for hosting environments with namespaces | `myapp-namespace` +| **spec** | - | **Detailed information on the component resource** +| spec.type | Y | The type of the component | `state.redis` +| spec.version | Y | The version of the component | `v1` +| spec.initTimeout | N | The timeout duration for the initialization of the component. Default is 30s | `5m`, `1h`, `20s` +| spec.ignoreErrors | N | Tells the Dapr sidecar to continue initialization if the component fails to load. Default is false | `false` +| **spec.metadata** | - | **A key/value pair of component specific configuration. See your component definition for fields**| + +## Further reading +- [Components concept]({{< ref components-concept.md >}}) +- [Reference secrets in component definitions]({{< ref component-secrets.md >}}) +- [Supported state stores]({{< ref supported-state-stores >}}) +- [Supported pub/sub brokers]({{< ref supported-pubsub >}}) +- [Supported secret stores]({{< ref supported-secret-stores >}}) +- [Supported bindings]({{< ref supported-bindings >}}) +- [Set component scopes]({{< ref component-scopes.md >}}) \ No newline at end of file diff --git a/daprdocs/content/en/operations/components/component-scopes.md b/daprdocs/content/en/operations/components/component-scopes.md index 56c980e68..ca7d2ca2d 100644 --- a/daprdocs/content/en/operations/components/component-scopes.md +++ b/daprdocs/content/en/operations/components/component-scopes.md @@ -2,7 +2,7 @@ type: docs title: "How-To: Scope components to one or more applications" linkTitle: "How-To: Set component scopes" -weight: 100 +weight: 200 description: "Limit component access to particular Dapr instances" --- diff --git a/daprdocs/content/en/operations/components/component-secrets.md b/daprdocs/content/en/operations/components/component-secrets.md index bd4c4f20f..0d552fc72 100644 --- a/daprdocs/content/en/operations/components/component-secrets.md +++ b/daprdocs/content/en/operations/components/component-secrets.md @@ -2,7 +2,7 @@ type: docs title: "How-To: Reference secrets in components" linkTitle: "How-To: Reference secrets" -weight: 200 +weight: 300 description: "How to securly reference secrets from a component definition" --- diff --git a/daprdocs/content/en/operations/troubleshooting/common_issues.md b/daprdocs/content/en/operations/troubleshooting/common_issues.md index b9e19f66d..f904b9149 100644 --- a/daprdocs/content/en/operations/troubleshooting/common_issues.md +++ b/daprdocs/content/en/operations/troubleshooting/common_issues.md @@ -82,7 +82,7 @@ The most common cause of this failure is that a component (such as a state store To diagnose the root cause: -- Significantly increase the liveness probe delay - [link]{{< ref "kubernetes-overview.md" >}}) +- Significantly increase the liveness probe delay - [link]({{< ref "kubernetes-overview.md" >}}) - Set the log level of the sidecar to debug - [link]({{< ref "logs-troubleshooting.md#setting-the-sidecar-log-level" >}}) - Watch the logs for meaningful information - [link]({{< ref "logs-troubleshooting.md#viewing-logs-on-kubernetes" >}}) diff --git a/sdkdocs/python b/sdkdocs/python new file mode 160000 index 000000000..3d610a4db --- /dev/null +++ b/sdkdocs/python @@ -0,0 +1 @@ +Subproject commit 3d610a4db9a589d85de7fa20c42be1934a1d6f0e From 047894bc344d72ea8a7e16353dcf8045102ed84f Mon Sep 17 00:00:00 2001 From: Sander Molenkamp Date: Sat, 23 Jan 2021 12:00:28 +0100 Subject: [PATCH 3/7] Fix SQL server query state store --- .../query-state-store/query-sqlserver-store.md | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/daprdocs/content/en/developing-applications/building-blocks/state-management/query-state-store/query-sqlserver-store.md b/daprdocs/content/en/developing-applications/building-blocks/state-management/query-state-store/query-sqlserver-store.md index 981db0e45..641724959 100644 --- a/daprdocs/content/en/developing-applications/building-blocks/state-management/query-state-store/query-sqlserver-store.md +++ b/daprdocs/content/en/developing-applications/building-blocks/state-management/query-state-store/query-sqlserver-store.md @@ -19,17 +19,17 @@ The easiest way to connect to your SQL Server instance is to use the [Azure Data To get all state keys associated with application "myapp", use the query: ```sql -SELECT * FROM states WHERE [Key] LIKE 'myapp-%' +SELECT * FROM states WHERE [Key] LIKE 'myapp||%' ``` -The above query returns all rows with id containing "myapp-", which is the prefix of the state keys. +The above query returns all rows with id containing "myapp||", which is the prefix of the state keys. ## 3. Get specific state data To get the state data by a key "balance" for the application "myapp", use the query: ```sql -SELECT * FROM states WHERE [Key] = 'myapp-balance' +SELECT * FROM states WHERE [Key] = 'myapp||balance' ``` Then, read the **Data** field of the returned row. @@ -37,7 +37,7 @@ Then, read the **Data** field of the returned row. To get the state version/ETag, use the command: ```sql -SELECT [RowVersion] FROM states WHERE [Key] = 'myapp-balance' +SELECT [RowVersion] FROM states WHERE [Key] = 'myapp||balance' ``` ## 4. Get filtered state data @@ -53,13 +53,13 @@ SELECT * FROM states WHERE JSON_VALUE([Data], '$.color') = 'blue' To get all the state keys associated with an actor with the instance ID "leroy" of actor type "cat" belonging to the application with ID "mypets", use the command: ```sql -SELECT * FROM states WHERE [Key] LIKE 'mypets-cat-leroy-%' +SELECT * FROM states WHERE [Key] LIKE 'mypets||cat||leroy||%' ``` And to get a specific actor state such as "food", use the command: ```sql -SELECT * FROM states WHERE [Key] = 'mypets-cat-leroy-food' +SELECT * FROM states WHERE [Key] = 'mypets||cat||leroy||food' ``` > **WARNING:** You should not manually update or delete states in the store. All writes and delete operations should be done via the Dapr runtime. From 9ca443d6483df0349100ec8baef97baea53639d6 Mon Sep 17 00:00:00 2001 From: Sander Molenkamp Date: Sat, 23 Jan 2021 12:01:16 +0100 Subject: [PATCH 4/7] Fix API ref --- daprdocs/content/en/reference/api/actors_api.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/daprdocs/content/en/reference/api/actors_api.md b/daprdocs/content/en/reference/api/actors_api.md index 78c30d1e2..b20059a3a 100644 --- a/daprdocs/content/en/reference/api/actors_api.md +++ b/daprdocs/content/en/reference/api/actors_api.md @@ -665,6 +665,6 @@ The state namespace created by Dapr for actors is composed of the following item - Key - A key for the specific state value. An actor ID can hold multiple state keys. The following example shows how to construct a key for the state of an actor instance under the `myapp` App ID namespace: -`myapp-cat-hobbit-food` +`myapp||cat||hobbit||food` In the example above, we are getting the value for the state key `food`, for the actor ID `hobbit` with an actor type of `cat`, under the App ID namespace of `myapp`. From 003ea2c3fd53a8d649b3d93f592885b3ca69fb70 Mon Sep 17 00:00:00 2001 From: Aaron Crawfis Date: Mon, 25 Jan 2021 10:32:09 -0800 Subject: [PATCH 5/7] Fix broken link --- .../building-blocks/pubsub/pubsub-overview.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/daprdocs/content/en/developing-applications/building-blocks/pubsub/pubsub-overview.md b/daprdocs/content/en/developing-applications/building-blocks/pubsub/pubsub-overview.md index 6e563b842..4c059a550 100644 --- a/daprdocs/content/en/developing-applications/building-blocks/pubsub/pubsub-overview.md +++ b/daprdocs/content/en/developing-applications/building-blocks/pubsub/pubsub-overview.md @@ -53,7 +53,7 @@ Dapr allows two methods by which you can subscribe to topics: **declarative**, w ### Message Delivery -In principle, Dapr considers message successfully delivered when the subscriber responds with a non-error response after processing the message. For more granular control, Dapr's publish/subscribe API also provides explicit statuses, defined in the response payload, which the subscriber can use to indicate the specific handling instructions to Dapr (e.g. `RETRY` or `DROP`). For more information message routing see [Dapr publish/subscribe API documentation] ({{< ref "pubsub_api.md#provide-routes-for-dapr-to-deliver-topic-events" >}}) +In principle, Dapr considers message successfully delivered when the subscriber responds with a non-error response after processing the message. For more granular control, Dapr's publish/subscribe API also provides explicit statuses, defined in the response payload, which the subscriber can use to indicate the specific handling instructions to Dapr (e.g. `RETRY` or `DROP`). For more information message routing see [Dapr publish/subscribe API documentation]({{< ref "pubsub_api.md#provide-routes-for-dapr-to-deliver-topic-events" >}}) ### At-Least-Once guarantee From f377db8fd543254c0b8f055b5f29ed9e7f7515fb Mon Sep 17 00:00:00 2001 From: Aaron Crawfis Date: Mon, 25 Jan 2021 13:21:37 -0800 Subject: [PATCH 6/7] Add codespaces page --- .../ides/codespaces.md | 32 ++++++++++++++++++ daprdocs/static/images/codespaces-create.png | Bin 0 -> 57904 bytes 2 files changed, 32 insertions(+) create mode 100644 daprdocs/content/en/developing-applications/ides/codespaces.md create mode 100644 daprdocs/static/images/codespaces-create.png diff --git a/daprdocs/content/en/developing-applications/ides/codespaces.md b/daprdocs/content/en/developing-applications/ides/codespaces.md new file mode 100644 index 000000000..af4de0e7d --- /dev/null +++ b/daprdocs/content/en/developing-applications/ides/codespaces.md @@ -0,0 +1,32 @@ +--- +type: docs +title: "Developing with GitHub Codespaces" +linkTitle: "GitHub Codespaces" +weight: 3000 +description: "How to get up and running with Dapr in a GitHub Codespace" +--- + +[GitHub Codespaces](https://github.com/features/codespaces) are the easiest way to get up and running in a Dapr environment. In as little as a single click you have the environment, packages, code, samples, and documentation all ready to go in your browser. + +{{% alert title="Private Beta" color="warning" %}} +GitHub Codespaces is currently in a private beta. Sign up [here](https://github.com/features/codespaces/signup). +{{% /alert %}} + +## Features + +- **Click and Run**: Get a dedicated and sandboxed environment with all of the required frameworks and packages ready to go. +- **Usage-based Billing**: Only pay for the time you spend developing in the Codespace. Environments are spun down automatically when not in use. +- **Portable**: Run in your browser or in Visual Studio Code + +## Open a Dapr repo in a Codespace + +To open a Dapr repository in a Codespace simply select "Code" from the repo homepage and "Open with Codespaces": + +Screenshot of creating a Dapr Codespace + +### Supported repos + +- [Python SDK](https://github.com/dapr/python-sdk) + +## Related links +- [GitHub documentation](https://docs.github.com/en/github/developing-online-with-codespaces/about-codespaces) \ No newline at end of file diff --git a/daprdocs/static/images/codespaces-create.png b/daprdocs/static/images/codespaces-create.png new file mode 100644 index 0000000000000000000000000000000000000000..2758086b9560015bca040758182e702a5c90b22a GIT binary patch literal 57904 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%0D|y&&F9=e|t)P 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 U68H8_4Tk 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 literal 0 HcmV?d00001 From 14660142c789f08c4f7227d51f1be4907ba6a605 Mon Sep 17 00:00:00 2001 From: Nghia Tran Date: Wed, 27 Jan 2021 14:47:50 -0800 Subject: [PATCH 7/7] Update OpenTelemetry Collector to not use queued_retry processor `queued_retry` processor has been deprecated, and we didn't use any retry settings. Remove it here to avoid errors with latest version of OpenTelemetry Collector agent. --- .../open-telemetry-collector/open-telemetry-collector.yaml | 4 ---- 1 file changed, 4 deletions(-) diff --git a/daprdocs/static/docs/open-telemetry-collector/open-telemetry-collector.yaml b/daprdocs/static/docs/open-telemetry-collector/open-telemetry-collector.yaml index bae9a336e..aa760183b 100644 --- a/daprdocs/static/docs/open-telemetry-collector/open-telemetry-collector.yaml +++ b/daprdocs/static/docs/open-telemetry-collector/open-telemetry-collector.yaml @@ -10,9 +10,6 @@ data: receivers: zipkin: endpoint: 0.0.0.0:9411 - processors: - queued_retry: - batch: extensions: health_check: pprof: @@ -37,7 +34,6 @@ data: traces: receivers: [zipkin] exporters: [azuremonitor,logging] - processors: [batch, queued_retry] --- apiVersion: v1 kind: Service