How does temporal guarantee deterministic execution for floating operations in typescript? I noticed that this topic is not covered in the docs, and I can’t find anything on the forum.
Wow that’s a hard one. AFAICT, the different factors that may affect determinism are:
- JIT changing the order of operations (may be a real issue but unlikely to be noticeable)
- Running on different architectures (could be an issue for replaying locally)
- Running on different JS engines (realistically this will never happen, even though the SDK can run on bun now)
I think we can’t totally guarantee determinism in those but I doubt it will ever manifest in practice.
Note that the determinism of Workflow code is only important in so far that it affects the commands produced by the Workflow. That is, which commands and in which order; however, on most commands, arguments to these commands are not checked for consistency.
Though it is true that replaying floating-point operations may technically be non-deterministic (e.g. if replayed on a different CPU), it is extremely unlikely in practice that these minute differences would have observable consequences on commands produced subsequently by a Workflow.
AFAICT there could be extremely rare conditions where due to JIT optimizations, non-deterministic floating point operations could affect code execution but I doubt that is likely to ever manifest in practice.
For example, if you workflow does some multi step floating point compilation, and your workflow code uses the result to determine the next step:
const computedResult = multiStepFPMath()
if (computedResult > 0.5) {
await someActivity();
}
The margin of error with rounding is highly unlikely to manifest, but I wouldn’t say we guarantee determinism because V8 doesn’t, even if the same code is run on the same architecture.