├── .dockerignore
├── .env
├── .gitignore
├── .vscode
├── launch.json
└── tasks.json
├── Client
├── Client.csproj
├── CloudEvent.cs
├── Dockerfile
├── Program.cs
├── Properties
│ └── launchSettings.json
├── appsettings.Development.json
└── appsettings.json
├── README.md
├── Workflow
├── Activities
│ ├── AlwaysFailActivity.cs
│ ├── FastActivity.cs
│ ├── HelloActivity.cs
│ ├── NoOpActivity.cs
│ ├── NotifyCompensateActivity.cs
│ ├── RaiseEventActivity.cs
│ ├── SlowActivity.cs
│ └── VerySlowActivity.cs
├── CloudEvent.cs
├── Dockerfile
├── Program.cs
├── Properties
│ └── launchSettings.json
├── Workflow.csproj
├── Workflows
│ ├── ConstrainedWorkflow.cs
│ ├── ExternalSystemWorkflow.cs
│ ├── FanOutWorkflow.cs
│ ├── MonitorWorkflow.cs
│ └── ThrottleWorkflow.cs
├── appsettings.Development.json
└── appsettings.json
├── components
├── pubsub-kafka.yaml
└── statestore-pg-v2.yaml
├── compose-1-1.yml
├── compose-10-3.yml
├── compose-5-3.yml
├── compose-only-dependencies.yml
├── compose.debug-workflow-app.yml
├── dapr-config
└── config.yml
├── dapr-workflow-examples.sln
├── deploy.yaml
└── multirun.yaml
/.dockerignore:
--------------------------------------------------------------------------------
1 | **/.classpath
2 | **/.dockerignore
3 | **/.env
4 | **/.git
5 | **/.gitignore
6 | **/.project
7 | **/.settings
8 | **/.toolstarget
9 | **/.vs
10 | **/.vscode
11 | **/*.*proj.user
12 | **/*.dbmdl
13 | **/*.jfm
14 | **/bin
15 | **/charts
16 | **/docker-compose*
17 | **/compose*
18 | **/Dockerfile*
19 | **/node_modules
20 | **/npm-debug.log
21 | **/obj
22 | **/secrets.dev.yaml
23 | **/values.dev.yaml
24 | LICENSE
25 | README.md
26 |
--------------------------------------------------------------------------------
/.env:
--------------------------------------------------------------------------------
1 | DAPR_RUNTIME_VERSION=1.15.4
2 | DAPR_SCHEDULER_VERSION=1.15.4
3 | DAPR_PLACEMENT_VERSION=1.15.4
4 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | ## Ignore Visual Studio temporary files, build results, and
2 | ## files generated by popular Visual Studio add-ons.
3 | ##
4 | ## Get latest from https://github.com/github/gitignore/blob/master/VisualStudio.gitignore
5 |
6 | # User-specific files
7 | *.rsuser
8 | *.suo
9 | *.user
10 | *.userosscache
11 | *.sln.docstates
12 |
13 | # User-specific files (MonoDevelop/Xamarin Studio)
14 | *.userprefs
15 |
16 | # Mono auto generated files
17 | mono_crash.*
18 |
19 | # Build results
20 | [Dd]ebug/
21 | [Dd]ebugPublic/
22 | [Rr]elease/
23 | [Rr]eleases/
24 | x64/
25 | x86/
26 | [Aa][Rr][Mm]/
27 | [Aa][Rr][Mm]64/
28 | bld/
29 | [Bb]in/
30 | [Oo]bj/
31 | [Ll]og/
32 | [Ll]ogs/
33 |
34 | # Visual Studio 2015/2017 cache/options directory
35 | .vs/
36 | # Uncomment if you have tasks that create the project's static files in wwwroot
37 | #wwwroot/
38 |
39 | # Visual Studio 2017 auto generated files
40 | Generated\ Files/
41 |
42 | # MSTest test Results
43 | [Tt]est[Rr]esult*/
44 | [Bb]uild[Ll]og.*
45 |
46 | # NUnit
47 | *.VisualState.xml
48 | TestResult.xml
49 | nunit-*.xml
50 |
51 | # Build Results of an ATL Project
52 | [Dd]ebugPS/
53 | [Rr]eleasePS/
54 | dlldata.c
55 |
56 | # Benchmark Results
57 | BenchmarkDotNet.Artifacts/
58 |
59 | # .NET Core
60 | project.lock.json
61 | project.fragment.lock.json
62 | artifacts/
63 |
64 | # StyleCop
65 | StyleCopReport.xml
66 |
67 | # Files built by Visual Studio
68 | *_i.c
69 | *_p.c
70 | *_h.h
71 | *.ilk
72 | *.meta
73 | *.obj
74 | *.iobj
75 | *.pch
76 | *.pdb
77 | *.ipdb
78 | *.pgc
79 | *.pgd
80 | *.rsp
81 | *.sbr
82 | *.tlb
83 | *.tli
84 | *.tlh
85 | *.tmp
86 | *.tmp_proj
87 | *_wpftmp.csproj
88 | *.log
89 | *.vspscc
90 | *.vssscc
91 | .builds
92 | *.pidb
93 | *.svclog
94 | *.scc
95 |
96 | # Chutzpah Test files
97 | _Chutzpah*
98 |
99 | # Visual C++ cache files
100 | ipch/
101 | *.aps
102 | *.ncb
103 | *.opendb
104 | *.opensdf
105 | *.sdf
106 | *.cachefile
107 | *.VC.db
108 | *.VC.VC.opendb
109 |
110 | # Visual Studio profiler
111 | *.psess
112 | *.vsp
113 | *.vspx
114 | *.sap
115 |
116 | # Visual Studio Trace Files
117 | *.e2e
118 |
119 | # TFS 2012 Local Workspace
120 | $tf/
121 |
122 | # Guidance Automation Toolkit
123 | *.gpState
124 |
125 | # ReSharper is a .NET coding add-in
126 | _ReSharper*/
127 | *.[Rr]e[Ss]harper
128 | *.DotSettings.user
129 |
130 | # TeamCity is a build add-in
131 | _TeamCity*
132 |
133 | # DotCover is a Code Coverage Tool
134 | *.dotCover
135 |
136 | # AxoCover is a Code Coverage Tool
137 | .axoCover/*
138 | !.axoCover/settings.json
139 |
140 | # Visual Studio code coverage results
141 | *.coverage
142 | *.coveragexml
143 |
144 | # NCrunch
145 | _NCrunch_*
146 | .*crunch*.local.xml
147 | nCrunchTemp_*
148 |
149 | # MightyMoose
150 | *.mm.*
151 | AutoTest.Net/
152 |
153 | # Web workbench (sass)
154 | .sass-cache/
155 |
156 | # Installshield output folder
157 | [Ee]xpress/
158 |
159 | # DocProject is a documentation generator add-in
160 | DocProject/buildhelp/
161 | DocProject/Help/*.HxT
162 | DocProject/Help/*.HxC
163 | DocProject/Help/*.hhc
164 | DocProject/Help/*.hhk
165 | DocProject/Help/*.hhp
166 | DocProject/Help/Html2
167 | DocProject/Help/html
168 |
169 | # Click-Once directory
170 | publish/
171 |
172 | # Publish Web Output
173 | *.[Pp]ublish.xml
174 | *.azurePubxml
175 | # Note: Comment the next line if you want to checkin your web deploy settings,
176 | # but database connection strings (with potential passwords) will be unencrypted
177 | *.pubxml
178 | *.publishproj
179 |
180 | # Microsoft Azure Web App publish settings. Comment the next line if you want to
181 | # checkin your Azure Web App publish settings, but sensitive information contained
182 | # in these scripts will be unencrypted
183 | PublishScripts/
184 |
185 | # NuGet Packages
186 | *.nupkg
187 | # NuGet Symbol Packages
188 | *.snupkg
189 | # The packages folder can be ignored because of Package Restore
190 | **/[Pp]ackages/*
191 | # except build/, which is used as an MSBuild target.
192 | !**/[Pp]ackages/build/
193 | # Uncomment if necessary however generally it will be regenerated when needed
194 | #!**/[Pp]ackages/repositories.config
195 | # NuGet v3's project.json files produces more ignorable files
196 | *.nuget.props
197 | *.nuget.targets
198 |
199 | # Microsoft Azure Build Output
200 | csx/
201 | *.build.csdef
202 |
203 | # Microsoft Azure Emulator
204 | ecf/
205 | rcf/
206 |
207 | # Windows Store app package directories and files
208 | AppPackages/
209 | BundleArtifacts/
210 | Package.StoreAssociation.xml
211 | _pkginfo.txt
212 | *.appx
213 | *.appxbundle
214 | *.appxupload
215 |
216 | # Visual Studio cache files
217 | # files ending in .cache can be ignored
218 | *.[Cc]ache
219 | # but keep track of directories ending in .cache
220 | !?*.[Cc]ache/
221 |
222 | # Others
223 | ClientBin/
224 | ~$*
225 | *~
226 | *.dbmdl
227 | *.dbproj.schemaview
228 | *.jfm
229 | *.pfx
230 | *.publishsettings
231 | orleans.codegen.cs
232 |
233 | # Including strong name files can present a security risk
234 | # (https://github.com/github/gitignore/pull/2483#issue-259490424)
235 | #*.snk
236 |
237 | # Since there are multiple workflows, uncomment next line to ignore bower_components
238 | # (https://github.com/github/gitignore/pull/1529#issuecomment-104372622)
239 | #bower_components/
240 |
241 | # RIA/Silverlight projects
242 | Generated_Code/
243 |
244 | # Backup & report files from converting an old project file
245 | # to a newer Visual Studio version. Backup files are not needed,
246 | # because we have git ;-)
247 | _UpgradeReport_Files/
248 | Backup*/
249 | UpgradeLog*.XML
250 | UpgradeLog*.htm
251 | ServiceFabricBackup/
252 | *.rptproj.bak
253 |
254 | # SQL Server files
255 | *.mdf
256 | *.ldf
257 | *.ndf
258 |
259 | # Business Intelligence projects
260 | *.rdl.data
261 | *.bim.layout
262 | *.bim_*.settings
263 | *.rptproj.rsuser
264 | *- [Bb]ackup.rdl
265 | *- [Bb]ackup ([0-9]).rdl
266 | *- [Bb]ackup ([0-9][0-9]).rdl
267 |
268 | # Microsoft Fakes
269 | FakesAssemblies/
270 |
271 | # GhostDoc plugin setting file
272 | *.GhostDoc.xml
273 |
274 | # Node.js Tools for Visual Studio
275 | .ntvs_analysis.dat
276 | node_modules/
277 |
278 | # Visual Studio 6 build log
279 | *.plg
280 |
281 | # Visual Studio 6 workspace options file
282 | *.opt
283 |
284 | # Visual Studio 6 auto-generated workspace file (contains which files were open etc.)
285 | *.vbw
286 |
287 | # Visual Studio LightSwitch build output
288 | **/*.HTMLClient/GeneratedArtifacts
289 | **/*.DesktopClient/GeneratedArtifacts
290 | **/*.DesktopClient/ModelManifest.xml
291 | **/*.Server/GeneratedArtifacts
292 | **/*.Server/ModelManifest.xml
293 | _Pvt_Extensions
294 |
295 | # Paket dependency manager
296 | .paket/paket.exe
297 | paket-files/
298 |
299 | # FAKE - F# Make
300 | .fake/
301 |
302 | # CodeRush personal settings
303 | .cr/personal
304 |
305 | # Python Tools for Visual Studio (PTVS)
306 | __pycache__/
307 | *.pyc
308 |
309 | # Cake - Uncomment if you are using it
310 | # tools/**
311 | # !tools/packages.config
312 |
313 | # Tabs Studio
314 | *.tss
315 |
316 | # Telerik's JustMock configuration file
317 | *.jmconfig
318 |
319 | # BizTalk build output
320 | *.btp.cs
321 | *.btm.cs
322 | *.odx.cs
323 | *.xsd.cs
324 |
325 | # OpenCover UI analysis results
326 | OpenCover/
327 |
328 | # Azure Stream Analytics local run output
329 | ASALocalRun/
330 |
331 | # MSBuild Binary and Structured Log
332 | *.binlog
333 |
334 | # NVidia Nsight GPU debugger configuration file
335 | *.nvuser
336 |
337 | # MFractors (Xamarin productivity tool) working folder
338 | .mfractor/
339 |
340 | # Local History for Visual Studio
341 | .localhistory/
342 |
343 | # BeatPulse healthcheck temp database
344 | healthchecksdb
345 |
346 | # Backup folder for Package Reference Convert tool in Visual Studio 2017
347 | MigrationBackup/
348 |
349 | # Ionide (cross platform F# VS Code tools) working folder
350 | .ionide/
351 | /dapr_scheduler
352 |
353 | .DS_store
--------------------------------------------------------------------------------
/.vscode/launch.json:
--------------------------------------------------------------------------------
1 | {
2 | "version": "0.2.0",
3 | "configurations": [
4 | {
5 | "name": "Debug workflow app",
6 | "type": "coreclr",
7 | "request": "launch",
8 | "preLaunchTask": "build",
9 | "program": "${workspaceFolder}/Workflow/bin/Debug/net6.0/Workflow.dll",
10 | "args": [],
11 | "cwd": "${workspaceFolder}",
12 | "stopAtEntry": false,
13 | "env": {
14 | "ASPNETCORE_ENVIRONMENT": "Development",
15 | "ASPNETCORE_URLS": "http://localhost:5111",
16 | "DAPR_HTTP_PORT": "3500",
17 | "DAPR_GRPC_PORT": "50001"
18 | },
19 | "sourceFileMap": {
20 | "/Views": "${workspaceFolder}/Views"
21 | }
22 | },
23 | {
24 | "name": "Docker .NET Attach (Preview)",
25 | "type": "docker",
26 | "request": "attach",
27 | "platform": "netCore",
28 | "sourceFileMap": {
29 | "/src": "${workspaceFolder}"
30 | }
31 | },
32 | {
33 | "name": ".NET Core Attach",
34 | "type": "coreclr",
35 | "request": "attach"
36 | },
37 | {
38 | "name": "Docker .NET Launch",
39 | "type": "docker",
40 | "request": "launch",
41 | "preLaunchTask": "docker-run: debug",
42 | "netCore": {
43 | "appProject": "${workspaceFolder}/Workflow/Workflow.csproj"
44 | }
45 | }
46 | ]
47 | }
--------------------------------------------------------------------------------
/.vscode/tasks.json:
--------------------------------------------------------------------------------
1 | {
2 | "version": "2.0.0",
3 | "tasks": [
4 | {
5 | "label": "build",
6 | "command": "dotnet",
7 | "type": "process",
8 | "args": [
9 | "build",
10 | "${workspaceFolder}/Workflow/Workflow.csproj",
11 | "/property:GenerateFullPaths=true",
12 | "/consoleloggerparameters:NoSummary"
13 | ],
14 | "problemMatcher": "$msCompile"
15 | },
16 | {
17 | "label": "publish",
18 | "command": "dotnet",
19 | "type": "process",
20 | "args": [
21 | "publish",
22 | "${workspaceFolder}/Workflow/Workflow.csproj",
23 | "/property:GenerateFullPaths=true",
24 | "/consoleloggerparameters:NoSummary"
25 | ],
26 | "problemMatcher": "$msCompile"
27 | },
28 | {
29 | "label": "watch",
30 | "command": "dotnet",
31 | "type": "process",
32 | "args": [
33 | "watch",
34 | "run",
35 | "--project",
36 | "${workspaceFolder}/Workflow/Workflow.csproj"
37 | ],
38 | "problemMatcher": "$msCompile"
39 | },
40 | {
41 | "label": "bridge-to-kubernetes.resource",
42 | "type": "bridge-to-kubernetes.resource",
43 | "resource": "workflow-dapr",
44 | "resourceType": "service",
45 | "ports": [
46 | 5111
47 | ],
48 | "targetCluster": "docker-desktop",
49 | "targetNamespace": "default",
50 | "useKubernetesServiceEnvironmentVariables": false
51 | },
52 | {
53 | "label": "bridge-to-kubernetes.compound",
54 | "dependsOn": [
55 | "bridge-to-kubernetes.resource",
56 | "build"
57 | ],
58 | "dependsOrder": "sequence"
59 | },
60 | {
61 | "type": "docker-build",
62 | "label": "docker-build: debug",
63 | "dependsOn": [
64 | "build"
65 | ],
66 | "dockerBuild": {
67 | "tag": "daprworkflowexamples:dev",
68 | "target": "base",
69 | "dockerfile": "${workspaceFolder}/Workflow/Dockerfile",
70 | "context": "${workspaceFolder}",
71 | "pull": true
72 | },
73 | "netCore": {
74 | "appProject": "${workspaceFolder}/Workflow/Workflow.csproj"
75 | }
76 | },
77 | {
78 | "type": "docker-build",
79 | "label": "docker-build: release",
80 | "dependsOn": [
81 | "build"
82 | ],
83 | "dockerBuild": {
84 | "tag": "daprworkflowexamples:latest",
85 | "dockerfile": "${workspaceFolder}/Workflow/Dockerfile",
86 | "context": "${workspaceFolder}",
87 | "pull": true
88 | },
89 | "netCore": {
90 | "appProject": "${workspaceFolder}/Workflow/Workflow.csproj"
91 | }
92 | },
93 | {
94 | "type": "docker-run",
95 | "label": "docker-run: debug",
96 | "dependsOn": [
97 | "docker-build: debug"
98 | ],
99 | "dockerRun": {},
100 | "netCore": {
101 | "appProject": "${workspaceFolder}/Workflow/Workflow.csproj",
102 | "enableDebugging": true
103 | }
104 | },
105 | {
106 | "type": "docker-run",
107 | "label": "docker-run: release",
108 | "dependsOn": [
109 | "docker-build: release"
110 | ],
111 | "dockerRun": {},
112 | "netCore": {
113 | "appProject": "${workspaceFolder}/Workflow/Workflow.csproj"
114 | }
115 | }
116 | ]
117 | }
--------------------------------------------------------------------------------
/Client/Client.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | net6.0
5 | enable
6 | enable
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
--------------------------------------------------------------------------------
/Client/CloudEvent.cs:
--------------------------------------------------------------------------------
1 | using System.Text.Json.Serialization;
2 | using System.Net.Mime;
3 |
4 | namespace Workflow;
5 |
6 | public class CustomCloudEvent : Dapr.CloudEvent
7 | {
8 | public CustomCloudEvent(TData data) : base(data)
9 | {
10 |
11 | }
12 |
13 | [JsonPropertyName("id")]
14 | public string Id { get; init; }
15 |
16 | [JsonPropertyName("specversion")]
17 | public string Specversion { get; init; }
18 |
19 | [JsonPropertyName("my-custom-property")]
20 | public string MyCustomProperty { get ;init; }
21 | }
--------------------------------------------------------------------------------
/Client/Dockerfile:
--------------------------------------------------------------------------------
1 | FROM mcr.microsoft.com/dotnet/aspnet:6.0 AS base
2 | WORKDIR /app
3 | EXPOSE 5111
4 |
5 | ENV ASPNETCORE_URLS=http://+:5111
6 |
7 | # Creates a non-root user with an explicit UID and adds permission to access the /app folder
8 | # For more info, please refer to https://aka.ms/vscode-docker-dotnet-configure-containers
9 | RUN adduser -u 5678 --disabled-password --gecos "" appuser && chown -R appuser /app
10 | USER appuser
11 |
12 | FROM mcr.microsoft.com/dotnet/sdk:6.0 AS build
13 | WORKDIR /src
14 | COPY ["Client/Client.csproj", "Client/"]
15 | RUN dotnet restore "Client/Client.csproj"
16 | COPY . .
17 | WORKDIR "/src/Client"
18 | RUN dotnet build "Client.csproj" -c Release -o /app/build
19 |
20 | FROM build AS publish
21 | RUN dotnet publish "Client.csproj" -c Release -o /app/publish /p:UseAppHost=false
22 |
23 | FROM base AS final
24 | WORKDIR /app
25 | COPY --from=publish /app/publish .
26 | ENTRYPOINT ["dotnet", "Client.dll"]
27 |
--------------------------------------------------------------------------------
/Client/Program.cs:
--------------------------------------------------------------------------------
1 | using Dapr;
2 | using Dapr.Client;
3 | using Workflow;
4 |
5 | var builder = WebApplication.CreateBuilder(args);
6 |
7 | builder.Services.AddDaprClient();
8 |
9 | // Add services to the container.
10 | builder.Services.AddEndpointsApiExplorer();
11 | builder.Services.AddSwaggerGen();
12 |
13 | var app = builder.Build();
14 |
15 | // Configure the HTTP request pipeline.
16 | if (app.Environment.IsDevelopment())
17 | {
18 | app.UseSwagger();
19 | app.UseSwaggerUI();
20 | }
21 |
22 | app.MapPost("/health", async () =>
23 | {
24 |
25 | app.Logger.LogInformation("Hello from Client!");
26 |
27 | return "Hello from Client!!";
28 | });
29 |
30 | app.MapPost("/start/monitor-workflow", async (DaprClient daprClient, string runId, int? count, bool? async, int? sleep, string? abortHint) =>
31 | {
32 | if (!count.HasValue || count.Value < 1)
33 | count = 1;
34 |
35 | if (!sleep.HasValue)
36 | sleep = 0;
37 |
38 | var results = new List();
39 |
40 | var cts = new CancellationTokenSource();
41 |
42 | var options = new ParallelOptions() { MaxDegreeOfParallelism = 50, CancellationToken = cts.Token };
43 |
44 | await Parallel.ForEachAsync(Enumerable.Range(0, count.Value), options, async (index, token) =>
45 | {
46 | var request = new StartWorkflowRequest
47 | {
48 | Id = $"{index}-{runId}",
49 | Sleep = sleep.Value,
50 | AbortHint = abortHint
51 | };
52 |
53 | var metadata = new Dictionary
54 | {
55 | { "cloudevent.id", request.Id },
56 | { "cloudevent.type", "Continue As New"} ,
57 | { "my-custom-property", "foo" },
58 | { "partitionKey", Guid.NewGuid().ToString() }
59 | };
60 |
61 | if (async.HasValue && async.Value == true)
62 | {
63 | await daprClient.PublishEventAsync("kafka-pubsub", "monitor-workflow", request, metadata, cts.Token);
64 | }
65 | else
66 | {
67 | var wrappedRequest = new CustomCloudEvent(request)
68 | {
69 | Id = request.Id,
70 | };
71 | await daprClient.InvokeMethodAsync, StartWorkflowResponse>("workflow-a", "monitor-workflow", wrappedRequest, cts.Token);
72 | }
73 | app.Logger.LogInformation("start Id: {0}", request.Id);
74 |
75 | results.Add(new StartWorkflowResponse { Index = index, Id = request.Id });
76 | });
77 | return results;
78 | }).Produces>();
79 |
80 |
81 | app.MapPost("/start-raise-event-workflow", async (DaprClient daprClient, string runId, int? count, bool? failOnTimeout, int? sleep, string? abortHint) =>
82 | {
83 | if (!count.HasValue || count.Value < 1)
84 | count = 1;
85 |
86 | if (!sleep.HasValue)
87 | sleep = 0;
88 |
89 | if (!failOnTimeout.HasValue)
90 | failOnTimeout = false;
91 |
92 | var results = new List();
93 |
94 | var cts = new CancellationTokenSource();
95 | var options = new ParallelOptions() { MaxDegreeOfParallelism = 50, CancellationToken = cts.Token };
96 | await Parallel.ForEachAsync(Enumerable.Range(0, count.Value), options, async (index, token) =>
97 | {
98 | var request = new StartWorkflowRequest
99 | {
100 | Id = $"{index}-{runId}",
101 | Sleep = sleep.Value,
102 | AbortHint = abortHint,
103 | FailOnTimeout = failOnTimeout.Value
104 | };
105 |
106 | await daprClient.PublishEventAsync("kafka-pubsub", "start-raise-event-workflow", request, cts.Token);
107 |
108 | app.Logger.LogInformation("start-raise-event-workflow Id: {0}", request.Id);
109 |
110 | results.Add(new StartWorkflowResponse { Index = index, Id = request.Id });
111 | });
112 | return results;
113 | }).Produces>();
114 |
115 |
116 | app.MapPost("/start/fanout-workflow", async (DaprClient daprClient, string runId, int? count, bool? async, int? sleep, string? abortHint) =>
117 | {
118 | if (!count.HasValue || count.Value < 1)
119 | count = 1;
120 |
121 | if (!sleep.HasValue)
122 | sleep = 0;
123 |
124 | var results = new List();
125 |
126 | var cts = new CancellationTokenSource();
127 |
128 | var options = new ParallelOptions() { MaxDegreeOfParallelism = 50, CancellationToken = cts.Token };
129 |
130 | await Parallel.ForEachAsync(Enumerable.Range(0, count.Value), options, async (index, token) =>
131 | {
132 | var request = new StartWorkflowRequest
133 | {
134 | Id = $"{index}-{runId}",
135 | Sleep = sleep.Value,
136 | AbortHint = abortHint
137 | };
138 |
139 | if (async.HasValue && async.Value == true)
140 | await daprClient.PublishEventAsync("kafka-pubsub", "fanout-workflow", request, cts.Token);
141 | else
142 | {
143 | var wrappedRequest = new CustomCloudEvent(request)
144 | {
145 | Id = request.Id,
146 | };
147 | await daprClient.InvokeMethodAsync, StartWorkflowResponse>("workflow-a", "fanout-workflow", wrappedRequest, cts.Token);
148 | }
149 | app.Logger.LogInformation("start Id: {0}", request.Id);
150 |
151 | results.Add(new StartWorkflowResponse { Index = index, Id = request.Id });
152 | });
153 | return results;
154 | }).Produces>();
155 |
156 |
157 | app.MapPost("/start/schedule-job", async (DaprClient daprClient, string runId, int? count, bool? async, int? sleep, string? abortHint) =>
158 | {
159 | if (!count.HasValue || count.Value < 1)
160 | count = 1;
161 |
162 | if (!sleep.HasValue)
163 | sleep = 0;
164 |
165 | var results = new List();
166 |
167 | var cts = new CancellationTokenSource();
168 |
169 | var options = new ParallelOptions() { MaxDegreeOfParallelism = 50, CancellationToken = cts.Token };
170 |
171 | await Parallel.ForEachAsync(Enumerable.Range(0, count.Value), options, async (index, token) =>
172 | {
173 | var request = new StartWorkflowRequest
174 | {
175 | Id = $"{index}-{runId}",
176 | Sleep = sleep.Value,
177 | AbortHint = abortHint
178 | };
179 |
180 | if (async.HasValue && async.Value == true)
181 | await daprClient.PublishEventAsync("kafka-pubsub", "schedule-job", request, cts.Token);
182 | else
183 | {
184 | var wrappedRequest = new CustomCloudEvent(request)
185 | {
186 | Id = request.Id,
187 | };
188 | await daprClient.InvokeMethodAsync, StartWorkflowResponse>("workflow-a", "fanout-workflow", wrappedRequest, cts.Token);
189 | }
190 | app.Logger.LogInformation("start Id: {0}", request.Id);
191 |
192 | results.Add(new StartWorkflowResponse { Index = index, Id = request.Id });
193 | });
194 | return results;
195 | }).Produces>();
196 |
197 |
198 |
199 | app.Run();
200 |
201 | public class StartWorkflowRequest
202 | {
203 | public string Id { get; set; }
204 | public bool FailOnTimeout { get; set; }
205 | public int Sleep { get; set; }
206 | public string AbortHint { get; set; }
207 | }
208 |
209 | public class StartWorkflowResponse
210 | {
211 | public int Index { get; set; }
212 | public string Id { get; set; }
213 | }
214 |
215 | public class RaiseEvent
216 | {
217 | public string InstanceId { get; set; }
218 | public string EventName { get; set; }
219 | public T EventData { get; set; }
220 | }
221 |
--------------------------------------------------------------------------------
/Client/Properties/launchSettings.json:
--------------------------------------------------------------------------------
1 | {
2 | "$schema": "https://json.schemastore.org/launchsettings.json",
3 | "iisSettings": {
4 | "windowsAuthentication": false,
5 | "anonymousAuthentication": true,
6 | "iisExpress": {
7 | "applicationUrl": "http://localhost:50807",
8 | "sslPort": 44394
9 | }
10 | },
11 | "profiles": {
12 | "WorkflowApi": {
13 | "commandName": "Project",
14 | "dotnetRunMessages": true,
15 | "launchBrowser": true,
16 | "launchUrl": "swagger",
17 | "applicationUrl": "https://localhost:7223;http://localhost:5111",
18 | "environmentVariables": {
19 | "ASPNETCORE_ENVIRONMENT": "Development"
20 | }
21 | },
22 | "IIS Express": {
23 | "commandName": "IISExpress",
24 | "launchBrowser": true,
25 | "launchUrl": "swagger",
26 | "environmentVariables": {
27 | "ASPNETCORE_ENVIRONMENT": "Development"
28 | }
29 | }
30 | }
31 | }
32 |
--------------------------------------------------------------------------------
/Client/appsettings.Development.json:
--------------------------------------------------------------------------------
1 | {
2 | "Logging": {
3 | "LogLevel": {
4 | "Default": "Information",
5 | "Microsoft.AspNetCore": "Warning",
6 | "System.Net.Http.HttpClient": "Warning"
7 | }
8 | }
9 | }
--------------------------------------------------------------------------------
/Client/appsettings.json:
--------------------------------------------------------------------------------
1 | {
2 | "Logging": {
3 | "LogLevel": {
4 | "Default": "Information",
5 | "Microsoft.AspNetCore": "Warning",
6 | "System.Net.Http.HttpClient": "Warning"
7 | }
8 | },
9 | "AllowedHosts": "*"
10 | }
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | ## Dapr Workflow Testing
2 |
3 | > [!IMPORTANT]
4 | > This repo is **purely** for load testing Dapr Workflows runtime.
5 | > It is **not** an example of what _good_ looks like! Most of the code is junk, to meet the end goal of testing the Workflows Runtime.
6 |
7 | In the past, this has flushed out many concurrency issues in the underlying durabletask-go library, which have been subsequently addressed in newer versions of the Dapr runtime.
8 |
9 | The dapr runtime version for all compose files is specified in the `.env` file.
10 |
11 | ---
12 |
13 | ### Run with 5 instances of the Workflow App, and 3 instances of the scheduler service
14 |
15 | 1. `docker compose -f compose-5-3.yml build`
16 | 2. `docker compose -f compose-5-3.yml up`
17 |
18 |
19 | ### Run a simple monitor pattern workflow
20 |
21 | This will create many workflow instances randomly distributed across the Workflow App instances.
22 |
23 | Run the workflows by making a POST request to :
24 |
25 | ```http://localhost:5112/start/monitor-workflow?runId={runId}&count=1000&async=true```
26 |
27 | - Where `{runId}` is a unique value i.e. UUID/GUID.
28 | - Increase/decrease the amount of workflows created by changing the `count` property
29 |
30 | ### Run a simple fan-out & fan-in pattern workflow
31 |
32 | This will create many workflow instances randomly distributed across the Workflow App instances.
33 |
34 | Run the workflows by making a POST request to :
35 |
36 | ```http://localhost:5112/start/fanout-workflow?runId={runId}&count=1000&async=true```
37 |
38 | - Where `{runId}` is a unique value i.e. UUID/GUID.
39 | - Increase/decrease the amount of workflows created by changing the `count` property
40 |
--------------------------------------------------------------------------------
/Workflow/Activities/AlwaysFailActivity.cs:
--------------------------------------------------------------------------------
1 | using Dapr.Workflow;
2 | using Microsoft.Extensions.Logging;
3 |
4 | namespace WorkflowConsoleApp.Activities
5 | {
6 | public class AlwaysFailActivity : WorkflowActivity
7 | {
8 | readonly ILogger logger;
9 |
10 | public AlwaysFailActivity(ILoggerFactory loggerFactory)
11 | {
12 | this.logger = loggerFactory.CreateLogger();
13 | }
14 |
15 | public override Task