Caudal.RateLimiting
0.1.0-preview.2
dotnet add package Caudal.RateLimiting --version 0.1.0-preview.2
NuGet\Install-Package Caudal.RateLimiting -Version 0.1.0-preview.2
<PackageReference Include="Caudal.RateLimiting" Version="0.1.0-preview.2" />
<PackageVersion Include="Caudal.RateLimiting" Version="0.1.0-preview.2" />
<PackageReference Include="Caudal.RateLimiting" />
paket add Caudal.RateLimiting --version 0.1.0-preview.2
#r "nuget: Caudal.RateLimiting, 0.1.0-preview.2"
#:package Caudal.RateLimiting@0.1.0-preview.2
#addin nuget:?package=Caudal.RateLimiting&version=0.1.0-preview.2&prerelease
#tool nuget:?package=Caudal.RateLimiting&version=0.1.0-preview.2&prerelease
<p align="center"> <picture> <source media="(prefers-color-scheme: dark)" srcset="docs/assets/banner-dark.svg"> <img src="docs/assets/banner-light.svg" alt="Caudal — bounded, observable async pipelines for .NET" width="560"> </picture> </p>
<p align="center"> <a href="https://github.com/nicoseijas/Caudal/actions/workflows/ci.yml"><img src="https://github.com/nicoseijas/Caudal/actions/workflows/ci.yml/badge.svg" alt="ci"></a> <a href="LICENSE"><img src="https://img.shields.io/badge/license-MIT-blue.svg" alt="MIT license"></a> </p>
Caudal (Spanish): the volume of water flowing through a channel per unit of time.
Caudal is a .NET library for building bounded, cancellable, observable async pipelines. It is aimed at the code most services end up writing by hand — a SemaphoreSlim to limit concurrency, Task.WhenAll to fan out, a Channel<T> to connect stages — and at the four problems that code keeps getting wrong:
- Bounded concurrency — never more in-flight work than you asked for.
- Real backpressure — a fast producer blocks instead of filling memory.
- Correct cancellation and shutdown — no orphaned tasks, no swallowed exceptions.
- Operational diagnostics — you can explain a slow pipeline without adding logs to your own code.
Status: pre-release. The full 0.1–0.3 API surface from the roadmap is implemented and tested (151 tests, all packages build warning-clean) and preview packages are on NuGet. The contracts in
docs/SEMANTICS.mdstay open to change until1.0— preview releases break deliberately when a contract needs fixing. The build order and exit criteria are inROADMAP.md.
What it looks like
await source
.ToFlow(capacity: 128) // bounded buffer: producer waits when full
.SelectAsync(
ProcessAsync,
concurrency: 8) // at most 8 concurrent invocations, always explicit
.ForEachAsync(
SaveAsync,
cancellationToken); // one token stops producer, workers, and sink
Pipelines are built on IAsyncEnumerable<T> at the edges and Channel<T> internally. Channel<T> is an implementation detail and is not part of the public API.
The operator that best shows why Caudal exists is LatestByKey, for real-time feeds where stale items should be replaced rather than queued:
priceUpdates
.ToFlow(capacity: 1_024)
.LatestByKey(x => x.Symbol, maximumKeys: 1_000) // at most one pending item per key, bounded to 1,000 keys; newer replaces older
.SelectAsync(CalculateIndicatorsAsync, concurrency: 8)
.Batch(maximumSize: 100, maximumDelay: TimeSpan.FromMilliseconds(50))
.ForEachAsync(UpdateDashboardAsync, ct);
Design principles
The full contract is in docs/SEMANTICS.md. The short version:
- No buffer is unbounded by default.
- Every operation accepts a
CancellationToken. - Concurrency is always explicit — there is no default parallelism.
- Ordering is never preserved by accident; you opt in with
PreserveOrder. - An exception cannot disappear silently. Error handling is a per-stage policy:
Stop,Skip, orCapture(failures becomeFlowResult<T>values). - Completing a pipeline means every internal task has finished or been cancelled.
- Telemetry never changes semantics.
- Each operator documents its behavior under saturation.
Time-based operators (Debounce, Throttle, Sample, IdleTimeout) depend on TimeProvider, so they are testable with a fake clock and no real delays. Resilience is an integration with Microsoft.Extensions.Resilience, not a reimplementation of Polly.
When not to use Caudal
Caudal is deliberately small: the goal is around ten operators with precise semantics, not a partial Rx. It is the wrong tool when:
- the work is small, sequential, CPU-bound loops — a plain loop wins;
- the collection is small and already materialized —
Task.WhenAllis fine; - you need concurrency but no backpressure, batching, or per-key semantics —
Parallel.ForEachAsyncis enough; - you need a full reactive event system — use Rx.
The benchmark suite under benchmarks/Caudal.Benchmarks measures the cost of the abstraction against all of these; see docs/when-not-to-use.md for the full reasoning and docs/benchmarks.md for how to run the suite and read its results honestly.
Packages
| Package | Contents |
|---|---|
Caudal.Core |
Sources, operators, sinks, error model |
Caudal.Diagnostics |
OpenTelemetry metrics, FlowSnapshot, pipeline visualization |
Caudal.Resilience |
Polly v8 / Microsoft.Extensions.Resilience integration |
Caudal.RateLimiting |
RateLimit / RateLimitBy over System.Threading.RateLimiting |
Caudal.Testing |
Controlled sources, AsyncGate, virtual time, pipeline assertions |
Documentation
docs/SEMANTICS.md— the behavioral contract: backpressure, errors, cancellation, completion, ordering.ROADMAP.md— phases, exit criteria, and what ships in each version.
License
| Product | Versions Compatible and additional computed target framework versions. |
|---|---|
| .NET | net8.0 is compatible. net8.0-android was computed. net8.0-browser was computed. net8.0-ios was computed. net8.0-maccatalyst was computed. net8.0-macos was computed. net8.0-tvos was computed. net8.0-windows was computed. net9.0 was computed. net9.0-android was computed. net9.0-browser was computed. net9.0-ios was computed. net9.0-maccatalyst was computed. net9.0-macos was computed. net9.0-tvos was computed. net9.0-windows was computed. net10.0 was computed. net10.0-android was computed. net10.0-browser was computed. net10.0-ios was computed. net10.0-maccatalyst was computed. net10.0-macos was computed. net10.0-tvos was computed. net10.0-windows was computed. |
-
net8.0
- Caudal.Core (>= 0.1.0-preview.2)
- System.Threading.RateLimiting (>= 8.0.0)
NuGet packages
This package is not used by any NuGet packages.
GitHub repositories
This package is not used by any popular GitHub repositories.
| Version | Downloads | Last Updated |
|---|---|---|
| 0.1.0-preview.2 | 69 | 7/31/2026 |