ManagedCode.TimeSeries 10.0.0

Prefix Reserved
dotnet add package ManagedCode.TimeSeries --version 10.0.0
                    
NuGet\Install-Package ManagedCode.TimeSeries -Version 10.0.0
                    
This command is intended to be used within the Package Manager Console in Visual Studio, as it uses the NuGet module's version of Install-Package.
<PackageReference Include="ManagedCode.TimeSeries" Version="10.0.0" />
                    
For projects that support PackageReference, copy this XML node into the project file to reference the package.
<PackageVersion Include="ManagedCode.TimeSeries" Version="10.0.0" />
                    
Directory.Packages.props
<PackageReference Include="ManagedCode.TimeSeries" />
                    
Project file
For projects that support Central Package Management (CPM), copy this XML node into the solution Directory.Packages.props file to version the package.
paket add ManagedCode.TimeSeries --version 10.0.0
                    
#r "nuget: ManagedCode.TimeSeries, 10.0.0"
                    
#r directive can be used in F# Interactive and Polyglot Notebooks. Copy this into the interactive tool or source code of the script to reference the package.
#:package ManagedCode.TimeSeries@10.0.0
                    
#:package directive can be used in C# file-based apps starting in .NET 10 preview 4. Copy this into a .cs file before any lines of code to reference the package.
#addin nuget:?package=ManagedCode.TimeSeries&version=10.0.0
                    
Install as a Cake Addin
#tool nuget:?package=ManagedCode.TimeSeries&version=10.0.0
                    
Install as a Cake Tool

ManagedCode TimeSeries

ManagedCode.TimeSeries

.NET Coverage Status Release CodeQL

Lock-free, allocation-conscious, thread-safe time-series primitives for building fast counters, rolling analytics, and metric pipelines in .NET 10 / C# 14.

Package NuGet
Core library NuGet Package

What is this library?

ManagedCode.TimeSeries is a high-performance .NET time series metrics library. It provides optimized, time-bucketed collections to aggregate events into time series:

  • Accumulators store raw events per time bucket (queue per bucket).
  • Summers store aggregated values per time bucket (sum/min/max/replace).
  • Groups manage many series by key (per endpoint, per customer, per shard, etc.).

It's designed for high-throughput analytics: clicks, tokens, CPU usage, money, events, and any numeric telemetry.

Why use it?

  • Lock-free ingestion for high write rates.
  • Thread-safe reads/writes for multi-threaded pipelines.
  • UTC-normalized timestamps for deterministic ordering.
  • Grouped series for multi-tenant metrics.
  • Orleans-friendly converters and System.Text.Json helpers.

Common use cases

  • Web analytics (page views, clicks, conversion funnels).
  • Infrastructure metrics (CPU, memory, latency, tokens).
  • Billing counters (money, usage, events per window).

Concepts (quick mental model)

  • SampleInterval: bucket size (e.g., 1s, 10s, 1m).
  • Samples/Buckets: ordered view of buckets keyed by UTC timestamps (Buckets is an alias of Samples).
  • MaxSamplesCount: maximum bucket count per series; 0 means unbounded (this is not the event count).
  • DataCount: total events processed (not the number of buckets).
  • UTC normalized: you can pass any DateTimeOffset, but values are normalized to UTC internally; offsets are not stored.
  • Thread-safe: concurrent reads/writes are supported across all public types.

Install

dotnet add package ManagedCode.TimeSeries

Quickstart

1) Make a rolling accumulator (store raw events)

using ManagedCode.TimeSeries.Accumulators;

var requests = new IntTimeSeriesAccumulator(
    sampleInterval: TimeSpan.FromSeconds(5),
    maxSamplesCount: 60); // 60 buckets -> 5 minutes at 5s interval

// Use current time (UTC internally)
requests.Record(1);
requests.Record(1);

Console.WriteLine($"Buckets: {requests.Samples.Count}");
Console.WriteLine($"Events: {requests.DataCount}");

2) Make a summer (store aggregates per bucket)

using ManagedCode.TimeSeries.Summers;
using ManagedCode.TimeSeries.Extensions;

var latency = new NumberTimeSeriesSummer<decimal>(TimeSpan.FromMilliseconds(500));

latency.Record(12.4m);
latency.Record(9.6m);

Console.WriteLine($"Sum: {latency.Sum()}");
Console.WriteLine($"Avg: {latency.Average():F2}");
Console.WriteLine($"Min/Max: {latency.Min()} / {latency.Max()}");

3) Track many keys at once (grouped series)

using ManagedCode.TimeSeries.Accumulators;
using ManagedCode.TimeSeries.Extensions;

var perEndpoint = new IntGroupTimeSeriesAccumulator(
    sampleInterval: TimeSpan.FromSeconds(1),
    maxSamplesCount: 300,
    deleteOverdueSamples: true);

