CONCEPT Cited by 1 source
numHistoryShards immutability¶
numHistoryShards is the shard count of Temporal's History subsystem — the value that determines how many partitions Temporal will hash workflows across. It is immutable after the initial cluster deployment. "The configured value assigned in this step directly impacts the system's throughput, latency, and resource utilization. Getting this configuration right for your setup and performance targets is essential for production, and the value is immutable after the initial cluster deployment. You must set this value high enough to scale with this Cluster's worst-case peak load" (Source: sources/2026-04-21-planetscale-temporal-workflows-at-scale-sharding-in-production).
The one-way door¶
Most sharded-storage systems treat shard count as a runtime-mutable dimension: Vitess Reshard can grow 1→4→8→16 shards online; Dynamo-family systems split ranges transparently; TAO re-partitions on the fly. Temporal's numHistoryShards does not have this property. The shard-id hash is baked into Temporal's code path — every workflow that exists in the cluster has a workflow_id → shard_id mapping computed at creation time. Changing numHistoryShards would re-hash every existing workflow to a different shard, invalidating all in-flight history replay.
The upshot: numHistoryShards is a worst-case-sizing decision you make once, at cluster creation time. Get it wrong low and you hit the single-shard throughput ceiling with no way to escape except migrating workflows to a new cluster.
Why the value can't just be changed¶
Temporal's serialise-per-shard discipline requires that the History subsystem hold a single-writer lock per shard. If numHistoryShards changed from N to M mid-cluster, existing workflows' shard_id = hash(workflow_id) mod N addresses would no longer match the M-shard topology — and the shard-ownership locks would be meaningless. Fixing this requires re-hashing every workflow, which means reading every live event history, re-writing it under the new shard, and updating the in-memory state held by every History process. None of this can happen online without stopping all workflows first.
Sizing guidance¶
Longoria's rule: "You must set this value high enough to scale with this Cluster's worst-case peak load." In practice this means:
- Measure (or estimate) peak persistence-layer latency under worst-case load.
- Compute the single-shard ceiling:
1 / latency. - Divide the worst-case target QPS by the single-shard ceiling to get minimum shard count.
- Multiply by a safety factor (3×–5× is common in public guidance — see the Shilkov 2021 sizing post referenced in Part 1) to allow headroom for:
- Traffic growth between deploy and reshard-forced migration
- Skew in
hash(workflow_id)distribution - Cold-start spikes when the cluster restarts
- Deploy.
Distinction from storage-layer shard count¶
numHistoryShards (Temporal) and Vitess shard count (storage) are independent knobs:
| Knob | Mutable? | What it partitions |
|---|---|---|
numHistoryShards |
No (cluster-lifetime immutable) | Temporal workflows across History processes |
| Vitess shard count | Yes (online via Reshard) |
MySQL rows across MySQL primaries |
A Temporal cluster running on a sharded Vitess backing store has both — and changing the Vitess shard count is safe (resharding is online, doesn't touch Temporal's hash function); changing numHistoryShards is not. Operators should treat the two as separate capacity-planning exercises: Vitess shard count sized for data volume + IOPS cost; numHistoryShards sized for peak operation rate.
Contrast with other sharded systems¶
- Vitess
numShards: mutable online viaReshard+ VReplication +SwitchTraffic. No workflow state is embedded in the shard mapping. - Cassandra virtual nodes (vnodes): mutable via
nodetoolrepair/rebalance. - Elasticsearch
number_of_shards: immutable per index, but indices can be split (_splitAPI) or re-indexed into a new index with a different shard count. - Temporal
numHistoryShards: immutable per cluster; requires new cluster + workflow migration to change. Closest peer is Kafka topicnum.partitions(immutable; increasing partitions doesn't re-hash existing messages) — but Kafka partitions less-strictly constrain correctness than Temporal shards.
Seen in¶
- sources/2026-04-21-planetscale-temporal-workflows-at-scale-sharding-in-production — Longoria's Part 2 canonicalises the immutability + worst-case-sizing rule verbatim. The rule is the operational bridge between Temporal's correctness discipline (concepts/serialized-per-shard-updates) and the operator's capacity plan: because you can't reshard your way out, you must oversize on day zero.