Skip to content

Commit 0a693bc

Browse files
committed
Document UUIDv7 statistics
Refs #70.
1 parent 37a2e41 commit 0a693bc

5 files changed

Lines changed: 91 additions & 2 deletions

File tree

‎CHANGELOG.md‎

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,10 +7,14 @@ and this project aims to follow [Semantic Versioning](https://semver.org/spec/v2
77

88
## [Unreleased]
99

10+
### Added
11+
- Add opt-in `UuidV7FactoryStatistics` counters for generated UUIDs, clock rollback, counter overflow, spin-wait, logical drift, CAS retries, and random-buffer refills.
12+
1013
### Changed
1114
- `HlcGuidFactory` constructor now enforces a 14-bit node ID constraint and throws `ArgumentOutOfRangeException` for values above `HlcGuidFactory.MaxNodeId` (16383). Previously, higher values were silently truncated in generated UUIDv7 values.
1215

1316
### Documentation
17+
- Document UUIDv7 factory statistics and counter semantics.
1418
- Clarify `UuidV7Factory` collision and clock-skew guarantees, including the distinction between per-instance deterministic monotonicity and probabilistic cross-factory uniqueness.
1519
- Document the custom RNG contract for `UuidV7Factory`, including deterministic replay behavior and production CSPRNG guidance.
1620

‎README.md‎

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ It is built around `TimeProvider` so that *time becomes an injectable dependency
2121
- `UuidV7Factory` produces RFC 9562 UUIDv7 values as `Guid`
2222
- Works with real or simulated time
2323
- Configurable counter overflow behavior
24+
- Optional statistics for rollback, overflow, spin-wait, contention, and random-buffer refill diagnostics
2425
- Per-instance monotonicity under clock rollback; cross-factory uniqueness remains probabilistic unless coordinated externally
2526

2627
- **Hybrid Logical Clock (HLC)**
@@ -35,7 +36,7 @@ It is built around `TimeProvider` so that *time becomes an injectable dependency
3536
- Canonical binary wire format (`VectorClock.WriteTo`/`ReadFrom`) and string form for HTTP/gRPC headers
3637

3738
- **Lightweight instrumentation**
38-
- Counters for timers, advances, and timeouts useful in simulation/test assertions
39+
- Counters for timers, advances, timeouts, and UUIDv7 factory behavior useful in simulation/test assertions
3940

4041
## Installation
4142

‎docs/changelog.md‎

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,10 +10,14 @@ This page mirrors the repository root `CHANGELOG.md`.
1010

1111
## [Unreleased]
1212

13+
### Added
14+
- Add opt-in `UuidV7FactoryStatistics` counters for generated UUIDs, clock rollback, counter overflow, spin-wait, logical drift, CAS retries, and random-buffer refills.
15+
1316
### Changed
1417
- `HlcGuidFactory` constructor now enforces a 14-bit node ID constraint and throws `ArgumentOutOfRangeException` for values above `HlcGuidFactory.MaxNodeId` (16383). Previously, higher values were silently truncated in generated UUIDv7 values.
1518

1619
### Documentation
20+
- Document UUIDv7 factory statistics and counter semantics.
1721
- Clarify `UuidV7Factory` collision and clock-skew guarantees, including the distinction between per-instance deterministic monotonicity and probabilistic cross-factory uniqueness.
1822
- Document the custom RNG contract for `UuidV7Factory`, including deterministic replay behavior and production CSPRNG guidance.
1923

‎docs/guide/instrumentation.md‎

Lines changed: 39 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,45 @@ You can reset counters between test phases:
3333
tp.Statistics.Reset();
3434
```
3535

36+
## UUIDv7 factory statistics
37+
38+
`UuidV7Factory` can update opt-in `UuidV7FactoryStatistics` counters:
39+
40+
```csharp
41+
var stats = new UuidV7FactoryStatistics();
42+
using var factory = new UuidV7Factory(
43+
TimeProvider.System,
44+
rng: null,
45+
overflowBehavior: CounterOverflowBehavior.SpinWait,
46+
statistics: stats);
47+
48+
factory.NewGuid();
49+
50+
var snapshot = stats.Snapshot();
51+
Console.WriteLine(snapshot.GeneratedCount);
52+
Console.WriteLine(snapshot.ClockRollbackCount);
53+
Console.WriteLine(snapshot.CasRetryCount);
54+
```
55+
56+
Statistics are disabled unless you pass an instance to the factory. When enabled, counters use atomic operations so they can be read safely while UUIDs are being generated concurrently.
57+
58+
Useful counters include:
59+
60+
- `GeneratedCount`
61+
- `ClockRollbackCount`
62+
- `CounterOverflowCount`
63+
- `SpinWaitCount`
64+
- `LogicalTimestampAdvanceCount`
65+
- `MaxLogicalDriftMs`
66+
- `CasRetryCount`
67+
- `RandomBufferRefillCount`
68+
69+
Use `Snapshot()` when you need a point-in-time value object, and `Reset()` to isolate test phases:
70+
71+
```csharp
72+
stats.Reset();
73+
```
74+
3675
## Timeout statistics
3776

3877
Timeout factory helpers (`Timeouts`) can record aggregate timeout activity via `TimeoutStatistics`.
@@ -58,4 +97,3 @@ If you prefer isolation per test (or per component), pass your own `TimeoutStati
5897
var stats = new TimeoutStatistics();
5998
using var handle = Timeouts.CreateTimeoutHandle(tp, TimeSpan.FromSeconds(1), stats);
6099
```
61-

‎docs/guide/uuidv7.md‎

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -68,6 +68,46 @@ For production services, prefer a single `UuidV7Factory` singleton per process o
6868

6969
For high-assurance shared namespaces, use a storage uniqueness constraint as the final guardrail and retry on conflict. If you need node-aware ordering semantics, consider `HlcGuidFactory`; it embeds a node ID and HLC timestamp, but it should be chosen for causal/node-aware ordering rather than treated as a blanket substitute for storage-level uniqueness.
7070

71+
## Statistics
72+
73+
`UuidV7Factory` statistics are opt-in. Leave them disabled for the lowest-overhead path, or pass a `UuidV7FactoryStatistics` instance when you want to observe clock rollback, counter overflow, spin-wait pressure, and lock-free contention:
74+
75+
```csharp
76+
var stats = new UuidV7FactoryStatistics();
77+
using var factory = new UuidV7Factory(
78+
TimeProvider.System,
79+
rng: null,
80+
overflowBehavior: CounterOverflowBehavior.SpinWait,
81+
statistics: stats);
82+
83+
var id = factory.NewGuid();
84+
var snapshot = stats.Snapshot();
85+
86+
Console.WriteLine(snapshot.GeneratedCount);
87+
Console.WriteLine(snapshot.CounterOverflowCount);
88+
```
89+
90+
The built-in DI helpers can register the same shared statistics object as the singleton factory:
91+
92+
```csharp
93+
services.AddLockFreeGuidFactory(new UuidV7FactoryStatistics());
94+
```
95+
96+
Key counters:
97+
98+
| Counter | Meaning |
99+
|---|---|
100+
| `GeneratedCount` | Number of UUIDs successfully generated. Batch generation increments by the span length. |
101+
| `ClockRollbackCount` | Successful allocation decisions made while physical time was behind the factory's logical frontier. |
102+
| `CounterOverflowCount` | Times the 12-bit per-millisecond counter overflow path was reached. |
103+
| `SpinWaitCount` | Times overflow handling had to wait for physical time to advance. |
104+
| `LogicalTimestampAdvanceCount` | Successful allocation decisions that emitted logical time ahead of physical time. |
105+
| `MaxLogicalDriftMs` | Maximum observed distance between emitted logical time and physical time. |
106+
| `CasRetryCount` | Failed compare-exchange attempts in the lock-free allocation loop. |
107+
| `RandomBufferRefillCount` | Thread-local random buffer refills. |
108+
109+
Most counters are diagnostic event counts, not rates. For example, a single `NewGuids(span)` call may reserve many UUIDs with one successful allocation decision, so `GeneratedCount` increases by `span.Length` while rollback or drift counters increase once for that reservation.
110+
71111
## Custom RNGs and Deterministic Tests
72112

73113
Custom RNG injection exists so tests and simulations can replay exact UUID sequences. Clockworks does not ship a deterministic RNG implementation; provide your own test-only `RandomNumberGenerator` when you need replay.
@@ -162,3 +202,5 @@ dotnet run --project demo/Clockworks.Demo -- uuidv7-sortability
162202
# Benchmark mode
163203
dotnet run --project demo/Clockworks.Demo -- uuidv7 --bench
164204
```
205+
206+
Benchmark mode includes a side-by-side single-threaded comparison of the default hot path and statistics-enabled hot path.

0 commit comments

Comments
 (0)