-
Notifications
You must be signed in to change notification settings - Fork 33
Added Counter Interceptor Sample #77
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
Changes from 3 commits
d678366
f428229
26fdbee
c68e75d
ec9da55
31c0743
ca90698
7ac67a9
34c83af
7682937
37692a7
d548089
d113bd3
c0131fd
4557ffe
c7b6e31
f3a82cc
078bbe6
fc4fada
c6f5e55
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,89 @@ | ||
| namespace TemporalioSamples.CounterInterceptor; | ||
|
||
|
|
||
| using System.Numerics; | ||
|
|
||
| public class ClientCounter | ||
|
||
| { | ||
| private const string NumberOfWorkflowExecutions = "numOfWorkflowExec"; | ||
| private const string NumberOfSignals = "numOfSignals"; | ||
| private const string NumberOfQueries = "numOfQueries"; | ||
| private static Dictionary<string, Dictionary<string, BigInteger?>> perWorkflowIdDictionary = | ||
rross marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
rross marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
| new Dictionary<string, Dictionary<string, BigInteger?>>(); | ||
rross marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
|
|
||
| public static string Info() | ||
| { | ||
| string result = string.Empty; | ||
| foreach (var item in perWorkflowIdDictionary) | ||
rross marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
| { | ||
| var info = item.Value; | ||
| result = result + | ||
| "\n** Workflow ID: " + item.Key + | ||
| "\n\tTotal Number of Workflow Exec: " + info[NumberOfWorkflowExecutions] + | ||
| "\n\tTotal Number of Signals: " + info[NumberOfSignals] + | ||
| "\n\tTotal Number of Queries: " + info[NumberOfQueries]; | ||
| } | ||
|
|
||
| return result; | ||
| } | ||
|
|
||
| public static BigInteger? NumOfWorkflowExecutions(string workflowId) | ||
|
||
| { | ||
| return perWorkflowIdDictionary[workflowId][NumberOfWorkflowExecutions]; | ||
| } | ||
|
|
||
| public static BigInteger? NumOfSignals(string workflowId) | ||
| { | ||
| return perWorkflowIdDictionary[workflowId][NumberOfSignals]; | ||
| } | ||
|
|
||
| public static BigInteger? NumOfQueries(string workflowId) | ||
| { | ||
| return perWorkflowIdDictionary[workflowId][NumberOfQueries]; | ||
| } | ||
|
|
||
| public void AddStartInvocation(string workflowId) | ||
| { | ||
| Add(workflowId, NumberOfWorkflowExecutions); | ||
| } | ||
|
|
||
| public void AddSignalInvocation(string workflowId) | ||
| { | ||
| Add(workflowId, NumberOfSignals); | ||
| } | ||
|
|
||
| public void AddQueryInvocation(string workflowId) | ||
| { | ||
| Add(workflowId, NumberOfQueries); | ||
| } | ||
|
|
||
| // Creates a default counter info map for a workflowid | ||
| private static Dictionary<string, BigInteger?> GetDefaultInfoMap() | ||
| { | ||
| return new Dictionary<string, BigInteger?>() | ||
| { | ||
| { NumberOfWorkflowExecutions, 0 }, | ||
| { NumberOfSignals, 0 }, | ||
| { NumberOfQueries, 0 }, | ||
| }; | ||
| } | ||
|
|
||
| private void Add(string workflowId, string type) | ||
| { | ||
| if (!perWorkflowIdDictionary.TryGetValue(workflowId, out Dictionary<string, BigInteger?>? value)) | ||
| { | ||
| value = GetDefaultInfoMap(); | ||
| perWorkflowIdDictionary.Add(workflowId, value); | ||
| } | ||
|
|
||
| if (value[type] == null) | ||
| { | ||
| value[type] = 1; | ||
| } | ||
| else | ||
| { | ||
| var current = value[type]; | ||
| var next = current + 1; | ||
| value[type] = next; | ||
| } | ||
rross marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
| } | ||
| } | ||
|
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,7 @@ | ||
| namespace TemporalioSamples.CounterInterceptor; | ||
|
|
||
| public static class Constants | ||
| { | ||
| public const string TaskQueue = "counter-interceptor-sample"; | ||
| public const string ChildWorkflowId = "TestInterceptorChildWorkflow"; | ||
| } |
| Original file line number | Diff line number | Diff line change | ||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|
| @@ -0,0 +1,19 @@ | ||||||||||||
| namespace TemporalioSamples.CounterInterceptor; | ||||||||||||
|
|
||||||||||||
| using System.Diagnostics; | ||||||||||||
| using Temporalio.Activities; | ||||||||||||
|
|
||||||||||||
| public class MyActivities | ||||||||||||
| { | ||||||||||||
| [Activity] | ||||||||||||
| public string SayHello(string name, string title) | ||||||||||||
| { | ||||||||||||
| return "Hello " + title + " " + name; | ||||||||||||
| } | ||||||||||||
|
||||||||||||
| public string SayHello(string name, string title) | |
| { | |
| return "Hello " + title + " " + name; | |
| } | |
| public string SayHello(string name, string title) => $"Hello {title} {name}"; |
Use string interpolation and single-statement syntax (will stop commenting on this, but it applies project wide)
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
Outdated
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
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| @@ -0,0 +1,21 @@ | ||||||||||||||||||||||||||
| namespace TemporalioSamples.CounterInterceptor; | ||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||
| using Temporalio.Workflows; | ||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||
| [Workflow] | ||||||||||||||||||||||||||
| public class MyChildWorkflow | ||||||||||||||||||||||||||
| { | ||||||||||||||||||||||||||
| private readonly ActivityOptions activityOptions = new() | ||||||||||||||||||||||||||
| { | ||||||||||||||||||||||||||
| StartToCloseTimeout = TimeSpan.FromSeconds(10), | ||||||||||||||||||||||||||
| }; | ||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||
| [WorkflowRun] | ||||||||||||||||||||||||||
| public async Task<string> ExecChildAsync(string name, string title) | ||||||||||||||||||||||||||
|
||||||||||||||||||||||||||
| public async Task<string> ExecChildAsync(string name, string title) | |
| public async Task<string> RunAsync(string name, string title) |
Not required obviously, but I think calling all run calls RunAsync is clearest
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.
Yeah I agree. Fixed.
Outdated
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 async Task<string> ExecChildAsync(string name, string title) | |
| { | |
| string result = await Workflow.ExecuteActivityAsync((MyActivities act) => act.SayHello(name, title), activityOptions); | |
| result += await Workflow.ExecuteActivityAsync((MyActivities act) => act.SayGoodBye(name, title), activityOptions); | |
| return result; | |
| } | |
| public Task<string> ExecChildAsync(string name, string title) => | |
| await Workflow.ExecuteActivityAsync( | |
| (MyActivities act) => act.SayHello(name, title), activityOptions) + | |
| await Workflow.ExecuteActivityAsync( | |
| (MyActivities act) => act.SayGoodBye(name, title), activityOptions); |
This reads clearer to me
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.
Agree. Fixed.
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| @@ -0,0 +1,56 @@ | ||||||||||||||||||||||||||||||||||
| namespace TemporalioSamples.CounterInterceptor; | ||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||
| using Temporalio.Workflows; | ||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||
| [Workflow] | ||||||||||||||||||||||||||||||||||
| public class MyWorkflow | ||||||||||||||||||||||||||||||||||
| { | ||||||||||||||||||||||||||||||||||
| private string name = string.Empty; | ||||||||||||||||||||||||||||||||||
| private string title = string.Empty; | ||||||||||||||||||||||||||||||||||
| private bool exit; // automatically defaults to false | ||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||
| [WorkflowRun] | ||||||||||||||||||||||||||||||||||
| public async Task<string> ExecAsync() | ||||||||||||||||||||||||||||||||||
| { | ||||||||||||||||||||||||||||||||||
| // wait for greeting info | ||||||||||||||||||||||||||||||||||
| await Workflow.WaitConditionAsync(() => name != null && title != null); | ||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||
| // Execute Child Workflow | ||||||||||||||||||||||||||||||||||
| string result = await Workflow.ExecuteChildWorkflowAsync( | ||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||
| string result = await Workflow.ExecuteChildWorkflowAsync( | |
| var result = await Workflow.ExecuteChildWorkflowAsync( |
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.
Outdated
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.
| new() | |
| { | |
| Id = Constants.ChildWorkflowId, | |
| }); | |
| new() { Id = Constants.ChildWorkflowId }); |
Don't have to span lines
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.
Outdated
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.
| await Workflow.WaitConditionAsync(() => exit != false); | |
| await Workflow.WaitConditionAsync(() => exit); |
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.
Outdated
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.
Remove the private fields and use properties. Queries are built to be property getters:
| [WorkflowQuery] | |
| public string QueryName() | |
| { | |
| return name; | |
| } | |
| [WorkflowQuery] | |
| public string QueryTitle() | |
| { | |
| return title; | |
| } | |
| [WorkflowQuery] | |
| public string Name { get; private set; } = string.Empty; | |
| [WorkflowQuery] | |
| public string Title { get; private set; } = string.Empty; |
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.
Very cool. Fixed.
Outdated
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 async Task ExitAsync() | |
| { | |
| this.exit = true; | |
| } | |
| public async Task ExitAsync() => exit = true; |
No need for all the extra lines
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.
| Original file line number | Diff line number | Diff line change | ||||||||
|---|---|---|---|---|---|---|---|---|---|---|
| @@ -0,0 +1,108 @@ | ||||||||||
| namespace TemporalioSamples.CounterInterceptor; | ||||||||||
|
|
||||||||||
| using Temporalio.Client; | ||||||||||
| using Temporalio.Worker; | ||||||||||
|
|
||||||||||
| internal class Program | ||||||||||
| { | ||||||||||
| private static async Task Main(string[] args) | ||||||||||
| { | ||||||||||
| static string GetEnvVarWithDefault(string envName, string defaultValue) | ||||||||||
| { | ||||||||||
| string? value = Environment.GetEnvironmentVariable(envName); | ||||||||||
| if (string.IsNullOrEmpty(value)) | ||||||||||
| { | ||||||||||
| return defaultValue; | ||||||||||
| } | ||||||||||
| return value; | ||||||||||
| } | ||||||||||
|
|
||||||||||
| var address = GetEnvVarWithDefault("TEMPORAL_ADDRESS", "127.0.0.1:7233"); | ||||||||||
| var temporalNamespace = GetEnvVarWithDefault("TEMPORAL_NAMESPACE", "default"); | ||||||||||
| var tlsCertPath = GetEnvVarWithDefault("TEMPORAL_TLS_CERT", string.Empty); | ||||||||||
| var tlsKeyPath = GetEnvVarWithDefault("TEMPORAL_TLS_KEY", string.Empty); | ||||||||||
|
||||||||||
| TlsOptions? tls = null; | ||||||||||
| if (!string.IsNullOrEmpty(tlsCertPath) && !string.IsNullOrEmpty(tlsKeyPath)) | ||||||||||
rross marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||||||||||
| { | ||||||||||
| tls = new() | ||||||||||
| { | ||||||||||
| ClientCert = await File.ReadAllBytesAsync(tlsCertPath), | ||||||||||
| ClientPrivateKey = await File.ReadAllBytesAsync(tlsKeyPath), | ||||||||||
| }; | ||||||||||
| } | ||||||||||
|
|
||||||||||
| var client = await TemporalClient.ConnectAsync( | ||||||||||
| options: new(address) | ||||||||||
| { | ||||||||||
| Namespace = temporalNamespace, | ||||||||||
| Tls = tls, | ||||||||||
| Interceptors = new[] | ||||||||||
| { | ||||||||||
| new SimpleClientCallsInterceptor(), | ||||||||||
|
||||||||||
| }, | ||||||||||
| }); | ||||||||||
|
|
||||||||||
| using var tokenSource = new CancellationTokenSource(); | ||||||||||
| Console.CancelKeyPress += (_, eventArgs) => | ||||||||||
|
||||||||||
| { | ||||||||||
| tokenSource.Cancel(); | ||||||||||
| eventArgs.Cancel = true; | ||||||||||
| }; | ||||||||||
|
|
||||||||||
| var activities = new MyActivities(); | ||||||||||
|
|
||||||||||
| var workerOptions = new TemporalWorkerOptions(Constants.TaskQueue). | ||||||||||
| AddAllActivities(activities). | ||||||||||
| AddWorkflow<MyWorkflow>(). | ||||||||||
| AddWorkflow<MyChildWorkflow>(); | ||||||||||
|
|
||||||||||
| workerOptions.Interceptors = new[] { new SimpleCounterWorkerInterceptor() }; | ||||||||||
|
||||||||||
|
|
||||||||||
| using var worker = new TemporalWorker( | ||||||||||
| client, | ||||||||||
| workerOptions); | ||||||||||
|
|
||||||||||
| // Run worker until cancelled | ||||||||||
| Console.WriteLine("Running worker..."); | ||||||||||
| try | ||||||||||
| { | ||||||||||
| // Start the workers | ||||||||||
| var workerResult = worker.ExecuteAsync(tokenSource.Token); | ||||||||||
|
|
||||||||||
| // start the workflow | ||||||||||
|
||||||||||
| var handle = await client.StartWorkflowAsync( | ||||||||||
| (MyWorkflow wf) => wf.ExecAsync(), | ||||||||||
| new(id: Guid.NewGuid().ToString(), taskQueue: Constants.TaskQueue)); | ||||||||||
|
|
||||||||||
| Console.WriteLine("Sending name and title to workflow"); | ||||||||||
| await handle.SignalAsync(wf => wf.SignalNameAndTitleAsync("John", "Customer")); | ||||||||||
|
|
||||||||||
| var name = await handle.QueryAsync(wf => wf.QueryName()); | ||||||||||
| var title = await handle.QueryAsync(wf => wf.QueryTitle()); | ||||||||||
|
|
||||||||||
| // send exit signal to workflow | ||||||||||
| await handle.SignalAsync(wf => wf.ExitAsync()); | ||||||||||
|
|
||||||||||
| var result = await handle.GetResultAsync(); | ||||||||||
|
|
||||||||||
| Console.WriteLine($"Workflow result is {result}", result); | ||||||||||
|
||||||||||
| Console.WriteLine($"Workflow result is {result}", result); | |
| Console.WriteLine($"Workflow result is {result}"); |
or
| Console.WriteLine($"Workflow result is {result}", result); | |
| Console.WriteLine($"Workflow result is {0}", result); |
But not both. This mistake is made in other lines below. Probably the former approach is best.
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.
rross marked this conversation as resolved.
Show resolved
Hide resolved
|
| Original file line number | Diff line number | Diff line change | ||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| @@ -0,0 +1,28 @@ | ||||||||||||||
| # dotnet-counter-interceptor | ||||||||||||||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Not really the best title, but that's ok |
||||||||||||||
| The sample demonstrates: | ||||||||||||||
| - the use of a simple Worker Workflow Interceptor that counts the number of Workflow Executions, Child Workflow Executions, and Activity Executions as well as the number of Signals and Queries. It is based | ||||||||||||||
| off of the [Java Sample](https://github.com/temporalio/samples-java/tree/main) located [here](https://github.com/temporalio/samples-java/tree/main/core/src/main/java/io/temporal/samples/countinterceptor) | ||||||||||||||
| - the use of a simple Client Workflow Interceptor that counts the number of Workflow Executions as well as the number of Signals and Queries. | ||||||||||||||
|
|
||||||||||||||
| ## Start local Temporal Server | ||||||||||||||
| ```bash | ||||||||||||||
| # run only once | ||||||||||||||
| temporal server start-dev | ||||||||||||||
| ``` | ||||||||||||||
|
||||||||||||||
| ## Start local Temporal Server | |
| ```bash | |
| # run only once | |
| temporal server start-dev | |
| ``` | |
| To run, first see [README.md](https://github.com/temporalio/samples-dotnet/blob/main/README.md) for prerequisites |
We like to keep the starting of the local server in one place since it can change and there are multiple options. This can be seen in other samples. (in fact, we will make these run easy on cloud too soon, so we don't want to have to come back and update these)
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
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.
Finally addressed :)
Outdated
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 is more than the worker, it's the worker and the client calls mixed into one. Many samples split these, but don't have to here, but it's not just "worker" as the title suggests.
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
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.
Fixed.
Outdated
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.
I don't think this section applies anymore
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
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.
Deleted.
Uh oh!
There was an error while loading. Please reload this page.