Skip to content

Commit

Permalink
more changes per mark
Browse files Browse the repository at this point in the history
Signed-off-by: Hannah Hunter <hannahhunter@microsoft.com>
  • Loading branch information
hhunter-ms committed Mar 30, 2023
1 parent 333f3ac commit fe468ff
Show file tree
Hide file tree
Showing 7 changed files with 500 additions and 343 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -2,31 +2,37 @@
type: docs
title: "How-to: Enable and use actor reentrancy in Dapr"
linkTitle: "How-To: Actor reentrancy"
weight: 50
weight: 70
description: Learn more about actor reentrancy
---

## Actor reentrancy
A core tenet of the virtual actor pattern is the single-threaded nature of actor execution. Without reentrancy, the Dapr runtime locks on all actor requests, even those that are in the same call chain. A second request could not start until the first had completed. This means an actor cannot call itself, or have another actor call into it even if it is part of the same chain. Reentrancy solves this by allowing requests from the same chain, or context, to re-enter into an already locked actor. This is especially useful in scenarios where an actor wants to call a method on itself or when actors are used in workflows where other actors are used to perform work, and they then call back onto the coordinating actor. Examples of chains that reentrancy allows are shown below:
A core tenet of the [virtual actor pattern](https://www.microsoft.com/research/project/orleans-virtual-actors/) is the single-threaded nature of actor execution. Without reentrancy, the Dapr runtime locks on all actor requests. A second request wouldn't be able to start until the first had completed. This means an actor cannot call itself, or have another actor call into it, even if it's part of the same call chain.

Reentrancy solves this by allowing requests from the same chain, or context, to re-enter into an already locked actor. This proves useful in scenarios where:
- An actor wants to call a method on itself
- Actors are used in workflows to perform work, then call back onto the coordinating actor.

Examples of chains that reentrancy allows are shown below:

```
Actor A -> Actor A
ActorA -> Actor B -> Actor A
```

With reentrancy, there can be more complex actor calls without sacrificing the single-threaded behavior of virtual actors.
With reentrancy, you can perform more complex actor calls, without sacrificing the single-threaded behavior of virtual actors.

<img src="/images/actor-reentrancy.png" width=1000 height=500 alt="Diagram showing reentrancy for a coordinator workflow actor calling worker actors or an actor calling an method on itself">

The `maxStackDepth` parameter sets a value that controls how many reentrant calls be made to the same actor. By default this is set to 32, which is more than sufficient in most cases.
The `maxStackDepth` parameter sets a value that controls how many reentrant calls can be made to the same actor. By default, this is set to **32**, which is more than sufficient in most cases.

## Enable Actor Reentrancy with Actor Configuration
## Configure the actor runtime to enable reentrancy

The actor that will be reentrant must provide configuration to use reentrancy. This is done by the actor's endpoint for `GET /dapr/config`, similar to other actor configuration elements.
The reentrant actor must provide the appropriate configuration. This is done by the actor's endpoint for `GET /dapr/config`, similar to other actor configuration elements.

{{< tabs Dotnet Python Go >}}
{{< tabs ".NET" JavaScript Python Java Go >}}

{{% codetab %}}
<!--dotnet-->

```csharp
public class Startup
Expand All @@ -50,6 +56,27 @@ public class Startup
{{% /codetab %}}

{{% codetab %}}
<!--javascript-->

```js
import { CommunicationProtocolEnum, DaprClient, DaprServer } from "@dapr/dapr";

// Configure the actor runtime with the DaprClientOptions.
const clientOptions = {
actor: {
reentrancy: {
enabled: true,
maxStackDepth: 32,
},
},
};
```

{{% /codetab %}}

{{% codetab %}}
<!--python-->

```python
from fastapi import FastAPI
from dapr.ext.fastapi import DaprActor
Expand All @@ -75,6 +102,16 @@ def do_something_reentrant():
```
{{% /codetab %}}

{{% codetab %}}
<!--java-->

```java

```

{{% /codetab %}}


{{% codetab %}}

Here is a snippet of an actor written in Golang providing the reentrancy configuration via the HTTP API. Reentrancy has not yet been included into the Go SDK.
Expand Down Expand Up @@ -105,10 +142,11 @@ func configHandler(w http.ResponseWriter, r *http.Request) {
}
```

### Handling reentrant requests
### Handle reentrant requests

The key to a reentrant request is the `Dapr-Reentrancy-Id` header. The value of this header is used to match requests to their call chain and allow them to bypass the actor's lock.

The header is generated by the Dapr runtime for any actor request that has a reentrant config specified. Once it is generated, it is used to lock the actor and must be passed to all future requests. Below are the snippets of code from an actor handling this:
The header is generated by the Dapr runtime for any actor request that has a reentrant config specified. Once it is generated, it is used to lock the actor and must be passed to all future requests. Below is an example of an actor handling a reentrant request:

```go
func reentrantCallHandler(w http.ResponseWriter, r *http.Request) {
Expand All @@ -134,7 +172,19 @@ func reentrantCallHandler(w http.ResponseWriter, r *http.Request) {

{{< /tabs >}}

## Demo

Watch this [video](https://www.youtube.com/watch?v=QADHQ5v-gww&list=PLcip_LgkYwzuF-OV6zKRADoiBvUvGhkao&t=674s) on how to use actor reentrancy.

<div class="embed-responsive embed-responsive-16by9">
<iframe width="560" height="315" src="https://www.youtube-nocookie.com/embed/QADHQ5v-gww?start=674" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture" allowfullscreen></iframe>
</div>

## Next steps

{{< button text="Actors in the Dapr SDKs" page="developing-applications/sdks/#sdk-languages" >}}

## Related links

- [Actors API reference]({{< ref actors_api.md >}})
- [Actors overview]({{< ref actors-overview.md >}})
Original file line number Diff line number Diff line change
Expand Up @@ -100,8 +100,4 @@ Learn more about actor types and ids specific to each SDK:

- [Actors API reference]({{< ref actors_api.md >}})
- [Actors overview]({{< ref actors-overview.md >}})
- [How to: Use virtual actors in Dapr]({{< ref howto-actors.md >}})
- Actors using the:
- [.NET SDK]({{< ref dotnet-actors.md >}})
- [Python SDK]({{< ref python-actor.md >}})
- [Java SDK]({{< ref js-actors.md >}})
- [How to: Use virtual actors in Dapr]({{< ref howto-actors.md >}})
Original file line number Diff line number Diff line change
Expand Up @@ -105,32 +105,14 @@ The functionality of timers and reminders is very similar. The main difference i
This distinction allows users to trade off between light-weight but stateless timers vs. more resource-demanding but stateful reminders.

- [Learn more about actor timers.]({{< ref "actors-features-concepts.md#timers" >}})
- [Learn more about actor timers.]({{< ref "actors-features-concepts.md#reminders" >}})
- [Learn more about actor reminders.]({{< ref "actors-features-concepts.md#reminders" >}})
- [Learn more about timer and reminder error handling and failover.]({{< ref "actors-features-concepts.md#timers-and-reminders-error-handling" >}})


## Try out actors

### Tutorials

Want to put the Dapr actors API to the test? Walk through the following quickstart and tutorials to see actors in action:

| Quickstart/tutorial | Description |
| ------------------- | ----------- |
| [Actors tutorial](todo) | todo |

### Start using actors directly in your app

Want to skip the quickstarts? Not a problem. You can try out the actors building block directly in your application. After [Dapr is installed]({{< ref "getting-started/_index.md" >}}), you can begin using the actors API starting with [the how-to guide]({{< ref howto-actors.md >}}).

## Next steps

{{< button text="Actors features and concepts >>" page="actors-features-concepts.md" >}}

## Related links

- [Actors API reference]({{< ref actors_api.md >}})
- Actors using the:
- [.NET SDK]({{< ref dotnet-actors.md >}})
- [Python SDK]({{< ref python-actor.md >}})
- [Java SDK]({{< ref js-actors.md >}})
- Refer to the [Dapr SDK documentation and examples]({{< ref "developing-applications/sdks/#sdk-languages" >}}).
Original file line number Diff line number Diff line change
@@ -0,0 +1,206 @@
---
type: docs
title: "Actor runtime configuration parameters"
linkTitle: "Runtime configuration"
weight: 40
description: Modify the default Dapr actor runtime configuration behavior
---

You can modify the default Dapr actor runtime behavior using the following configuration parameters.

| Parameter | Description | Default |
| --------- | ----------- | ------- |
| `entities` | The actor types supported by this host. | N/A |
| `actorIdleTimeout` | The timeout before deactivating an idle actor. Checks for timeouts occur every `actorScanInterval` interval. | 60 minutes |
| `actorScanInterval` | The duration which specifies how often to scan for actors to deactivate idle actors. Actors that have been idle longer than actor_idle_timeout will be deactivated. | 30 seconds |
| `drainOngoingCallTimeout` | The duration when in the process of draining rebalanced actors. This specifies the timeout for the current active actor method to finish. If there is no current actor method call, this is ignored. | 60 seconds |
| `drainRebalancedActors` | If true, Dapr will wait for `drainOngoingCallTimeout` duration to allow a current actor call to complete before trying to deactivate an actor. | true |
| `reentrancy` (`ActorReentrancyConfig`) | Configure the reentrancy behavior for an actor. If not provided, reentrancy is disabled. | disabled, false |
| `remindersStoragePartitions` | Configure the number of partitions for actor's reminders. If not provided, all reminders are saved as a single record in actor's state store. | 0 |
| `entitiesConfig` | Configure each actor type individually with an array of configurations. Any entity specified in the individual entity configurations must also be specified in the top level `entities` field. | N/A |

## Examples

{{< tabs ".NET" JavaScript Python Java Go >}}

{{% codetab %}}
```csharp
// In Startup.cs
public void ConfigureServices(IServiceCollection services)
{
// Register actor runtime with DI
services.AddActors(options =>
{
// Register actor types and configure actor settings
options.Actors.RegisterActor<MyActor>();

// Configure default settings
options.ActorIdleTimeout = TimeSpan.FromMinutes(60);
options.ActorScanInterval = TimeSpan.FromSeconds(30);
options.DrainOngoingCallTimeout = TimeSpan.FromSeconds(60);
options.DrainRebalancedActors = true;
options.RemindersStoragePartitions = 7;
options.ReentrancyConfig = new() { Enabled = false };

// Add a configuration for a specific actor type.
// This actor type must have a matching value in the base level 'entities' field. If it does not, the configuration will be ignored.
// If there is a matching entity, the values here will be used to overwrite any values specified in the root configuration.
// In this example, `ReentrantActor` has reentrancy enabled; however, 'MyActor' will not have reentrancy enabled.
options.Actors.RegisterActor<ReentrantActor>(typeOptions: new()
{
ReentrancyConfig = new()
{
Enabled = true,
}
});
});

// Register additional services for use with actors
services.AddSingleton<BankService>();
}
```
[See the .NET SDK documentation on registring actors]({{< ref "dotnet-actors-usage.md#registring-actors" >}}).

{{% /codetab %}}

{{% codetab %}}

<!--javascript-->

```js
import { CommunicationProtocolEnum, DaprClient, DaprServer } from "@dapr/dapr";

// Configure the actor runtime with the DaprClientOptions.
const clientOptions = {
actor: {
actorIdleTimeout: "1h",
actorScanInterval: "30s",
drainOngoingCallTimeout: "1m",
drainRebalancedActors: true,
reentrancy: {
enabled: true,
maxStackDepth: 32,
},
remindersStoragePartitions: 0,
},
};

// Use the options when creating DaprServer and DaprClient.

// Note, DaprServer creates a DaprClient internally, which needs to be configured with clientOptions.
const server = new DaprServer(serverHost, serverPort, daprHost, daprPort, clientOptions);

const client = new DaprClient(daprHost, daprPort, CommunicationProtocolEnum.HTTP, clientOptions);
```

[See the documentation on writing actors with the JavaScript SDK]({{< ref "js-actors.md#registering-actors" >}}).

{{% /codetab %}}

{{% codetab %}}

<!--python-->

```python
from datetime import timedelta
from dapr.actor.runtime.config import ActorRuntimeConfig, ActorReentrancyConfig

ActorRuntime.set_actor_config(
ActorRuntimeConfig(
actor_idle_timeout=timedelta(hours=1),
actor_scan_interval=timedelta(seconds=30),
drain_ongoing_call_timeout=timedelta(minutes=1),
drain_rebalanced_actors=True,
reentrancy=ActorReentrancyConfig(enabled=False),
remindersStoragePartitions=7
)
)
```

[See the documentation on running actors with the Python SDK]({{< ref "python-actor.md" >}})

{{% /codetab %}}

{{% codetab %}}

<!--java-->

```java
// import io.dapr.actors.runtime.ActorRuntime;
// import java.time.Duration;

ActorRuntime.getInstance().getConfig().setActorIdleTimeout(Duration.ofMinutes(60));
ActorRuntime.getInstance().getConfig().setActorScanInterval(Duration.ofSeconds(30));
ActorRuntime.getInstance().getConfig().setDrainOngoingCallTimeout(Duration.ofSeconds(60));
ActorRuntime.getInstance().getConfig().setDrainBalancedActors(true);
ActorRuntime.getInstance().getConfig().setActorReentrancyConfig(false, null);
ActorRuntime.getInstance().getConfig().setRemindersStoragePartitions(7);
```

[See the documentation on writing actors with the Java SDK]({{< ref "java.md#actors" >}}).

{{% /codetab %}}

{{% codetab %}}
<!--go-->

```go
const (
defaultActorType = "basicType"
reentrantActorType = "reentrantType"
)

type daprConfig struct {
Entities []string `json:"entities,omitempty"`
ActorIdleTimeout string `json:"actorIdleTimeout,omitempty"`
ActorScanInterval string `json:"actorScanInterval,omitempty"`
DrainOngoingCallTimeout string `json:"drainOngoingCallTimeout,omitempty"`
DrainRebalancedActors bool `json:"drainRebalancedActors,omitempty"`
Reentrancy config.ReentrancyConfig `json:"reentrancy,omitempty"`
EntitiesConfig []config.EntityConfig `json:"entitiesConfig,omitempty"`
}

var daprConfigResponse = daprConfig{
Entities: []string{defaultActorType, reentrantActorType},
ActorIdleTimeout: actorIdleTimeout,
ActorScanInterval: actorScanInterval,
DrainOngoingCallTimeout: drainOngoingCallTimeout,
DrainRebalancedActors: drainRebalancedActors,
Reentrancy: config.ReentrancyConfig{Enabled: false},
EntitiesConfig: []config.EntityConfig{
{
// Add a configuration for a specific actor type.
// This actor type must have a matching value in the base level 'entities' field. If it does not, the configuration will be ignored.
// If there is a matching entity, the values here will be used to overwrite any values specified in the root configuration.
// In this example, `reentrantActorType` has reentrancy enabled; however, 'defaultActorType' will not have reentrancy enabled.
Entities: []string{reentrantActorType},
Reentrancy: config.ReentrancyConfig{
Enabled: true,
MaxStackDepth: &maxStackDepth,
},
},
},
}

func configHandler(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Content-Type", "application/json")
w.WriteHeader(http.StatusOK)
json.NewEncoder(w).Encode(daprConfigResponse)
}
```

[See an example for using actors with the Go SDK](https://github.com/dapr/go-sdk/tree/main/examples/actor).

{{% /codetab %}}

{{< /tabs >}}

## Next steps

{{< button text="Enable actor reminder partitioning >>" page="howto-actors-partitioning.md" >}}

## Related links

- Refer to the [Dapr SDK documentation and examples]({{< ref "developing-applications/sdks/#sdk-languages" >}}).
- [Actors API reference]({{< ref actors_api.md >}})
- [Actors overview]({{< ref actors-overview.md >}})
Loading

0 comments on commit fe468ff

Please sign in to comment.