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

Update .NET SDK and add update-with-start samples #91

Merged
merged 3 commits into from
Jan 15, 2025
Merged
Show file tree
Hide file tree
Changes from 2 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
3 changes: 3 additions & 0 deletions .editorconfig
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,9 @@ dotnet_diagnostic.CA1307.severity = none
dotnet_diagnostic.CA1310.severity = none
dotnet_diagnostic.CA1311.severity = none

# In samples, we do not care about making public things internal
dotnet_diagnostic.CA1515.severity = none

# Don't need to worry about name clashes across samples
dotnet_diagnostic.CA1724.severity = none

Expand Down
12 changes: 6 additions & 6 deletions Directory.Build.props
Original file line number Diff line number Diff line change
Expand Up @@ -19,19 +19,19 @@
</PropertyGroup>

<ItemGroup>
<PackageReference Include="Temporalio" Version="1.3.1" />
<PackageReference Include="Temporalio.Extensions.DiagnosticSource" Version="1.3.1" />
<PackageReference Include="Temporalio.Extensions.Hosting" Version="1.3.1" />
<PackageReference Include="Temporalio.Extensions.OpenTelemetry" Version="1.3.1" />
<PackageReference Include="Temporalio" Version="1.4.0" />
<PackageReference Include="Temporalio.Extensions.DiagnosticSource" Version="1.4.0" />
<PackageReference Include="Temporalio.Extensions.Hosting" Version="1.4.0" />
<PackageReference Include="Temporalio.Extensions.OpenTelemetry" Version="1.4.0" />
<!--
Can also reference the SDK downloaded to a local directory:
<ProjectReference Include="$(MSBuildThisFileDirectory)..\temporal-sdk-dotnet\src\Temporalio\Temporalio.csproj" />
-->
</ItemGroup>

