-
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 9 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,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; | ||
| } | ||
|
|
||
| [Activity] | ||
| public string SayGoodBye(string name, string title) | ||
|
||
| { | ||
| return "Goodbye " + title + " " + name; | ||
| } | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,17 @@ | ||
| namespace TemporalioSamples.CounterInterceptor; | ||
|
|
||
| using Temporalio.Workflows; | ||
|
|
||
| [Workflow] | ||
| public class MyChildWorkflow | ||
| { | ||
| private readonly ActivityOptions activityOptions = new() | ||
| { | ||
| StartToCloseTimeout = TimeSpan.FromSeconds(10), | ||
| }; | ||
|
|
||
| [WorkflowRun] | ||
| public async Task<string> RunAsync(string name, string title) => | ||
| await Workflow.ExecuteActivityAsync((MyActivities act) => act.SayHello(name, title), activityOptions) + | ||
| await Workflow.ExecuteActivityAsync((MyActivities act) => act.SayGoodBye(name, title), activityOptions); | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,42 @@ | ||
| namespace TemporalioSamples.CounterInterceptor; | ||
|
|
||
| using Temporalio.Workflows; | ||
|
|
||
| [Workflow] | ||
| public class MyWorkflow | ||
| { | ||
| private bool exit; // Automatically defaults to false | ||
|
|
||
| [WorkflowRun] | ||
| public async Task<string> RunAsync() | ||
| { | ||
| // Wait for greeting info | ||
| await Workflow.WaitConditionAsync(() => Name != null && Title != null); | ||
|
||
|
|
||
| // Execute Child Workflow | ||
| var result = await Workflow.ExecuteChildWorkflowAsync( | ||
| (MyChildWorkflow wf) => wf.RunAsync(Name, Title), | ||
| new() { Id = "counter-interceptor-child" }); | ||
|
|
||
| // Wait for exit signal | ||
| await Workflow.WaitConditionAsync(() => exit); | ||
|
|
||
| return result; | ||
| } | ||
|
|
||
| [WorkflowSignal] | ||
| public async Task SignalNameAndTitleAsync(string name, string title) | ||
| { | ||
| Name = name; | ||
| Title = title; | ||
| } | ||
|
|
||
| [WorkflowQuery] | ||
| public string Name { get; private set; } = string.Empty; | ||
|
|
||
| [WorkflowQuery] | ||
| public string Title { get; private set; } = string.Empty; | ||
|
|
||
| [WorkflowSignal] | ||
| public async Task ExitAsync() => exit = true; | ||
| } | ||
| Original file line number | Diff line number | Diff line change | ||||||||
|---|---|---|---|---|---|---|---|---|---|---|
| @@ -0,0 +1,86 @@ | ||||||||||
| namespace TemporalioSamples.CounterInterceptor; | ||||||||||
|
|
||||||||||
| using Temporalio.Client; | ||||||||||
| using Temporalio.Worker; | ||||||||||
|
|
||||||||||
| internal class Program | ||||||||||
| { | ||||||||||
| private static async Task Main(string[] args) | ||||||||||
| { | ||||||||||
| var clientInterceptor = new SimpleClientCallsInterceptor(); | ||||||||||
| var client = await TemporalClient.ConnectAsync( | ||||||||||
| options: new("localhost:7233") | ||||||||||
| { | ||||||||||
| Interceptors = new[] | ||||||||||
| { | ||||||||||
| clientInterceptor, | ||||||||||
| }, | ||||||||||
| }); | ||||||||||
|
|
||||||||||
| using var tokenSource = new CancellationTokenSource(); | ||||||||||
| Console.CancelKeyPress += (_, eventArgs) => | ||||||||||
|
||||||||||
| { | ||||||||||
| tokenSource.Cancel(); | ||||||||||
| eventArgs.Cancel = true; | ||||||||||
| }; | ||||||||||
|
|
||||||||||
| var activities = new MyActivities(); | ||||||||||
|
|
||||||||||
| var taskQueue = "CounterInterceptorTaskQueue"; | ||||||||||
|
|
||||||||||
| var workerOptions = new TemporalWorkerOptions(taskQueue). | ||||||||||
| AddAllActivities(activities). | ||||||||||
| AddWorkflow<MyWorkflow>(). | ||||||||||
| AddWorkflow<MyChildWorkflow>(); | ||||||||||
|
|
||||||||||
| var workerInterceptor = new SimpleCounterWorkerInterceptor(); | ||||||||||
| workerOptions.Interceptors = new[] { workerInterceptor }; | ||||||||||
|
|
||||||||||
| 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.RunAsync(), | ||||||||||
| new(id: Guid.NewGuid().ToString(), taskQueue: taskQueue)); | ||||||||||
|
|
||||||||||
| Console.WriteLine("Sending name and title to workflow"); | ||||||||||
| await handle.SignalAsync(wf => wf.SignalNameAndTitleAsync("John", "Customer")); | ||||||||||
|
|
||||||||||
| var name = await handle.QueryAsync(wf => wf.Name); | ||||||||||
| var title = await handle.QueryAsync(wf => wf.Title); | ||||||||||
|
|
||||||||||
| // 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 Worker Workflow Interceptor that counts the number of Workflow Executions, Child Workflow Executions, and Activity Executions and 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 Client Workflow Interceptor that counts the number of Workflow Executions and 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.
| Original file line number | Diff line number | Diff line change | ||||||||
|---|---|---|---|---|---|---|---|---|---|---|
| @@ -0,0 +1,97 @@ | ||||||||||
| namespace TemporalioSamples.CounterInterceptor; | ||||||||||
|
|
||||||||||
| using Temporalio.Client; | ||||||||||
| using Temporalio.Client.Interceptors; | ||||||||||
|
|
||||||||||
| public record ClientCounts | ||||||||||
|
||||||||||
| { | ||||||||||
| public uint Executions { get; internal set; } | ||||||||||
|
|
||||||||||
| public uint Signals { get; internal set; } | ||||||||||
|
|
||||||||||
| public uint Queries { get; internal set; } | ||||||||||
|
|
||||||||||
| public override string ToString() => | ||||||||||
| $"\n\tTotal Number of Workflow Exec: {Executions}\n\t" + | ||||||||||
| $"Total Number of Signals: {Signals}\n\t" + | ||||||||||
| $"Total Number of Queries: {Queries}"; | ||||||||||
| } | ||||||||||
|
|
||||||||||
| public class SimpleClientCallsInterceptor : IClientInterceptor | ||||||||||
|
||||||||||
| { | ||||||||||
| private const string NumberOfWorkflowExecutions = "numOfWorkflowExec"; | ||||||||||
| private const string NumberOfSignals = "numOfSignals"; | ||||||||||
| private const string NumberOfQueries = "numOfQueries"; | ||||||||||
| private static Dictionary<string, ClientCounts> clientDictionary = new(); | ||||||||||
|
||||||||||
|
|
||||||||||
| public ClientOutboundInterceptor InterceptClient(ClientOutboundInterceptor nextInterceptor) => | ||||||||||
| new ClientOutbound(this, nextInterceptor); | ||||||||||
|
|
||||||||||
| public string Info() => | ||||||||||
|
||||||||||
| string.Join( | ||||||||||
| "\n", | ||||||||||
| clientDictionary.Select(kvp => $"** Workflow ID: {kvp.Key} {kvp.Value}")); | ||||||||||
|
|
||||||||||
| public uint NumOfWorkflowExecutions(string workflowId) => | ||||||||||
| clientDictionary[workflowId].Executions; | ||||||||||
|
|
||||||||||
| public uint NumOfSignals(string workflowId) => | ||||||||||
| clientDictionary[workflowId].Signals; | ||||||||||
|
|
||||||||||
| public uint NumOfQueries(string workflowId) => | ||||||||||
| clientDictionary[workflowId].Queries; | ||||||||||
|
|
||||||||||
| private void Add(string workflowId, string type) | ||||||||||
|
||||||||||
| { | ||||||||||
| if (!clientDictionary.TryGetValue(workflowId, out ClientCounts? value)) | ||||||||||
| { | ||||||||||
| value = new ClientCounts(); | ||||||||||
| clientDictionary.Add(workflowId, value); | ||||||||||
| } | ||||||||||
|
|
||||||||||
| switch (type) | ||||||||||
| { | ||||||||||
| case NumberOfWorkflowExecutions: | ||||||||||
| value.Executions++; | ||||||||||
| break; | ||||||||||
| case NumberOfQueries: | ||||||||||
| value.Queries++; | ||||||||||
| break; | ||||||||||
| case NumberOfSignals: | ||||||||||
| value.Signals++; | ||||||||||
| break; | ||||||||||
| default: | ||||||||||
| throw new NotImplementedException("Unknown type: " + type); | ||||||||||
| } | ||||||||||
| } | ||||||||||
|
|
||||||||||
| private class ClientOutbound : ClientOutboundInterceptor | ||||||||||
| { | ||||||||||
| private SimpleClientCallsInterceptor root; | ||||||||||
|
|
||||||||||
| public ClientOutbound(SimpleClientCallsInterceptor root, ClientOutboundInterceptor next) | ||||||||||
| : base(next) => this.root = root; | ||||||||||
|
|
||||||||||
| public override Task<WorkflowHandle<TWorkflow, TResult>> StartWorkflowAsync<TWorkflow, TResult>( | ||||||||||
| StartWorkflowInput input) | ||||||||||
| { | ||||||||||
| var id = input.Options.Id ?? "None"; | ||||||||||
| root.Add(id, NumberOfWorkflowExecutions); | ||||||||||
|
||||||||||
| root.Add(id, NumberOfWorkflowExecutions); | |
| Interlocked.Increment(ref root.ClientCounts(id).Executions); |
Or
| root.Add(id, NumberOfWorkflowExecutions); | |
| root.UpdateClientCounts(id, counts => counts.Executions++); |
Depending on how you want to do the thread safety
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 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