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 1 commit
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 DM as DecoratedMethod

C->>P: Calls ExecuteAsync
P->>F: Calls ExecuteCore
F->>+DM: Invokes
DM->>-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 DM as DecoratedMethod
peter-csala marked this conversation as resolved.
Show resolved Hide resolved

C->>P: Calls ExecuteAsync
P->>F: Calls ExecuteCore
F->>+DM: Invokes
DM->>-F: Fails transiently
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`.

### Happy path sequence diagram

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

C->>P: Calls ExecuteAsync
P->>RL: Calls ExecuteCore
Note over RL,DM: Window start
RL->>+DM: Invokes
DM->>-RL: Returns result
RL->>P: Returns result
P->>C: Returns result
Note right of C: Several seconds later...
peter-csala marked this conversation as resolved.
Show resolved Hide resolved
Note over RL,DM: Window end
C->>P: Calls ExecuteAsync
P->>RL: Calls ExecuteCore
Note over RL,DM: Window start
RL->>+DM: Invokes
DM->>-RL: Returns result
RL->>P: Returns result
P->>C: Returns result
Note over RL,DM: Window end
```

#### Unhappy path sequence diagram

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

C->>P: Calls ExecuteAsync
P->>RL: Calls ExecuteCore
Note over RL,DM: Window start
RL->>+DM: Invokes
DM->>-RL: Returns result
RL->>P: Returns result
P->>C: Returns result
Note right of 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,DM: Window end
```

### Concurrency Limiter

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

#### 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 DM as DecoratedMethod

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

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

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

#### 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 DM as DecoratedMethod

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

P->>CL: Calls ExecuteCore
CL->>+DM: 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

DM->>-CL: Returns result (C1)
CL->>P: Returns result (C1)
CL->>+DM: Invokes (C2)
P->>C1: Returns result
DM->>-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 DM as DecoratedMethod

C->>P: Calls ExecuteAsync
P->>R: Calls ExecuteCore
Note over R,DM: Initial attempt
R->>+DM: Invokes
DM->>-R: Fails transiently
R->>R: Sleeps
Note over R,DM: 1st retry attempt
R->>+DM: Invokes
DM->>-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 DM as DecoratedMethod

C->>P: Calls ExecuteAsync
P->>R: Calls ExecuteCore
Note over R,DM: Initial attempt
R->>+DM: Invokes
DM->>-R: Fails transiently
R->>R: Sleeps
Note over R,DM: 1st retry attempt
R->>+DM: Invokes
DM->>-R: Fails transiently
R->>R: Sleeps
Note over R,DM: 2nd retry attempt
R->>+DM: Invokes
DM->>-R: Fails transiently
R->>P: Propagates failure
P->>C: Propagates failure
```

## Patterns

### Limiting the maximum delay
Expand Down