<ItemGroup>
<PackageReference Include="Microsoft.Extensions.Logging" Version="8.0.0" />
<PackageReference Include="Microsoft.Extensions.Logging.Console" Version="8.0.0" />
<PackageReference Include="Microsoft.Extensions.Logging" Version="8.0.1" />
<PackageReference Include="Microsoft.Extensions.Logging.Console" Version="8.0.1" />
<PackageReference Include="Microsoft.VisualStudio.Threading.Analyzers" Version="17.10.48" PrivateAssets="all" />
<PackageReference Include="StyleCop.Analyzers" Version="1.2.0-beta.435" PrivateAssets="all" />
</ItemGroup>
Expand Down
2 changes: 2 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,8 @@ Prerequisites:
* [Schedules](src/Schedules) - How to schedule workflows to be run at specific times in the future.
* [SignalsQueries](src/SignalsQueries) - A loyalty program using Signals and Queries.
* [Timer](src/Timer) - Use a timer to implement a monthly subscription; handle workflow cancellation.
* [UpdateWithStartEarlyReturn](src/UpdateWithStartEarlyReturn) - Use update with start to get an early return, letting the rest of the workflow complete in the background.
* [UpdateWithStartLazyInit](src/UpdateWithStartLazyInit) - Use update with start to lazily start a workflow before sending update.
* [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
16 changes: 15 additions & 1 deletion TemporalioSamples.sln
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,10 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "TemporalioSamples.Bedrock.S
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "TemporalioSamples.Bedrock.Entity", "src\Bedrock\Entity\TemporalioSamples.Bedrock.Entity.csproj", "{AE18875F-B7D2-4A3C-8784-15B76277D508}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "TemporalioSamples.UpdateWithStartLazyInit", "src\UpdateWithStartLazyInit\TemporalioSamples.UpdateWithStartLazyInit.csproj", "{B99AA6E8-4224-4BAB-84BC-0A6D6095B9BA}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "TemporalioSamples.UpdateWithStartEarlyReturn", "src\UpdateWithStartEarlyReturn\TemporalioSamples.UpdateWithStartEarlyReturn.csproj", "{69C72FEE-D1A1-4F3F-9B1A-462C9FF91B16}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Expand Down Expand Up @@ -209,6 +213,14 @@ Global
{AE18875F-B7D2-4A3C-8784-15B76277D508}.Debug|Any CPU.Build.0 = Debug|Any CPU
{AE18875F-B7D2-4A3C-8784-15B76277D508}.Release|Any CPU.ActiveCfg = Release|Any CPU
{AE18875F-B7D2-4A3C-8784-15B76277D508}.Release|Any CPU.Build.0 = Release|Any CPU
{B99AA6E8-4224-4BAB-84BC-0A6D6095B9BA}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{B99AA6E8-4224-4BAB-84BC-0A6D6095B9BA}.Debug|Any CPU.Build.0 = Debug|Any CPU
{B99AA6E8-4224-4BAB-84BC-0A6D6095B9BA}.Release|Any CPU.ActiveCfg = Release|Any CPU
{B99AA6E8-4224-4BAB-84BC-0A6D6095B9BA}.Release|Any CPU.Build.0 = Release|Any CPU
{69C72FEE-D1A1-4F3F-9B1A-462C9FF91B16}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{69C72FEE-D1A1-4F3F-9B1A-462C9FF91B16}.Debug|Any CPU.Build.0 = Debug|Any CPU
{69C72FEE-D1A1-4F3F-9B1A-462C9FF91B16}.Release|Any CPU.ActiveCfg = Release|Any CPU
{69C72FEE-D1A1-4F3F-9B1A-462C9FF91B16}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
Expand Down Expand Up @@ -242,9 +254,11 @@ Global
{F9C44936-8BF9-4919-BB66-8F1888E22AEB} = {1A647B41-53D0-4638-AE5A-6630BAAE45FC}
{751E0AF8-62EE-4220-A571-D1C53DD0D45A} = {1A647B41-53D0-4638-AE5A-6630BAAE45FC}
{4EA37A92-E4D5-4348-AF2F-0CAE37A1E079} = {1A647B41-53D0-4638-AE5A-6630BAAE45FC}
{5339989C-3791-4D75-A9F1-42620C443D4A} = {1A647B41-53D0-4638-AE5A-6630BAAE45FC}
{5339989C-3791-4D75-A9F1-42620C443D4A} = {1A647B41-53D0-4638-AE5A-6630BAAE45FC}
{CC3487CB-F795-4CA9-A4F6-28C145AE383B} = {5339989C-3791-4D75-A9F1-42620C443D4A}
{4E69DE72-E972-4155-B4D1-ABFE0B054ECF} = {5339989C-3791-4D75-A9F1-42620C443D4A}
{AE18875F-B7D2-4A3C-8784-15B76277D508} = {5339989C-3791-4D75-A9F1-42620C443D4A}
{B99AA6E8-4224-4BAB-84BC-0A6D6095B9BA} = {1A647B41-53D0-4638-AE5A-6630BAAE45FC}
{69C72FEE-D1A1-4F3F-9B1A-462C9FF91B16} = {1A647B41-53D0-4638-AE5A-6630BAAE45FC}
EndGlobalSection
EndGlobal
2 changes: 1 addition & 1 deletion src/AspNet/Worker/TemporalioSamples.AspNet.Worker.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,6 @@
</PropertyGroup>

<ItemGroup>
<PackageReference Include="Microsoft.Extensions.Hosting" Version="7.0.0" />
<PackageReference Include="Microsoft.Extensions.Hosting" Version="8.0.1" />
</ItemGroup>
</Project>
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
<Project Sdk="Microsoft.NET.Sdk.Worker">
<ItemGroup>
<PackageReference Include="Microsoft.Extensions.Hosting" Version="7.0.0" />
<PackageReference Include="Microsoft.Extensions.Hosting" Version="8.0.1" />
</ItemGroup>
</Project>
28 changes: 28 additions & 0 deletions src/UpdateWithStartEarlyReturn/Activities.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
namespace TemporalioSamples.UpdateWithStartEarlyReturn;

using Microsoft.Extensions.Logging;
using Temporalio.Activities;

public static class Activities
{
[Activity]
public static async Task AuthorizePaymentAsync(int amount)
{
ActivityExecutionContext.Current.Logger.LogInformation(
"Authorizing payment with ID {PaymentId} for amount {Amount}",
ActivityExecutionContext.Current.Info.WorkflowId,
amount);
// Simulate some time taken
await Task.Delay(1000);
}

[Activity]
public static async Task CapturePaymentAsync()
{
ActivityExecutionContext.Current.Logger.LogInformation(
"Capturing payment with ID {PaymentId}",
ActivityExecutionContext.Current.Info.WorkflowId);
// Simulate some time taken
await Task.Delay(2000);
}
}
34 changes: 34 additions & 0 deletions src/UpdateWithStartEarlyReturn/PaymentWorkflow.workflow.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
namespace TemporalioSamples.UpdateWithStartEarlyReturn;

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

[Workflow]
public class PaymentWorkflow
{
public record PaymentDetails(int Amount);

[WorkflowRun]
public async Task RunAsync(PaymentDetails details)
{
// Request authorization then mark as authorized so early return can complete
Workflow.Logger.LogInformation("Requesting authorization for {Amount}", details.Amount);
await Workflow.ExecuteActivityAsync(
() => Activities.AuthorizePaymentAsync(details.Amount),
new() { ScheduleToCloseTimeout = TimeSpan.FromMinutes(10) });
Authorized = true;

// Now we can do the heavier payment processing/capture
Workflow.Logger.LogInformation("Request authorized, requesting capture");
await Workflow.ExecuteActivityAsync(
() => Activities.CapturePaymentAsync(),
new() { ScheduleToCloseTimeout = TimeSpan.FromMinutes(10) });
Workflow.Logger.LogInformation("Capture complete");
}

[WorkflowQuery]
public bool Authorized { get; set; }

[WorkflowUpdate]
public Task WaitUntilAuthorizedAsync() => Workflow.WaitConditionAsync(() => Authorized);
}
83 changes: 83 additions & 0 deletions src/UpdateWithStartEarlyReturn/Program.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
using Microsoft.Extensions.Logging;
using Temporalio.Client;
using Temporalio.Worker;
using TemporalioSamples.UpdateWithStartEarlyReturn;

// 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)),
});

