Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Docs] Add sequence diagrams to fallback, retry, and rate limiter #1702

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
39 changes: 39 additions & 0 deletions docs/strategies/fallback.md
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,45 @@ new ResiliencePipelineBuilder<UserAvatar>()
| `FallbackAction` | `Null`, **Required** | Fallback action to be executed. |
| `OnFallback` | `null` | Event that is raised when fallback happens. |

## Diagrams

### Happy path sequence diagram

```mermaid
%%{init: {'theme':'dark'}}%%
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This looks quite bad if you don't use the dark theme: #1701 (comment)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

On the mermaid documentation site the diagrams are dynamically switching between default and dark theme depending on the selected appearance mode. I haven't figured out yet how they do that so, I will spend some time to dig deeper.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

How about we just don't do anything and raise an issue in DocFx repo?

My thinking is that default behavior should handle this automatically.

Copy link
Member

@martincostello martincostello Oct 17, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would agree that it should just work.

If we can't make it look good for both light and dark, then we should use whichever variant looks less worse.

How does the default/light diagram look in dark mode?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The defaults for white theme:

image

Dark theme:
image

Copy link
Contributor Author

@peter-csala peter-csala Oct 17, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I've found how they do it. :)

const renderChart = async () => {
  const hasDarkClass = document.documentElement.classList.contains('dark');
  const mermaidConfig = {
     ...   
    theme: hasDarkClass ? 'dark' : 'default',
  };
  ...
}

I don't think we can do similar via the docfx config :(

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is there a way to add a custom module/script to apply a global config or something when the page is being rendered in the client? Docs

If not, would be good to create an issue in the docfx repo if there isn't one already requesting this feature.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@martintmk, @martincostello I've filed an issue on docfx: dotnet/docfx#9306.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Shall we add a note to the diagrams sections something like:

Please prefer dark appearance mode for better legibility ... it is a temporarily solution

?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Let's avoid a comment - otherwise if we have lots of diagrams I think it's just going to duplicate all over the place.

sequenceDiagram
actor C as Caller
participant P as Pipeline
participant F as Fallback
participant D as DecoratedUserCallback

C->>P: Calls ExecuteAsync
P->>F: Calls ExecuteCore
F->>+D: Invokes
D->>-F: Returns result
F->>P: Returns result
P->>C: Returns result
```

### Unhappy path sequence diagram

```mermaid
%%{init: {'theme':'dark'}}%%
sequenceDiagram
actor C as Caller
participant P as Pipeline
participant F as Fallback
participant D as DecoratedUserCallback

C->>P: Calls ExecuteAsync
P->>F: Calls ExecuteCore
F->>+D: Invokes
D->>-F: Fails
F->>F: Falls back to<br/>substitute result
F->>P: Returns <br/>substituted result
P->>C: Returns <br/>substituted result
```

## Patterns

### Fallback after retries
Expand Down
134 changes: 134 additions & 0 deletions docs/strategies/rate-limiter.md
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,140 @@ catch (RateLimiterRejectedException ex)
| `DefaultRateLimiterOptions` | `PermitLimit` set to 1000 and `QueueLimit` set to 0. | The options for the default concurrency limiter that will be used when `RateLimiter` is `null`. |
| `OnRejected` | `null` | Event that is raised when the execution is rejected by the rate limiter. |

## Diagrams

### Rate Limiter

Let's suppose we have a rate limiter strategy with `PermitLimit` : `1` and `Window` : `10 seconds`.

### Rate Limiter: happy path sequence diagram

```mermaid
%%{init: {'theme':'dark'}}%%
sequenceDiagram
actor C as Caller
participant P as Pipeline
participant RL as RateLimiter
participant D as DecoratedUserCallback

C->>P: Calls ExecuteAsync
P->>RL: Calls ExecuteCore
Note over RL,D: Window start
RL->>+D: Invokes
D->>-RL: Returns result
RL->>P: Returns result
P->>C: Returns result
Note over C: Several seconds later...
Note over RL,D: Window end
C->>P: Calls ExecuteAsync
P->>RL: Calls ExecuteCore
Note over RL,D: Window start
RL->>+D: Invokes
D->>-RL: Returns result
RL->>P: Returns result
P->>C: Returns result
Note over RL,D: Window end
```

#### Rate limiter: unhappy path sequence diagram

```mermaid
%%{init: {'theme':'dark'}}%%
sequenceDiagram
actor C as Caller
participant P as Pipeline
participant RL as RateLimiter
participant D as DecoratedUserCallback

C->>P: Calls ExecuteAsync
P->>RL: Calls ExecuteCore
Note over RL,D: Window start
RL->>+D: Invokes
D->>-RL: Returns result
RL->>P: Returns result
P->>C: Returns result
Note over C: Few seconds later...
C->>P: Calls ExecuteAsync
P->>RL: Calls ExecuteCore
RL->>RL: Rejects request
RL->>P: Throws <br/>RateLimiterRejectedException
P->>C: Propagates exception
Note over RL,D: Window end
```

### Concurrency Limiter

Let's suppose we have a concurrency limiter strategy with `PermitLimit` : `1` and `QueueLimit` : `1`.

#### Concurrency limiter: happy path sequence diagram

```mermaid
%%{init: {'theme':'dark'}}%%
sequenceDiagram
actor C1 as Caller1
actor C2 as Caller2
participant P as Pipeline
participant CL as ConcurrencyLimiter
participant D as DecoratedUserCallback

par
C1->>P: Calls ExecuteAsync
and
C2->>P: Calls ExecuteAsync
end

P->>CL: Calls ExecuteCore
CL->>+D: Invokes (C1)
P->>CL: Calls ExecuteCore
CL->>CL: Queues request

D->>-CL: Returns result (C1)
CL->>P: Returns result (C1)
CL->>+D: Invokes (C2)
P->>C1: Returns result
D->>-CL: Returns result (C2)
CL->>P: Returns result (C2)
P->>C2: Returns result
```

#### Concurrency Limiter: unhappy path sequence diagram

```mermaid
%%{init: {'theme':'dark'}}%%
sequenceDiagram
actor C1 as Caller1
actor C2 as Caller2
actor C3 as Caller3
participant P as Pipeline
participant CL as ConcurrencyLimiter
participant D as DecoratedUserCallback

par
C1->>P: Calls ExecuteAsync
and
C2->>P: Calls ExecuteAsync
and
C3->>P: Calls ExecuteAsync
end

P->>CL: Calls ExecuteCore
CL->>+D: Invokes (C1)
P->>CL: Calls ExecuteCore
CL->>CL: Queues request (C2)
P->>CL: Calls ExecuteCore
CL->>CL: Rejects request (C3)
CL->>P: Throws <br/>RateLimiterRejectedException
P->>C3: Propagate exception

D->>-CL: Returns result (C1)
CL->>P: Returns result (C1)
CL->>+D: Invokes (C2)
P->>C1: Returns result
D->>-CL: Returns result (C2)
CL->>P: Returns result (C2)
P->>C2: Returns result
```

## Disposal of rate limiters

The `RateLimiter` is a disposable resource. When you explicitly create a `RateLimiter` instance, it's good practice to dispose of it once it's no longer needed. This is usually not an issue when manually creating resilience pipelines using the `ResiliencePipelineBuilder`. However, when dynamic reloads are enabled, failing to dispose of discarded rate limiters can lead to excessive resource consumption. Fortunately, Polly provides a way to dispose of discarded rate limiters, as demonstrated in the example below:
Expand Down
54 changes: 54 additions & 0 deletions docs/strategies/retry.md
Original file line number Diff line number Diff line change
Expand Up @@ -104,6 +104,60 @@ new ResiliencePipelineBuilder().AddRetry(new RetryStrategyOptions
| `OnRetry` | `null` | Action executed when retry occurs. |
| `MaxDelay` | `null` | Caps the calculated retry delay to a specified maximum duration. |

## Diagrams

Let's suppose we have a retry strategy with `MaxRetryAttempts`: `2`.

### Happy path sequence diagram

```mermaid
%%{init: {'theme':'dark'}}%%
sequenceDiagram
actor C as Caller
participant P as Pipeline
participant R as Retry
participant D as DecoratedUserCallback

C->>P: Calls ExecuteAsync
P->>R: Calls ExecuteCore
Note over R,D: Initial attempt
R->>+D: Invokes
D->>-R: Fails
R->>R: Sleeps
Note over R,D: 1st retry attempt
R->>+D: Invokes
D->>-R: Returns result
R->>P: Returns result
P->>C: Returns result
```

### Unhappy path sequence diagram

```mermaid
%%{init: {'theme':'dark'}}%%
sequenceDiagram
actor C as Caller
participant P as Pipeline
participant R as Retry
participant D as DecoratedUserCallback

C->>P: Calls ExecuteAsync
P->>R: Calls ExecuteCore
Note over R,D: Initial attempt
R->>+D: Invokes
D->>-R: Fails
R->>R: Sleeps
Note over R,D: 1st retry attempt
R->>+D: Invokes
D->>-R: Fails
R->>R: Sleeps
Note over R,D: 2nd retry attempt
martintmk marked this conversation as resolved.
Show resolved Hide resolved
R->>+D: Invokes
D->>-R: Fails
R->>P: Propagates failure
P->>C: Propagates failure
```

## Patterns

### Limiting the maximum delay
Expand Down
20 changes: 10 additions & 10 deletions docs/strategies/timeout.md
Original file line number Diff line number Diff line change
Expand Up @@ -88,13 +88,13 @@ sequenceDiagram
actor C as Caller
participant P as Pipeline
participant T as Timeout
participant DM as DecoratedMethod
participant D as DecoratedUserCallback

C->>P: Calls ExecuteAsync
P->>T: Calls ExecuteCore
T->>+DM: Invokes
DM->>DM: Performs <br/>long-running <br/>operation
DM->>-T: Returns result
T->>+D: Invokes
D->>D: Performs <br/>long-running <br/>operation
D->>-T: Returns result
T->>P: Returns result
P->>C: Returns result
```
Expand All @@ -107,18 +107,18 @@ sequenceDiagram
actor C as Caller
participant P as Pipeline
participant T as Timeout
participant DM as DecoratedMethod
participant D as DecoratedUserCallback

C->>P: Calls ExecuteAsync
P->>T: Calls ExecuteCore
T->>+DM: Invokes
T->>+D: Invokes
activate T
activate DM
DM->>DM: Performs <br/>long-running <br/>operation
activate D
D->>D: Performs <br/>long-running <br/>operation
T->T: Times out
deactivate T
T->>DM: Propagates cancellation
deactivate DM
T->>D: Propagates cancellation
deactivate D
T->>P: Throws <br/>TimeoutRejectedException
P->>C: Propagates exception
```