Skip to content

feat(workflow): add nondeterministic random API to UnsafeWorkflowInfo#2133

Closed
brucearctor wants to merge 1 commit into
temporalio:mainfrom
brucearctor:feat/unsafe-random-api
Closed

feat(workflow): add nondeterministic random API to UnsafeWorkflowInfo#2133
brucearctor wants to merge 1 commit into
temporalio:mainfrom
brucearctor:feat/unsafe-random-api

Conversation

@brucearctor

Copy link
Copy Markdown

Summary

Adds workflowInfo().unsafe.random() — a nondeterministic random number generator exposed on UnsafeWorkflowInfo, for use in query handlers, update validators, interceptors, and sinks where deterministic replay is not required.

Primary use case: generating unique trace/correlation IDs for observability and telemetry in query handlers (as requested in #2110).

Motivation

The workflow sandbox replaces Math.random() with a deterministic PRNG seeded from the workflow seed. This is essential for replay correctness, but makes it impossible for query handlers and interceptors to generate truly unique identifiers for observability purposes. SideEffect() cannot be used in query handlers since they cannot produce commands.

Note: This is the first Temporal SDK to expose unsafe nondeterministic random. Go/Java SDKs rely on SideEffect() for nondeterminism, but SideEffect cannot be used in query handlers. This API fills a TypeScript-specific gap caused by the sandbox replacing Math.random.

Implementation

Follows the exact same pattern as the existing unsafe.now():

  1. Capture the real Math.random at module load time in worker-interface.ts (before overrideGlobals() replaces it with the deterministic PRNG)
  2. Inject it as unsafe.random during initRuntime()
  3. Delete/restore around thread serialization boundaries (same as unsafe.now)

Changes

Core (3 files)

  • packages/workflow/src/interfaces.ts — Added random: () => number to UnsafeWorkflowInfo with comprehensive JSDoc (@experimental)
  • packages/workflow/src/worker-interface.ts — Captured OriginalMathRandom before sandbox overrides
  • packages/worker/src/worker.ts — Added random: Math.random to initial unsafe object

Thread serialization (2 files)

  • packages/worker/src/workflow/threaded-vm.ts — Delete before thread send; restore after sink call deserialization
  • packages/worker/src/workflow/workflow-worker-thread.ts — Delete before sink call serialization

Tests (6 files)

  • New test workflow (unsafe-random.ts) with both unsafeRandom and deterministicRandom query handlers
  • Test verifies: valid range, nondeterminism across queries, independence from seeded PRNG
  • Updated all existing test helpers constructing UnsafeWorkflowInfo

API

import { workflowInfo } from '@temporalio/workflow';

// In a query handler:
const traceId = workflowInfo().unsafe.random().toString(36).slice(2);

Safe to use in: query handlers, update validators, interceptors, sinks.
NOT safe in: main workflow function, signal handlers, update handlers (breaks replay).

Testing

  • All 73 tests in test-workflows.js pass
  • New unsafeRandom test validates nondeterminism and source independence

Closes #2110

Adds unsafe.random() to workflowInfo().unsafe, providing a nondeterministic
source of randomness for use in query handlers and interceptors where
deterministic replay is not required (e.g. generating unique trace IDs
for observability and telemetry).

The implementation captures the real Math.random before the workflow sandbox
overrides it, following the same pattern used for unsafe.now (which captures
OriginalDate.now).

Note: This is the first Temporal SDK to expose unsafe nondeterministic random.
Go/Java SDKs rely on SideEffect() for nondeterminism, but SideEffect cannot
be used in query handlers (which can't produce commands). This API fills
a TypeScript-specific gap caused by the workflow sandbox replacing
Math.random with a deterministic PRNG.

Closes temporalio#2110
@brucearctor brucearctor requested a review from a team as a code owner June 21, 2026 03:48
@chris-olszewski

Copy link
Copy Markdown
Member

Apologies that the original issue #2110 didn't got into clear detail about the desired interface, but we need the unsafe random interface to mirror the new named one e.g. being able to generate UUIDs/fill buffers.

#2141 was also opened by a team member and will be the PR we go with for resolving this issue. We appreciate your contributions and sorry for the mixup.

@brucearctor

brucearctor commented Jun 25, 2026

Copy link
Copy Markdown
Author

Thanks for the message. Community management is hard, what is in public, and putting enough detail in tickets/etc -- esp. balancing open contributions and those on payroll [ latter more easy to jump on a call with ].

I do hope we can improve process. Ex: so I can ensure to not spending time on PRs that are undesired. Second time this week [ other was in sdk-java ].

Def happy to understand what i can be doing differently [ ideally in a way that doesn't add much more friction to contributing, as a volunteer ].

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.

[Feature Request] Nondeterministic random generation API for use in query plugins

2 participants