-
Notifications
You must be signed in to change notification settings - Fork 21
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
Added Counter Interceptor Sample #77
Conversation
|
src/CounterInterceptor/TemporalioSamples.CounterInterceptor.csproj
Outdated
Show resolved
Hide resolved
@@ -0,0 +1,87 @@ | |||
namespace TemporalioSamples.CounterInterceptor; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
A bit surprised this passes CI, maybe we don't have a dotnet format
check. Usually we want there to be a blank line between namespace declaration and type declarataion
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
fixed.
@@ -0,0 +1,87 @@ | |||
namespace TemporalioSamples.CounterInterceptor; | |||
public record ClientCounts |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Should not have a top-level type declared in any non-Program.cs
file that doesn't match its name. Either make it a nested class or give it its own file.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Good catch. Fixed.
public override string ToString() | ||
{ | ||
return | ||
"\n\tTotal Number of Workflow Exec: " + Executions + | ||
"\n\tTotal Number of Signals: " + Signals + | ||
"\n\tTotal Number of Queries: " + Queries; | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
public override string ToString() | |
{ | |
return | |
"\n\tTotal Number of Workflow Exec: " + Executions + | |
"\n\tTotal Number of Signals: " + Signals + | |
"\n\tTotal Number of Queries: " + Queries; | |
} | |
public override string ToString() => | |
$"\n\tTotal Number of Workflow Exec: {Executions}\n\t" + | |
$"Total Number of Signals: {Signals}\n\tTotal Number of Queries: {Queries}"; | |
} |
Can break up long line, but might as well use interpolation, and might as well use =>
for single-line methods. Granted, despite what I said before, using ToString()
for multiline strings only for a specific scenario is a bit much, but fine.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done
perWorkflowIdDictionary.Select(kvp => $"** Workflow ID: {kvp.Key} {kvp.Value}")); | ||
} | ||
|
||
public static uint NumOfWorkflowExecutions(string workflowId) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Use =>
for single-line methods
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done.
} | ||
} | ||
|
||
public class ClientCounter |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why have this separate class? Why not just use Dictionary<string, ClientCounts>
where it's needed? Do not use static state, store the dictionary on the interceptor and make it available to the creator of the interceptor.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Refactored.
|
||
public class SimpleClientCallsInterceptor : IClientInterceptor | ||
{ | ||
private ClientCounter clientCounter; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Bump
return base.StartWorkflowUpdateAsync<TResult>(input); | ||
} | ||
|
||
private static string CheckId(string? id) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Bump, only three places have this, no need to make a whole new method for a simple id ?? "None"
in those three places
public override Task<WorkflowUpdateHandle<TResult>> StartWorkflowUpdateAsync<TResult>( | ||
StartWorkflowUpdateInput input) | ||
{ | ||
// Not tracking this |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Then can remove this method entirely
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Removed
public override Task<TResult> ScheduleActivityAsync<TResult>( | ||
ScheduleActivityInput input) | ||
{ | ||
return base.ScheduleActivityAsync<TResult>(input); | ||
} | ||
|
||
public override Task SignalChildWorkflowAsync(SignalChildWorkflowInput input) | ||
{ | ||
return base.SignalChildWorkflowAsync(input); | ||
} | ||
|
||
public override Task SignalExternalWorkflowAsync(SignalExternalWorkflowInput input) | ||
{ | ||
return base.SignalExternalWorkflowAsync(input); | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Bump
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
All the same things on the ClientCounter
class apply here
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Changed.
public string SayHello(string name, string title) | ||
{ | ||
return "Hello " + title + " " + name; | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Bump, this was not addressed
public async Task<string> RunAsync() | ||
{ | ||
// Wait for greeting info | ||
await Workflow.WaitConditionAsync(() => Name != null && Title != null); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Bump, this was never addressed
src/CounterInterceptor/Program.cs
Outdated
}); | ||
|
||
using var tokenSource = new CancellationTokenSource(); | ||
Console.CancelKeyPress += (_, eventArgs) => |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Bump, this was never addressed
src/CounterInterceptor/Program.cs
Outdated
|
||
var result = await handle.GetResultAsync(); | ||
|
||
Console.WriteLine($"Workflow result is {result}", result); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Bump this was never addressed. If you are using interpolation, you don't also pass as a param.
src/CounterInterceptor/README.md
Outdated
## Start local Temporal Server | ||
```bash | ||
# run only once | ||
temporal server start-dev | ||
``` |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Bump, this was never addressed
private const string NumberOfWorkflowExecutions = "numOfWorkflowExec"; | ||
private const string NumberOfSignals = "numOfSignals"; | ||
private const string NumberOfQueries = "numOfQueries"; | ||
private static Dictionary<string, ClientCounts> clientDictionary = new(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This should not be static and it should be readonly and you do not have to suffix "Dictionary", just call it counts
or something
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done.
public ClientOutboundInterceptor InterceptClient(ClientOutboundInterceptor nextInterceptor) => | ||
new ClientOutbound(this, nextInterceptor); | ||
|
||
public string Info() => |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
May be easier if you just let the caller access the dictionary and do what they want with it instead of all of these separate methods to read/write it
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Most things in client interceptor class apply here
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done
$"Total Number of Queries: {Queries}"; | ||
} | ||
|
||
public class SimpleClientCallsInterceptor : IClientInterceptor |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
IMO for simplicity you should make one interceptor that implements both IClientInterceptor
and IWorkerInterceptor
(and you only then need to configure the client not the worker interceptor separately). You could also make just one counts object.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done
public override Task<TResult> ScheduleActivityAsync<TResult>( | ||
ScheduleActivityInput input) | ||
{ | ||
return base.ScheduleActivityAsync<TResult>(input); | ||
} | ||
|
||
public override Task SignalChildWorkflowAsync(SignalChildWorkflowInput input) | ||
{ | ||
return base.SignalChildWorkflowAsync(input); | ||
} | ||
|
||
public override Task SignalExternalWorkflowAsync(SignalExternalWorkflowInput input) | ||
{ | ||
return base.SignalExternalWorkflowAsync(input); | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Bump again, these weren't addressed
src/CounterInterceptor/Counts.cs
Outdated
@@ -0,0 +1,54 @@ | |||
public record Counts |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
No benefit to making a record, might as well make a class. Also, make sure you put the namespace on this file.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
fixed
src/CounterInterceptor/Counts.cs
Outdated
public Counts() | ||
{ | ||
clientExecutions = 0; | ||
clientQueries = 0; | ||
clientSignals = 0; | ||
workflowExecutions = 0; | ||
workflowSignals = 0; | ||
workflowQueries = 0; | ||
workflowChildExecutions = 0; | ||
workflowActivityExecutions = 0; | ||
} | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
public Counts() | |
{ | |
clientExecutions = 0; | |
clientQueries = 0; | |
clientSignals = 0; | |
workflowExecutions = 0; | |
workflowSignals = 0; | |
workflowQueries = 0; | |
workflowChildExecutions = 0; | |
workflowActivityExecutions = 0; | |
} |
Not needed, this is the default value already of every field here.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Fixed
{ | ||
[Activity] | ||
public string SayHello(string name, string title) => | ||
"Hello " + title + " " + name; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
"Hello " + title + " " + name; | |
$"Hello {title} {name}"; |
I think interpolation is a bit clearer here and in next activity
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Fixed
"Hello " + title + " " + name; | ||
|
||
[Activity] | ||
public string SayGoodBye(string name, string title) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You are inconsistent with your single-line methods, this should be =>
like the one just above it
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Fixed
private ConcurrentDictionary<string, Counts> counts = new(); | ||
|
||
public ConcurrentDictionary<string, Counts> Counts => counts; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
private ConcurrentDictionary<string, Counts> counts = new(); | |
public ConcurrentDictionary<string, Counts> Counts => counts; | |
public ConcurrentDictionary<string, Counts> Counts { get; } = new(); |
Use auto properties
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Fixed
// we don't care if it doesn't add it as we will | ||
// still increment the current value. | ||
root.counts.TryAdd(id, new Counts()); | ||
Interlocked.Increment(ref root.counts[id].WorkflowExecutions); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Note, this is not workflow executions, but workflow replays. You could get 1000 of these for the same workflow. Same for other counts in the workflow inbound/outbound. These will be double counted on every replay. You can consider not doing this on replay, or you can clarify in docs of the fields that they include replay attempts (you probably want the former).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Oh wow. That's important to call out.
I see that Workflow.Info has an Attempt field, but not sure it makes sense to use that to determine replay, as I think that is used for the number of retry attempts.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There is Workflow.Unsafe.IsReplaying
you can make sure is false before doing the increment (this is what we do in tracing interceptor for instance). It should be noted though that on task failure, this will run over and over in non-replaying situation, but besides task failure it will work.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Fixed
public async Task<string> RunAsync() | ||
{ | ||
// Wait for greeting info | ||
await Workflow.WaitConditionAsync(() => Name != null && Title != null); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Bump again, this is still not addressed
src/CounterInterceptor/Program.cs
Outdated
}); | ||
|
||
using var tokenSource = new CancellationTokenSource(); | ||
Console.CancelKeyPress += (_, eventArgs) => |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Bump again, this is still not addressed
src/CounterInterceptor/README.md
Outdated
## Start local Temporal Server | ||
```bash | ||
# run only once | ||
temporal server start-dev | ||
``` |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Bump again, this is still not addressed
src/CounterInterceptor/README.md
Outdated
temporal server start-dev | ||
``` | ||
|
||
## Run Worker Locally |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Bump again, this is still not addressed
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Looks good, just want to take a look at the outstanding issues on the README
namespace TemporalioSamples.CounterInterceptor; | ||
public class Counts |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You are inconsistent with whether you add spacing between namespace and type declaration
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Looks good, thanks!
@@ -0,0 +1,13 @@ | |||
# dotnet-counter-interceptor |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Not really the best title, but that's ok
What was changed
Added a CounterInterceptor example
Why?
Demonstrate how to use interceptors with the .NET SDK
Checklist
Closes n/a
How was this tested:
Added a test that validates the counts.
Any docs updates needed?
Updated the main README to include this sample