perEndpoint.AddNewData("/home", 1);
perEndpoint.AddNewData("/checkout", 1);

foreach (var (endpoint, accumulator) in perEndpoint.Snapshot())
{
    Console.WriteLine($"{endpoint}: {accumulator.DataCount} hits");
}

4) Serialize/deserialize with System.Text.Json

using ManagedCode.TimeSeries.Accumulators;
using ManagedCode.TimeSeries.Serialization;

var series = new IntTimeSeriesAccumulator(TimeSpan.FromSeconds(1), maxSamplesCount: 10);
series.AddNewData(1);
series.AddNewData(2);

var json = TimeSeriesJsonSerializer.SerializeAccumulator<int, IntTimeSeriesAccumulator>(series);
var restored = TimeSeriesJsonSerializer.DeserializeAccumulator<int, IntTimeSeriesAccumulator>(json);

Console.WriteLine(restored.DataCount); // same as original

5) Orleans serialization (v9)

Converters in ManagedCode.TimeSeries.Orleans are marked with [RegisterConverter] and are auto-registered when the assembly is loaded. Reference the package from your silo and client projects so the converters are available. Converters are provided for int/float/double accumulators, summers, and grouped series, plus generic numeric summers.

Time handling (DateTimeOffset vs DateTime)

  • Public APIs accept DateTimeOffset so callers can pass any offset they have.
  • Internally, timestamps are normalized to UTC (offset zero) for speed and determinism.
  • If you need to preserve offsets, store them separately alongside your metric data.
  • If you do not need custom timestamps, use Record(value) or AddNewData(value) and let the library use DateTime.UtcNow.

Accumulators vs Summers

Type Stored per bucket Best for
Accumulator ConcurrentQueue<T> raw event lists, replay, exact values
Summer T (aggregated) fast stats, memory-efficient counters

Performance tips

  • Keep MaxSamplesCount reasonable to limit memory.
  • Use Record(value) (or AddNewData(value)) unless you must pass custom timestamps.
  • Use summers for high-volume metrics (they avoid storing every event).
  • Avoid locks in code that touches these structures.

Design notes

  • Accumulators use ConcurrentQueue<T> to remain lock-free and thread-safe under concurrent writes.
  • Even in single-threaded runtimes, timers and background cleanup can introduce concurrency.
  • If you need single-thread-only storage, consider using summers or ask for a dedicated single-thread accumulator.

Architecture

  • See docs/Architecture/Overview.md for detailed workflows, data model, and module boundaries.

Development Workflow

dotnet restore ManagedCode.TimeSeries.slnx
dotnet build ManagedCode.TimeSeries.slnx --configuration Release
dotnet format ManagedCode.TimeSeries.slnx
dotnet build ManagedCode.TimeSeries.slnx --configuration Release
dotnet test ManagedCode.TimeSeries.Tests/ManagedCode.TimeSeries.Tests.csproj --configuration Release

Extensibility

Scenario Hook
Custom numeric type Implement INumber<T> and use NumberTimeSeriesSummer<T>
Different aggregation Use Strategy or implement a custom summer
Serialization System.Text.Json helpers in ManagedCode.TimeSeries.Serialization

Contributing

  1. Restore/build/test using the commands above.
  2. Keep new APIs covered with tests (see existing samples in ManagedCode.TimeSeries.Tests).
  3. Keep hot paths lock-free; only introduce locking when unavoidable and document the trade-off.
  4. Update this README if you change behavior or public APIs.

License

MIT (c) ManagedCode SAS.

Product Compatible and additional computed target framework versions.
.NET net10.0 is compatible.  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. 
Compatible target framework(s)
Included target framework(s) (in package)
Learn more about Target Frameworks and .NET Standard.
  • net10.0

    • No dependencies.

NuGet packages (3)

Showing the top 3 NuGet packages that depend on ManagedCode.TimeSeries:

Package Downloads
ManagedCode.Keda.Orleans.Scaler.Client

Keda

ManagedCode.Keda.Orleans.Scaler

Keda

ManagedCode.TimeSeries.Orleans

TimeSeries

GitHub repositories

This package is not used by any popular GitHub repositories.

Version Downloads Last Updated
10.0.0 64 1/11/2026
0.0.20 260 10/19/2025
0.0.18 819 5/17/2023
0.0.17 12,322 12/9/2022
0.0.16 413 12/9/2022
0.0.15 443 12/9/2022
0.0.12 1,996 10/11/2022
0.0.11 542 10/11/2022
0.0.10 829 10/10/2022
0.0.9 1,302 10/10/2022
0.0.8 768 10/10/2022
0.0.7 776 10/10/2022
0.0.6 1,335 10/10/2022
0.0.5 845 10/10/2022
0.0.4 1,869 10/3/2022
0.0.3 2,888 9/23/2022
0.0.2 2,663 9/19/2022
0.0.1 608 9/19/2022