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

Add timer sample #57

Merged
merged 6 commits into from
Apr 2, 2024
Merged
Show file tree
Hide file tree
Changes from 5 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
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ Prerequisites:
* [Polling](src/Polling) - Recommended implementation of an activity that needs to periodically poll an external resource waiting its successful completion.
* [Saga](src/Saga) - Demonstrates how to implement a saga pattern.
* [Schedules](src/Schedules) - How to schedule workflows to be run at specific times in the future.
* [Timer](src/Timer) - Use a timer to implement a monthly subscription; handle workflow cancellation.
* [WorkerSpecificTaskQueues](src/WorkerSpecificTaskQueues) - Use a unique task queue per Worker to have certain Activities only run on that specific Worker.
* [WorkerVersioning](src/WorkerVersioning) - How to use the Worker Versioning feature to more easily deploy changes to Workflow & other code.
* [WorkflowUpdate](src/WorkflowUpdate) - How to use the Workflow Update feature while blocking in update method for concurrent updates.
Expand Down
9 changes: 9 additions & 0 deletions src/Timer/MyActivities.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
namespace TemporalioSamples.Timer;

using Temporalio.Activities;

public class MyActivities
{
[Activity]
public static string Charge(string userId) => "charge successful";
}
63 changes: 63 additions & 0 deletions src/Timer/Program.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
using Microsoft.Extensions.Logging;
using Temporalio.Client;
using Temporalio.Worker;
using TemporalioSamples.Timer;

// Create a client to localhost on default namespace
var client = await TemporalClient.ConnectAsync(new("localhost:7233")
{
LoggerFactory = LoggerFactory.Create(builder =>
builder.
AddSimpleConsole(options => options.TimestampFormat = "[HH:mm:ss] ").
SetMinimumLevel(LogLevel.Information)),
});

async Task RunWorkerAsync()
Copy link
Member

Choose a reason for hiding this comment

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

We haven't on most samples, but we may find using https://github.com/temporalio/sdk-dotnet/tree/main/src/Temporalio.Extensions.Hosting for worker running to be easier in our samples here and elsewhere (this is what many of our users use)

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Making worker file shorter sgtm, going to leave this for future work for time's sake.

{
// Cancellation token cancelled on ctrl+c
using var tokenSource = new CancellationTokenSource();
Console.CancelKeyPress += (_, eventArgs) =>
{
tokenSource.Cancel();
eventArgs.Cancel = true;
};

// Create an activity instance with some state
var activities = new MyActivities();

// Run worker until cancelled
Console.WriteLine("Running worker");
using var worker = new TemporalWorker(
client,
new TemporalWorkerOptions(taskQueue: "timer-sample").
AddActivity(MyActivities.Charge).
AddWorkflow<Subscription>());
try
{
await worker.ExecuteAsync(tokenSource.Token);
}
catch (OperationCanceledException)
{
Console.WriteLine("Worker cancelled");
}
}

async Task ExecuteWorkflowAsync()
{
Console.WriteLine("Executing workflow");
await client.ExecuteWorkflowAsync(
(Subscription wf) => wf.RunAsync("user-id-123"),
new(id: "timer-workflow-id", taskQueue: "timer-sample"));
}

switch (args.ElementAtOrDefault(0))
{
case "worker":
await RunWorkerAsync();
break;
case "workflow":
await ExecuteWorkflowAsync();
break;
default:
throw new ArgumentException("Must pass 'worker' or 'workflow' as the single argument");
}
14 changes: 14 additions & 0 deletions src/Timer/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
# Timer

Use a timer (`Workflow.DelayAsync`) to implement a monthly subscription. Also, handle workflow cancellation.

To run, first see [README.md](../../README.md) for prerequisites. Then, run the following from this directory
in a separate terminal to start the worker:

dotnet run worker

Then in another terminal, run the workflow from this directory:

dotnet run workflow

The worker terminal will show logs from running the workflow.
32 changes: 32 additions & 0 deletions src/Timer/Subscription.workflow.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
namespace TemporalioSamples.Timer;

using Microsoft.Extensions.Logging;
using Temporalio.Workflows;

[Workflow]
public class Subscription
{
[WorkflowRun]
public async Task RunAsync(string userId)
{
try
{
while (true)
{
await Workflow.DelayAsync(TimeSpan.FromDays(30));

var result = await Workflow.ExecuteActivityAsync(
() => MyActivities.Charge(userId),
new() { StartToCloseTimeout = TimeSpan.FromMinutes(5) });
Workflow.Logger.LogInformation("Activity result: {Result}", result);
}
}
catch (OperationCanceledException)
lorensr marked this conversation as resolved.
Show resolved Hide resolved
{
Workflow.Logger.LogInformation("Workflow cancelled, cleaning up...");
// Handle any cleanup here
// Re-throw to close the workflow as Cancelled. Otherwise, it will be closed as Completed.
throw;
}
}
}
7 changes: 7 additions & 0 deletions src/Timer/TemporalioSamples.Timer.csproj
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
<Project Sdk="Microsoft.NET.Sdk">

<PropertyGroup>
<OutputType>Exe</OutputType>
</PropertyGroup>

</Project>
Loading