Skip to content

Add standalone-activity support to throughput_stress (RE-353)#403

Merged
lilydoar merged 11 commits into
mainfrom
lilydoar/re-353-standalone-activity
Jun 22, 2026
Merged

Add standalone-activity support to throughput_stress (RE-353)#403
lilydoar merged 11 commits into
mainfrom
lilydoar/re-353-standalone-activity

Conversation

@lilydoar

@lilydoar lilydoar commented Jun 10, 2026

Copy link
Copy Markdown
Contributor

What was changed

Adds an include-standalone-activity scenario option to throughput_stress that starts activities outside any workflow context via StartActivityExecution and waits for the outcome with PollActivityExecution — mirroring the include-standalone-nexus pattern (#339) and bench-go's standalone-activity throughputstress step (bench-go #357/#358).

  • kitchen_sink.proto: new ClientAction variant DoStandaloneActivity (field 7).
  • Go client-action executor: Start + Poll implementation (30s start-to-close, 3 attempts), targeting the existing noop activity on the workflow's task queue. Runs for both the loadgen runner and the Go worker (shared executor).
  • Java/Python/Ruby/TS/.NET workers raise UnsupportedOperation, exactly like standalone Nexus.
  • Bindings regenerated via go run ./cmd/dev build-proto (Go, Java, Python, .NET).

Why

bench-go grew an EnableStandaloneActivity toggle (used by saas-cicd chasm e2e pipelines, with server dynamic config activity.enableStandalone) after the bench→omes parity scan closed. omes had no standalone-activity coverage at all — this closes that gap so the saas-cicd e2e cutover (RE-264) isn't blocked on it. RE-353, part of epic RE-257.

Notes for review

  • Ruby generated proto (workers/ruby/.../kitchen_sink_pb.rb) is not covered by dev build-proto — if it's generated by a separate step, it needs a follow-up regen (the Ruby executor stub is in place).
  • Server must support standalone activities; on unsupported servers the action fails — bench-go added a "short-circuit to success" for that case (bench-go bdd2879), which I deliberately did not replicate: the option is opt-in, so failing loudly on unsupported servers seems preferable. Open to changing.
  • Validated: go build, go test ./scenarios/ ./loadgen/kitchensink/ pass. Not yet exercised against a server with activity.enableStandalone — needs a verification run before un-drafting.

@lilydoar

Copy link
Copy Markdown
Contributor Author

Verification run (local, server 1.31.0 via temporal server start-dev):

go run ./cmd/omes run-scenario-with-worker --scenario throughput_stress --language go \
  --run-id verify353 --iterations 2 --option include-standalone-activity=true \
  --option internal-iterations=2 --server-address localhost:7233 --namespace default

Scenario completed cleanly, and temporal activity list confirms the standalone path was exercised end-to-end — 4 standalone activities (2 iterations × 2 internal iterations), all Completed, type noop, IDs matching the executor's scheme:

Completed  standalone-activity-w-verify353-7176d07159bb5746-1-a9dbbf7e-...  noop
Completed  standalone-activity-w-verify353-7176d07159bb5746-2-273892df-...  noop
...

One caveat: start-dev accepts standalone-activity APIs whether or not --dynamic-config-value activity.enableStandalone=true is passed (a control run without the flag also succeeded), so this validates the omes side but not the server-side gating — cloud/e2e environments still need activity.enableStandalone set, as the README section notes.

Also pushed: regenerated Ruby kitchen-sink bindings (workers/ruby/protos/.../kitchen_sink_pb.rb) — build-proto doesn't cover Ruby, so this was done with protoc --ruby_out plus the same temporalio/api/... require rewrite the file already used.

@lilydoar lilydoar marked this pull request as ready for review June 11, 2026 16:18
@lilydoar lilydoar requested review from a team as code owners June 11, 2026 16:18
@lilydoar lilydoar force-pushed the lilydoar/re-353-standalone-activity branch from ecb0d9d to 9c55cd1 Compare June 11, 2026 16:19

@GregoryTravis GregoryTravis left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Approving for Java and Ruby

@lilydoar

Copy link
Copy Markdown
Contributor Author

Reworked this PR to fold in the advantages of #351 (thanks @prathyushpv) while keeping this PR's include-standalone-activity flag and README conventions. After comparing both PRs, #351's approach is strictly better on several axes, so I adopted it here rather than maintaining two competing implementations.

What changed from the original version of this PR:

  • proto: DoStandaloneActivity now carries ExecuteActivityAction activity = 1 instead of a bare string activity_type. This reuses the existing DSL message, so the activity variant, task queue, timeouts, and retry policy are all configurable rather than hardcoded.
  • Go executor: switched from the raw StartActivityExecution/PollActivityExecution RPCs to the high-level SDK client.ExecuteActivity + StartActivityOptions + handle.Get. Idiomatic, ~15 fewer lines, and drops the manual Namespace threading.
  • Dedup: extracted ActivityNameAndArgs + ConvertFromPBRetryPolicy into shared helpers and pointed the Go worker's launchActivity at them.
  • Load shape: throughput_stress now runs a payload activity (256B in/out) instead of noop, matching bench-go.
  • Cross-language coverage: implemented the standalone-activity client action for Python, TypeScript, .NET, and Java workers (each via its SDK's high-level standalone-activity client API + a shared ActivityDispatch helper). Ruby remains stubbed. Previously this PR only supported the Go worker.
  • Java SDK bumped to 1.35.0 (required for ActivityClient/StartActivityOptions).

Verified builds: Go (build + test), Python (mypy), TypeScript (tsc --noEmit), .NET (dotnet build), Java (gradlew build incl. Spotless).

Still outstanding before un-drafting: not yet exercised against a live server with activity.enableStandalone — that verification run is the remaining step.

@lilydoar lilydoar force-pushed the lilydoar/re-353-standalone-activity branch from 9099991 to 1be8737 Compare June 16, 2026 22:07

@THardy98 THardy98 left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Couple small things but Ruby support is blocking

throw new ApplicationFailureException(
"DoStandaloneNexusOperation is not supported", "UnsupportedOperation", nonRetryable: true);
}
else if (action.DoStandaloneActivity != null)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

do we also need to check if this is explicitly false

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

DoStandaloneActivity is a message-typed oneof member, so the generated property is the message or null — != null is the "is this variant set" check (there's no false/default case to guard as there would be for a bool), and it matches every other branch in this method.

Basically, this isn't a bool, it is either null, or a valid value

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Just as an aside - it's a bit odd that this file is located in loadgen/kitchensink, theClientActionsExecutor is worker logic, it doesn't generate any load.

(not really related to your PR, but just noting it)

Comment on lines +54 to +58
when :do_standalone_activity
raise Temporalio::Error::ApplicationError.new(
'DoStandaloneActivity is not supported',
non_retryable: true
)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

standalone activities is supported in ruby SDK version 1.5.0 (see temporalio/sdk-ruby#443)

in any case, if there's a feature that's not supported by a particular SDK, we should check err-if-unimplemented here and log instead of throwing if set

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Added the checks for err-if-unimplemented

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

any reason why we didn't add Ruby support ?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

The reason it was stubbed: the Ruby worker's Gemfile.lock/gemspec were pinned to temporalio 1.3.0, which predates the standalone-activity API. But that was actually a latent inconsistency from #404 — that PR bumped versions.env to RUBY_SDK_VERSION=1.5.0 but left the lock/gemspec at 1.3.0.

Adding it now

lilydoar added 5 commits June 18, 2026 13:59
Adds an include-standalone-activity scenario option that starts activities
outside any workflow context via StartActivityExecution and waits for the
outcome with PollActivityExecution, mirroring the include-standalone-nexus
pattern (#339) and bench-go's standalone-activity throughputstress step
(bench-go #357/#358).

- kitchen_sink.proto: new ClientAction variant DoStandaloneActivity.
- Go client-action executor: Start + Poll implementation; targets the
  existing noop activity on the workflow's task queue.
- Other SDK workers raise UnsupportedOperation, like standalone Nexus.
- Bindings regenerated via 'go run ./cmd/dev build-proto' (Go, Java,
  Python, .NET; Ruby pb not covered by build-proto - see PR notes).

Requires server-side activity.enableStandalone.
protoc --ruby_out with the same require rewrite as the existing file
(temporal/api/*_pb -> temporalio/api/*); build-proto does not cover Ruby.
The previous regen used a local protoc 7.x, producing gencode for
protobuf runtimes newer than the repo pins (Java RuntimeVersion/
GeneratedFile, .NET end-group handling, protoc version headers) - which
broke compilation against the pinned runtimes and failed the
protos-up-to-date check. Rebuilt via 'dev install protoc' +
'dev build-proto' exactly as CI does. Ruby is intentionally untouched:
CI does not regenerate it and its committed style matches main.
Integrates the advantages of #351 into this PR while keeping the
include-standalone-activity flag and README conventions:

- proto: DoStandaloneActivity now carries ExecuteActivityAction (was a bare
  activity_type string), so the activity variant, task queue, timeouts, and
  retry policy are all configurable. Regenerated Go/Java/Python/.NET/Ruby
  bindings (TS protos regenerate at build).
- Go loadgen executor uses the high-level SDK client.ExecuteActivity +
  StartActivityOptions instead of raw StartActivityExecution/PollActivityExecution
  RPCs; drops manual Namespace threading.
- Extracted ActivityNameAndArgs + ConvertFromPBRetryPolicy into shared helpers
  and deduped the Go worker's launchActivity to reuse them.
- throughput_stress emits a payload(256B/256B) standalone activity with
  configurable timeouts/retry instead of noop.
- Implemented the standalone-activity client action for Python, TypeScript,
  .NET, and Java workers (each via its high-level SDK API + a shared
  ActivityDispatch helper). Ruby remains stubbed.
- Bumped Java SDK to 1.35.0 (required for ActivityClient/StartActivityOptions).

Builds verified: Go (build+test), Python (mypy), TS (tsc), .NET (dotnet build),
Java (gradle build + Spotless).
include-standalone-nexus and include-standalone-activity are parsed as
free-form --option flags, so they don't appear in cobra --help. The
scenario Description string is the only place omes list-scenarios can
surface them, so add both.
@lilydoar lilydoar force-pushed the lilydoar/re-353-standalone-activity branch from 11ffb2d to 1bfc3f8 Compare June 18, 2026 21:01
lilydoar added 6 commits June 18, 2026 14:20
The standalone-nexus stubs (all languages) and Ruby's standalone-activity
stub raised unconditionally, ignoring the err-on-unimplemented flag. Make
them follow the same pattern as concurrent client actions: raise only when
err-on-unimplemented is set, otherwise log and skip.
The Go executor errors when DoStandaloneActivity.activity is unset; the
other workers either NPE'd or silently ran a noop activity. Add the same
guard to the .NET, Java, Python, and TypeScript workers so a missing
activity fails loudly and consistently across languages.
The standalone-nexus stubs now respect err-on-unimplemented, so they skip
silently by default instead of throwing. The kitchensink test that asserts
a worker rejects an unsupported feature relied on the unconditional throw,
so thread --err-on-unimplemented through the test harness and have
testUnsupportedFeature start the worker in strict mode. This preserves the
rejection assertion while keeping the gated skip-by-default behavior.
The Ruby SDK gained standalone-activity client APIs in 1.5.0
(temporalio/sdk-ruby#443). Bump the Ruby SDK from 1.3.0 to 1.5.0
(versions.env already targeted 1.5.0; the Gemfile.lock and gemspec
lagged) and implement DoStandaloneActivity via client.execute_activity,
mirroring the other workers. Extract a shared ActivityDispatch helper so
the workflow-scheduled and standalone-activity paths stay in sync.
Exercises DoStandaloneActivity end-to-end across all SDKs (the prior
suite only covered standalone Nexus). The client activity starts a
standalone activity on the run's task queue and waits for it; the worker
picks it up and completes it. Enables activity.enableStandalone dynamic
config and fills the activity's task queue per run.
After extracting the activity mapping into ActivityDispatch, launch_activity
is small enough that the Metrics cops no longer fire, so the
rubocop:disable/enable Metrics directives are flagged as redundant by
check-worker. Remove them.

@THardy98 THardy98 left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Would prefer if we could add

@lilydoar lilydoar merged commit 574fbf1 into main Jun 22, 2026
45 checks passed
@lilydoar lilydoar deleted the lilydoar/re-353-standalone-activity branch June 22, 2026 21:06
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants