Reusable .NET building blocks for cloud solutions — a collection of small, focused, high-performance libraries you can pull in à la carte.
Targets: .NET 8, 9 and 10 • License: MIT • Each component ships as its own NuGet package, so you only take what you need.
dotnet add package CloudMesh.Core
dotnet add package CloudMesh.DataBlocks
dotnet add package CloudMesh.Mediator
dotnet add package CloudMesh.Variant
# ...etc| Package | What it gives you |
|---|---|
| CloudMesh.Core | Common utilities and allocation-conscious helpers (locks, throttling, fast parsing, buffered IO). |
| CloudMesh.Timestamp | Monotonic timestamps (Timestamp, HighResolutionTimestamp) plus FastClock, a lock-free, wall-clock-tracking clock. |
| CloudMesh.DataBlocks | A lightweight, in-process actor/pipeline library built on System.Threading.Channels. |
| CloudMesh.Mediator | A fast, allocation-conscious mediator for in-process CQRS (requests, streams, notifications) with a source generator for box-free, reflection-free dispatch and MediatR-compatible shims. |
| CloudMesh.Variant | A boxing-free discriminated union (Value) for storing arbitrary value types without heap allocation. |
| CloudMesh.Uuid | A very fast UUID v7 (RFC 9562) generator. |
| CloudMesh.Guid64 | A roughly time-sortable 64-bit id (Snowflake-style) for database primary keys, with a compact 13-char Crockford Base32 string form. |
| CloudMesh.Base32 | Fast, zero-allocation Crockford Base32 — encode/decode 32- and 64-bit integers and byte buffers (spans and ReadOnlySequence). |
| CloudMesh.DotNotation | Read/write nested object values by string path ("address.city"), compiled and cached. |
| CloudMesh.MurmurHash | 32-, 64- and 128-bit MurmurHash implementations with no heavy dependencies. |
| CloudMesh.Temporal | Effective-dated ("temporal") records with historical and pending future values. |
| CloudMesh.NetworkMutex.* | Cluster-wide exclusive locks borrowed from a database engine (Postgres / DynamoDB). |
| CloudMesh.Persistence.DynamoDB | A fluent repository pattern for DynamoDB, with an in-memory implementation for tests. |
| CloudMesh.SystemClock |
TimeProvider instead. |
Common utilities and optimization helpers.
| Class | Description |
|---|---|
Throttler |
Easy implementation of throttling within your code. |
AsyncLazy<T> |
Same as the standard Lazy<T> but with async initialization. |
AsyncLock |
An async/awaitable lock — like a semaphore, but for use in async code. |
AsyncReaderWriterLock |
An async/awaitable reader-writer lock. |
FastDecimalParser |
Very fast decimal parsing of strings — much faster than the built-in decimal.Parse(). |
BufferedStreamLineReader |
Statically-allocated, buffered line reader for reading one line of text at a time from a stream with little to no GC — designed for streaming millions of rows. |
MemoryHelper |
Helpers for growing/aligning pooled Memory<T> buffers. |
ConcurrentList, PriorityQueue
|
Small concurrent/utility collections. |
Cheap, allocation-free clocks for intervals, timeouts and timestamps. The two timestamp structs are
monotonic — their elapsed-time math is immune to wall-clock, NTP and DST changes — and project to wall
time through a fixed process-start origin (fast), with ToExact… variants that re-read the system clock when
you need precision. FastClock is the accurate counterpart: it tracks the system clock by re-anchoring
periodically, lock-free.
| Type | Backed by | Use it for |
|---|---|---|
Timestamp |
Environment.TickCount64 |
The cheapest monotonic stamp (ms resolution) — timeouts, retries, cache expiry. |
HighResolutionTimestamp |
Stopwatch.GetTimestamp() |
Higher-resolution monotonic stamp; same uses when you need sub-millisecond intervals. |
FastClock |
Stopwatch + periodic re-anchor |
A fast, always-accurate wall-clock reading — UnixTimeMillisecondsNow() / DateTimeOffsetNow() — re-anchored at most once every 5 s (configurable via AdjustInterval). |
Both structs expose ToUnixTimeMilliseconds() / ToDateTimeOffset() (fast, origin-based) and
ToExactUnixTimeMilliseconds() / ToExactDateTimeOffset() (re-read the clock). HighResolutionTimestamp
backs Guid64 (monotonic ordering); FastClock backs Uuid (accurate v7 timestamps).
Worker and producer/consumer patterns implemented using the highly performant channels from System.Threading.Channels.
It can be thought of as an actor framework that went on a diet to become a light-weight library, with in-process-only patterns.
Allows for easy, high-throughput, in-process processing using fan-out, fan-in, aggregation, buffering, round-robin and similar patterns.
Great for building processing pipelines and background workers.
| Class | Description |
|---|---|
DataBlock |
Generic consumer. Decouples receiving and consuming messages. |
AggregationDataBlock<T> |
Fan-in: consume messages to calculate state over time and periodically emit it. Example: sum, average or frequency every 5 seconds. Great for aggregating metrics. |
BufferBlock<T> |
Fan-in: buffer up to X items, or Y milliseconds, whichever happens first, then consume the batch received within the window. |
BufferRouter<T> |
Fan-in: extends BufferBlock<T> to automatically forward the batch to another DataBlock. |
RoundRobinDataBlock |
Fan-out: round-robin. Distributes load fairly across child actors — for each message, advance to the next child and wrap around. |
SpillOverDataBlock |
Fan-out: saturate-before-advance. Fills each child to capacity before switching to the next. Great for collecting full batches before pushing downstream. |
DataBlockScheduler |
Schedule message delivery to data blocks, with cancellation. Great for timeouts and wait patterns. |
CaptureBlock |
Collect all received messages and list them. Mostly for unit-testing code that uses DataBlocks. |
BackpressureMonitor |
A hook to identify backpressure buildup in pipelines. |
A fast, allocation-conscious mediator for in-process CQRS — send requests, stream responses, and publish notifications, with a pipeline for cross-cutting behaviors. A drop-in-minded alternative to MediatR (which moved to commercial licensing), built around a Roslyn source generator so dispatch is box-free and registration is reflection-free.
public readonly record struct GetUser(int Id) : IRequest<User>;
public sealed class GetUserHandler : IRequestHandler<GetUser, User>
{
public ValueTask<User> HandleAsync(GetUser request, CancellationToken ct) => ...;
}
// Register (scans the assembly); or use the generated, reflection-free AddCloudMeshMediatorGenerated…() for AOT/trim:
services.AddCloudMeshMediator(o => o.RegisterServicesFromAssemblyContaining<GetUser>());
// Send — with the generator referenced this binds a box-free overload automatically:
User user = await mediator.SendAsync(new GetUser(42));| Feature | Notes |
|---|---|
| Requests / streams / notifications |
IRequest<T> (single response), IStreamRequest<T> (IAsyncEnumerable<T>), INotification (fan-out to many handlers). |
| Pipeline behaviors |
IPipelineBehavior<,> / IStreamPipelineBehavior<,> for validation, logging, retries, transactions — including open generics. |
| Source generator | Compile-time handler registration (no reflection, AOT/trim-clean), per-request box-free SendAsync(in request) overloads, and diagnostics (missing/duplicate handler, boxing sends, shim usage). |
| MediatR-compatible shims |
Send/Publish/CreateStream methods and Handle/Task handler shapes, so most MediatR code ports by swapping using directives. |
| Distributed seam | A pluggable transport abstraction for out-of-process notification delivery; network transports (e.g. NATS) plug in separately. |
Everything is ValueTask-based. On the box-free path a send allocates nothing and dispatches in the low-nanosecond
range — well ahead of reflection-based mediators like MediatR on both latency and allocation (which also allocate
hundreds of bytes per send and publish). Compile-time diagnostics default to Info, so they never break a
TreatWarningsAsErrors build; opt into stricter enforcement via .editorconfig — see
docs/diagnostics.md.
→ Full documentation, use cases, quick start, and the MediatR migration guide: src/CloudMesh.Mediator/README.md.
A boxing-free discriminated union, Value, for stashing arbitrary value types in arrays or passing them
as arguments without allocating. Storing a value type as object boxes it (a heap allocation); in
high-throughput code — parsing records, then rearranging and bulk-loading them — that can mean millions of
allocations and heavy GC pressure just to box primitives.
Value avoids boxing wherever possible:
Value[] row = new Value[3];
row[0] = 42; // int — no boxing
row[1] = Guid.NewGuid(); // 16-byte struct, stored inline — no boxing
row[2] = default; // null — no boxing
int n = row[0].As<int>();
Guid? id = row[1].As<Guid?>(); // nullable round-trip — still no boxingIt handles the built-in primitives, DateTime/DateTimeOffset (packed), enums, ArraySegment<byte>/<char>,
and any user struct that fits inline (up to the union size) — including reading a stored value back as its
Nullable<T> form. Larger structs fall back to a single boxed reference.
A very fast UUID v7 (RFC 9562) generator — time-ordered, so it sorts well as a database key. It fills the
random bits from .NET's thread-safe Random.Shared (xoshiro256**) and takes its timestamp from
HighResolutionTimestamp, sidestepping the CoCreateGuid interop and DateTimeOffset.UtcNow call that
make the BCL's generator slower. In the included benchmarks Uuid.Create() runs in ~20 ns — about 2.5×
faster than Guid.CreateVersion7() — with no allocations, and is safe to call concurrently.
Guid id = Uuid.Create(); // v7, time-ordered
Guid at = Uuid.Next(timestamp); // or supply your own long ms / DateTimeOffset / TimeProviderA roughly time-sortable 64-bit guid implementation based on Twitter's Snowflake algorithm.
Great for client-generated primary keys, because database index operations are much faster on a 64-bit
integer than on a 128-bit Guid.
Guid64 id = Guid64.NewGuid(); // generated in-process — no database round-trip
long key = id; // implicitly a long — store it as a bigint
string s = id.ToString(); // 13-char Crockford Base32 (e.g. "3F8K2QH7ZB10A")
Guid64 back = Guid64.Parse(s); // round-trips; also TryParse + IParsable/ISpanParsableBy default ToString() renders the compact 13-character Crockford Base32 form (format B) — far friendlier
than a 128-bit Guid while still sorting by creation time; D gives the plain decimal long and X the
hex. The timestamp comes from HighResolutionTimestamp, so the clock is fast and monotonic. In clustered
deployments, set Guid64.NodeId (0–1023) per node to avoid collisions.
A fast, zero-allocation Crockford Base32 codec (alphabet
0123456789ABCDEFGHJKMNPQRSTVWXYZ — no I, L, O or U). Encoding writes directly into a
caller-supplied buffer, so nothing is allocated.
Span<char> text = stackalloc char[13];
Base32.Format(1_520_779_705_068_019_712L, text); // fixed-width: 13 chars for a long, 7 for an int
Base32.TryDecodeInt64(text, out long value); // ...and back againIt also encodes and decodes arbitrary byte buffers, and decodes from spans or ReadOnlySequence<T>
(the System.IO.Pipelines buffer type), taking the Base32 text as either char or UTF-8/ASCII byte data:
Base32.Format(bytes, chars); // bytes -> Base32 chars
Base32.TryDecode(chars, into, out int written); // ReadOnlySpan<char> -> bytes
Base32.TryDecode(utf8, into, out written); // ReadOnlySpan<byte> -> bytes
Base32.TryDecode(sequence, into, out written); // ReadOnlySequence<char|byte> -> bytesDecoding follows the Crockford spec leniently: case-insensitive, the confusable letters I/L read as 1
and O as 0, and - separators are ignored. Because the integer forms are fixed-width and
most-significant-symbol first, the encoded strings sort in the same order as the underlying numbers.
Read and write values on nested object graphs using string paths, e.g. GetValue(order, "customer.address.city")
and SetValue(order, "customer.address.city", "Stockholm"). Paths are compiled to expressions and cached, and
collections are supported. Handy for mapping, templating and config-driven access.
32-, 64- and 128-bit murmur hash implementations adapted from the Akka.NET project. Used by many of the other libraries here, but without taking a dependency on a massive framework. All credit for the original code goes to the Akka.NET authors.
Normally, mutexes are scoped to a single machine. Network mutexes let you acquire arbitrary, exclusive locks across an entire cluster or network. Behind the scenes they "borrow" the locking mechanisms of a database engine — either row locks or soft locks, depending on the engine's capabilities. If you need cross-machine mutexes and already have a Postgres database, its exclusive lock mechanisms are very capable, and this library simply packages them as a convenient mutex. DynamoDB doesn't support row locking the way Postgres does, but it supports optimistic locking via update conditions, which is what the DynamoDB implementation uses.
An easy-to-use repository pattern for DynamoDB, with a fluent API to select secondary indexes and perform scans or partial updates. Also includes an in-memory implementation that behaves like the real one, for use in your unit-test projects.
A library for working with temporal data — records with a change history of values that have taken effect, as well as pending future changes. Each property of a temporal class can have multiple values, each with an effective date. Given a point in time, the temporal object produces a snapshot of what it would look like. Effective dates can be in the past (historical) or the future (pending). Setting a new future value for a property clears any other pending changes for that property from that date onwards.
Typical scenarios include HR — e.g. recording that a person will change job title, manager and department in three months. Three months from now, unless changed, the new values "take effect".
dotnet build CloudMesh.sln
dotnet testPackage versions are managed centrally (Directory.Packages.props), and all projects multi-target
net8.0;net9.0;net10.0.
MIT © Jessie Wadman. See LICENSE.