const string TaskQueue = "update-with-start-early-return";

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

// Run worker until cancelled
Console.WriteLine("Running worker");
using var worker = new TemporalWorker(
client,
new TemporalWorkerOptions(TaskQueue).
AddAllActivities(typeof(Activities), null).
AddWorkflow<PaymentWorkflow>());
try
{
await worker.ExecuteAsync(tokenSource.Token);
}
catch (OperationCanceledException)
{
Console.WriteLine("Worker cancelled");
}
}

async Task ExecuteWorkflowAsync()
{
const string paymentId = "my-early-return-payment-123";
Console.WriteLine("Starting payment processing and waiting for authorize");

// Issue payment for 500 as an update with start which will only wait until
// the update (i.e. authorize) is complete, not the whole workflow like
// traditional workflow runs
var startOperation = WithStartWorkflowOperation.Create(
(PaymentWorkflow wf) => wf.RunAsync(new(500)),
new(id: paymentId, taskQueue: TaskQueue)
{
// Unlike the lazy-init sample, we want to fail if this is already
// running, our only purpose here is early return, not get-or-create
// logic
IdConflictPolicy = Temporalio.Api.Enums.V1.WorkflowIdConflictPolicy.Fail,
});
await client.ExecuteUpdateWithStartWorkflowAsync(
(PaymentWorkflow wf) => wf.WaitUntilAuthorizedAsync(),
new(startOperation));
Console.WriteLine("Payment authorized, can move on while rest of payment processing finishes...");

// Go ahead and wait for payment to be complete (we don't have to do this,
// we're only doing it for the sample)
var handle = await startOperation.GetHandleAsync();
await handle.GetResultAsync();
Console.WriteLine("Payment processing complete");
}

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");
}
24 changes: 24 additions & 0 deletions src/UpdateWithStartEarlyReturn/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
# Update with Start - Early Return

This sample demonstrates a how to start a workflow and wait for only part of it to complete, letting the rest of the
workflow run in the background. This is demonstrated with a simple payment processing workflow that returns after
authorization while continuing the rest of the payment processing in the background
cretz marked this conversation as resolved.
Show resolved Hide resolved

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 workflow terminal will output something like:

```
Starting payment processing and waiting for authorize
Payment authorized, can move on while rest of payment processing finishes...
Payment processing complete
```

See the code for more details.
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>
24 changes: 24 additions & 0 deletions src/UpdateWithStartLazyInit/Activities.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
using Temporalio.Activities;

namespace TemporalioSamples.UpdateWithStartLazyInit;

public static class Activities
{
public const string InvalidSku = "sku-456";
public const decimal DefaultPrice = 5.99m;

[Activity]
public static async Task<decimal?> GetPriceAsync(ShoppingCartItem item)
{
// Simulate some time taken
await Task.Delay(100);

// Simulate a not-found price
if (item.Sku == InvalidSku)
{
return null;
}
// Simulate a price
return DefaultPrice * item.Quantity;
}
}
Loading
Loading