ManagedCode.TimeSeries 0.0.20

Prefix Reserved
dotnet add package ManagedCode.TimeSeries --version 0.0.20
                    
NuGet\Install-Package ManagedCode.TimeSeries -Version 0.0.20
                    
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="0.0.20" />
                    
For projects that support PackageReference, copy this XML node into the project file to reference the package.
<PackageVersion Include="ManagedCode.TimeSeries" Version="0.0.20" />
                    
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 0.0.20
                    
#r "nuget: ManagedCode.TimeSeries, 0.0.20"
                    
#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@0.0.20
                    
#: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=0.0.20
                    
Install as a Cake Addin
#tool nuget:?package=ManagedCode.TimeSeries&version=0.0.20
                    
Install as a Cake Tool

ManagedCode TimeSeries

ManagedCode.TimeSeries

.NET Coverage Status Release CodeQL

Lock-free, allocation-conscious time-series primitives written in modern C# for building fast counters, aggregations, and rolling analytics.

Package NuGet
Core library NuGet Package

Why TimeSeries?

  • Data pipelines demand concurrency. We built the core on lock-free ConcurrentDictionary/ConcurrentQueue structures and custom atomic helpers, so write-heavy workloads scale across cores without blocking.
  • Numeric algorithms shouldn’t duplicate code. Everything is generic over INumber<T>, so int, decimal, or your own numeric type can use the same summer/accumulator implementations.
  • Hundreds of signals, one API. Grouped accumulators and summers make it trivial to manage keyed windows (think “per customer”, “per endpoint”, “per shard”) with automatic clean-up.
  • Production-ready plumbing. Orleans converters, Release automation, Coveralls reporting, and central package management are all wired up out of the box.

Table of Contents

  1. Feature Highlights
  2. Quickstart
  3. Architecture Notes
  4. Development Workflow
  5. Release Automation
  6. Extensibility
  7. Contributing
  8. License

Feature Highlights

  • Lock-free core – writes hit ConcurrentDictionary + ConcurrentQueue, range metadata updated via custom atomics.
  • Generic summersNumberTimeSeriesSummer<T> & friends operate on any INumber<T> implementation.
  • Mass fan-in ready – grouped accumulators/summers handle hundreds of keys without lock.
  • Orleans-native – converters bridge to Orleans surrogates so grains can persist accumulators out of the box.
  • Delivery pipeline – GitHub Actions release workflow bundles builds, tests, packs, tagging, and publishing.
  • Central package versions – single source of NuGet truth via Directory.Packages.props.

Quickstart

Install

dotnet add package ManagedCode.TimeSeries

Create a rolling accumulator

using ManagedCode.TimeSeries.Accumulators;

var requests = new IntTimeSeriesAccumulator(TimeSpan.FromSeconds(5), maxSamplesCount: 60);

Parallel.For(0, 10_000, i =>
{
    requests.AddNewData(i);
});

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

Summaries with any numeric type

using ManagedCode.TimeSeries.Summers;

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

latency.AddNewData(DateTimeOffset.UtcNow, 12.4m);
latency.AddNewData(DateTimeOffset.UtcNow.AddMilliseconds(250), 9.6m);

Console.WriteLine($"AVG: {latency.Average():F2} ms");
Console.WriteLine($"P50/P100: {latency.Min()} / {latency.Max()}");

Track many signals at once

using ManagedCode.TimeSeries.Accumulators;

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

Parallel.ForEach(requests, req =>
{
    perEndpoint.AddNewData(req.Path, req.Timestamp, 1);
});

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

Orleans-friendly serialization

// In your Orleans silo:
builder.Services.AddSerializer(builder =>
{
    builder.AddConverter<IntTimeSeriesAccumulatorConverter<int>>();
    builder.AddConverter<IntTimeSeriesSummerConverter<int>>();
    // …add others as needed
});

Architecture Notes

  • Lock-free core: BaseTimeSeries stores samples in a ConcurrentDictionary and updates range metadata through AtomicDateTimeOffset. Per-key data is a ConcurrentQueue<T> (accumulators) or direct INumber<T> (summers).
  • Deterministic reads: consumers get an ordered read-only projection of the concurrent map, so existing iteration/test semantics stay intact while writers remain lock-free.
  • Group managers: BaseGroupTimeSeriesAccumulator and BaseGroupNumberTimeSeriesSummer use ConcurrentDictionary<string, ...> plus lightweight background timers for overdue clean-up—no lock statements anywhere on the hot path.
  • Orleans bridge: converters project between the concurrent structures and Orleans’ plain dictionaries/queues, keeping serialized payloads simple while the live types stay lock-free.

Extensibility

Scenario Hook
Custom numeric type Implement INumber<T> and plug into NumberTimeSeriesSummer<T>
Alternative aggregation strategy Extend Strategy enum & override Update in a derived summer
Domain-specific accumulator Derive from TimeSeriesAccumulator<T, TSelf> (future rename of BaseTimeSeriesAccumulator) and expose tailored helpers
Serialization Add dedicated Orleans converters / System.Text.Json converters using the pattern in ManagedCode.TimeSeries.Orleans

Heads up: the Base* prefixes hang around for historical reasons. We plan to rename the concrete-ready generics to TimeSeriesAccumulator<T,...> / TimeSeriesSummer<T,...> in a future release with deprecation shims.

Development Workflow

  • Solution: ManagedCode.TimeSeries.slnx
    dotnet restore ManagedCode.TimeSeries.slnx
    dotnet build ManagedCode.TimeSeries.slnx --configuration Release
    dotnet test ManagedCode.TimeSeries.Tests/ManagedCode.TimeSeries.Tests.csproj --configuration Release
    
  • Packages: update versions only in Directory.Packages.props.
  • Coverage: dotnet test ... -p:CollectCoverage=true -p:CoverletOutputFormat=lcov.
  • Benchmarks: dotnet run --project ManagedCode.TimeSeries.Benchmark --configuration Release.

Release Automation

  • Workflow: .github/workflows/release.yml
    • Trigger: push to main or manual workflow_dispatch.
    • Steps: restore → build → test → pack → dotnet nuget push (skip duplicates) → create/tag release.
    • Configure secrets:
      • NUGET_API_KEY: NuGet publish token.
      • Default ${{ secrets.GITHUB_TOKEN }} is used for tagging and releases.

Contributing

  1. Restore/build/test using the commands above.
  2. Keep new APIs covered with tests (see existing samples in ManagedCode.TimeSeries.Tests).
  3. Align with the lock-free architecture—avoid introducing lock on hot paths.
  4. Document new features in this README.

License

MIT © ManagedCode SAS.

Product Compatible and additional computed target framework versions.
.NET net9.0 is compatible.  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. 
Compatible target framework(s)
Included target framework(s) (in package)
Learn more about Target Frameworks and .NET Standard.
  • net9.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
0.0.20 168 10/19/2025
0.0.18 784 5/17/2023
0.0.17 12,254 12/9/2022
0.0.16 399 12/9/2022
0.0.15 416 12/9/2022
0.0.12 1,974 10/11/2022
0.0.11 510 10/11/2022
0.0.10 805 10/10/2022
0.0.9 1,273 10/10/2022
0.0.8 751 10/10/2022
0.0.7 757 10/10/2022
0.0.6 1,319 10/10/2022
0.0.5 817 10/10/2022
0.0.4 1,839 10/3/2022
0.0.3 2,859 9/23/2022
0.0.2 2,631 9/19/2022
0.0.1 583 9/19/2022