Skip to content

Performance: reduce per-call SHA1 allocation for deterministic orchestration GUIDs #778

Description

@berndverst

1. What is the issue?

Each call to the deterministic orchestration NewGuid() implementation creates and disposes a new SHA1 HashAlgorithm instance.

2. Likely priority

Low. It affects orchestrations that use context.NewGuid() frequently, including some entity correlation paths, but other per-call allocations are likely larger.

3. Impact on performance

Repeated hash-algorithm construction adds allocation and disposal overhead during orchestration replay. The cost scales with the number of deterministic GUIDs generated per execution.

4. Details and code reference

Deterministic GUID implementation:

public override Guid NewGuid()
{
static void SwapByteArrayValues(byte[] byteArray)
{
SwapByteArrayElements(byteArray, 0, 3);
SwapByteArrayElements(byteArray, 1, 2);
SwapByteArrayElements(byteArray, 4, 5);
SwapByteArrayElements(byteArray, 6, 7);
}
static void SwapByteArrayElements(byte[] byteArray, int left, int right)
{
(byteArray[right], byteArray[left]) = (byteArray[left], byteArray[right]);
}
const string DnsNamespaceValue = "9e952958-5e33-4daf-827f-2fa12937b875";
const int DeterministicGuidVersion = 5;
Guid namespaceValueGuid = Guid.Parse(DnsNamespaceValue);
// The name is a combination of the instance ID, the current orchestrator date/time, and a counter.
string guidNameValue = string.Concat(
this.InstanceId,
"_",
this.CurrentUtcDateTime.ToString("o"),
"_",
this.newGuidCounter.ToString(CultureInfo.InvariantCulture));
this.newGuidCounter++;
byte[] nameByteArray = Encoding.UTF8.GetBytes(guidNameValue);
byte[] namespaceValueByteArray = namespaceValueGuid.ToByteArray();
SwapByteArrayValues(namespaceValueByteArray);
byte[] hashByteArray;
#pragma warning disable CA5350 // Do Not Use Weak Cryptographic Algorithms -- not for cryptography
using (HashAlgorithm hashAlgorithm = SHA1.Create()) /* CodeQL [SM02196] Suppressed: SHA1 is not used for cryptographic purposes here. The information being hashed is not sensitive,
and the goal is to generate a deterministic Guid. We cannot update to SHA2-based algorithms without breaking
customers' inflight orchestrations. */
{
hashAlgorithm.TransformBlock(namespaceValueByteArray, 0, namespaceValueByteArray.Length, null, 0);
hashAlgorithm.TransformFinalBlock(nameByteArray, 0, nameByteArray.Length);
hashByteArray = hashAlgorithm.Hash;
}
#pragma warning restore CA5350 // Do Not Use Weak Cryptographic Algorithms -- not for cryptography
byte[] newGuidByteArray = new byte[16];
Array.Copy(hashByteArray, 0, newGuidByteArray, 0, 16);
int versionValue = DeterministicGuidVersion;
newGuidByteArray[6] = (byte)((newGuidByteArray[6] & 0x0F) | (versionValue << 4));
newGuidByteArray[8] = (byte)((newGuidByteArray[8] & 0x3F) | 0x80);
SwapByteArrayValues(newGuidByteArray);
return new Guid(newGuidByteArray);

SHA1 creation is here:

byte[] hashByteArray;
#pragma warning disable CA5350 // Do Not Use Weak Cryptographic Algorithms -- not for cryptography
using (HashAlgorithm hashAlgorithm = SHA1.Create()) /* CodeQL [SM02196] Suppressed: SHA1 is not used for cryptographic purposes here. The information being hashed is not sensitive,
and the goal is to generate a deterministic Guid. We cannot update to SHA2-based algorithms without breaking
customers' inflight orchestrations. */
{
hashAlgorithm.TransformBlock(namespaceValueByteArray, 0, namespaceValueByteArray.Length, null, 0);
hashAlgorithm.TransformFinalBlock(nameByteArray, 0, nameByteArray.Length);
hashByteArray = hashAlgorithm.Hash;
}

5. Guidance for how to fix

Treat this as replay-compatibility-sensitive. Any reuse or replacement must produce byte-for-byte identical GUIDs for existing instance ID, timestamp, and counter inputs across supported target frameworks. Prefer an isolated optimization with deterministic-value regression tests before and after the change; do not change the namespace, hash input, byte ordering, SHA1 algorithm, or version-bit handling.

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions