Wiaoj.DistributedCounter.Abstractions 0.0.1-alpha.101-preview

This is a prerelease version of Wiaoj.DistributedCounter.Abstractions.
There is a newer prerelease version of this package available.
See the version list below for details.
dotnet add package Wiaoj.DistributedCounter.Abstractions --version 0.0.1-alpha.101-preview
                    
NuGet\Install-Package Wiaoj.DistributedCounter.Abstractions -Version 0.0.1-alpha.101-preview
                    
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="Wiaoj.DistributedCounter.Abstractions" Version="0.0.1-alpha.101-preview" />
                    
For projects that support PackageReference, copy this XML node into the project file to reference the package.
<PackageVersion Include="Wiaoj.DistributedCounter.Abstractions" Version="0.0.1-alpha.101-preview" />
                    
Directory.Packages.props
<PackageReference Include="Wiaoj.DistributedCounter.Abstractions" />
                    
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 Wiaoj.DistributedCounter.Abstractions --version 0.0.1-alpha.101-preview
                    
#r "nuget: Wiaoj.DistributedCounter.Abstractions, 0.0.1-alpha.101-preview"
                    
#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 Wiaoj.DistributedCounter.Abstractions@0.0.1-alpha.101-preview
                    
#: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=Wiaoj.DistributedCounter.Abstractions&version=0.0.1-alpha.101-preview&prerelease
                    
Install as a Cake Addin
#tool nuget:?package=Wiaoj.DistributedCounter.Abstractions&version=0.0.1-alpha.101-preview&prerelease
                    
Install as a Cake Tool

Wiaoj.DistributedCounter.Abstractions

Core contracts, interfaces, and value primitives for the Wiaoj.DistributedCounter library.

This package defines the public API surface, storage provider interfaces, and domain value objects required to consume or extend the distributed counter system without referencing concrete storage implementations or runtime hosting packages.


Installation

dotnet add package Wiaoj.DistributedCounter.Abstractions

Domain Primitives and Types

CounterKey (readonly record struct)

  • Validated key type ensuring consistent string representation across storage providers.
  • Implements ISpanParsable<CounterKey>, IUtf8SpanParsable<CounterKey>, and ISpanFormattable to support span-based parsing and formatting without unnecessary allocations.
  • Includes a dedicated JsonConverter (CounterKeyJsonConverter) for direct JSON property/value serialization.

CounterValue (readonly record struct)

  • Numerical wrapper around a long value.
  • Implements generic math interfaces (IComparisonOperators, IAdditionOperators, ISubtractionOperators).
  • Enforces checked arithmetic operators (+, -), throwing OverflowException on boundary overflows rather than wrapping silently.
  • Explicit cast to long prevents unintentional type coercion; implicit conversion from long is supported.

CounterExpiry (readonly record struct)

  • Encapsulates expiration policies and sliding TTL windows.
  • Supports CounterExpiry.Infinite (null duration) for persistent counters.
  • Provides factory methods (From, FromSeconds, FromMinutes, FromTicks) with non-positive duration validation.

CounterLimitResult (readonly record struct)

Returned by quota-evaluating operations (TryIncrementAsync, TryDecrementAsync):

  • IsAllowed (bool): Whether the requested amount was within limit boundaries.
  • CurrentValue (long): Current counter value after the operation (or unchanged value if rejected).
  • Remaining (long): Remaining capacity until reaching the limit threshold.
  • Ttl (TimeSpan?): Live remaining time-to-live of the sliding expiration window, if available from storage.

CounterValueCollection (readonly struct)

  • Read-only batch container returned by IDistributedCounterService.GetValuesAsync.
  • Uses a reference-counted DisposeGuard to return rented Dictionary<string, CounterValue> buffers to the underlying object pool upon disposal.
  • Safe after disposal: indexers and TryGetValue return CounterValue.Zero without throwing exceptions.

CounterStrategy (enum)

  • Immediate: Every operation is dispatched synchronously to storage.
  • Buffered: Operations are accumulated in local memory and flushed in batches by background workers.

Core Interfaces

Interface Role
IDistributedCounter Core operations: IncrementAsync, DecrementAsync, TryIncrementAsync, TryDecrementAsync, GetValueAsync, SetAsync, and ResetAsync.
IDistributedCounter<TTag> Open-generic DI contract providing tag-level categorization and scoped key resolution via .ForKey(key).
ICounterStorage Low-level storage provider contract implemented by storage backends (e.g. In-Memory, Redis).
IDistributedCounterFactory Engine factory contract for creating and caching counter instances by name or tag.
IDistributedCounterService System-level batch queries (GetValuesAsync), manual flush triggers (FlushAllAsync), and state resets (ResetAllAsync).
ICounterKeyBuilder Contract for formatting names, tags, and dynamic keys into structured CounterKey strings.

Configuration Models

  • DistributedCounterOptions: Root configuration model holding global prefixes (GlobalKeyPrefix), default strategy (DefaultStrategy), auto-flush intervals (AutoFlushInterval), and tag registrations.
  • CounterConfiguration: Specific settings for a named counter or tag. Allows configuring dedicated strategies, storage types (UseStorage<T>()), keyed storage identifiers (UseKeyedStorage(key)), or factory delegates (UseStorage(factory)).

Usage Examples

Consuming IDistributedCounter<TTag> in a Service

using Wiaoj.DistributedCounter;

public sealed class RateLimiterService(IDistributedCounter<RateLimitTag> rateLimiter) {

    public async Task<bool> CheckRequestAllowedAsync(string clientIp, CancellationToken cancellationToken) {
        CounterLimitResult result = await rateLimiter
            .ForKey(clientIp)
            .TryIncrementAsync(
                amount: 1,
                limit: 10,
                expiry: CounterExpiry.FromMinutes(1),
                cancellationToken: cancellationToken);

        return result.IsAllowed;
    }
}

public sealed class RateLimitTag;

Implementing a Custom Storage Backend

using Wiaoj.DistributedCounter;

public sealed class CustomStorage : ICounterStorage {

    public ValueTask<CounterValue> AtomicIncrementAsync(
        CounterKey key, 
        long amount, 
        CounterExpiry expiry, 
        CancellationToken cancellationToken) {
        // Storage-specific atomic increment implementation
        return new ValueTask<CounterValue>(new CounterValue(amount));
    }

    public ValueTask<CounterLimitResult> TryIncrementAsync(
        CounterKey key, 
        long amount, 
        long limit, 
        CounterExpiry expiry, 
        CancellationToken cancellationToken) {
        // Storage-specific limit evaluation implementation
        return new ValueTask<CounterLimitResult>(new CounterLimitResult(true, amount, limit - amount, null));
    }

    public ValueTask<CounterLimitResult> TryDecrementAsync(CounterKey key, long amount, long minLimit, CounterExpiry expiry, CancellationToken cancellationToken) => throw new NotImplementedException();
    public ValueTask<CounterValue> GetAsync(CounterKey key, CancellationToken cancellationToken) => throw new NotImplementedException();
    public ValueTask<TimeSpan?> GetTtlAsync(CounterKey key, CancellationToken cancellationToken) => throw new NotImplementedException();
    public ValueTask<IDictionary<CounterKey, CounterValue>> GetManyAsync(IEnumerable<CounterKey> keys, CancellationToken cancellationToken) => throw new NotImplementedException();
    public ValueTask GetManyAsync(ReadOnlyMemory<CounterKey> keys, Memory<CounterValue> destination, CancellationToken cancellationToken) => throw new NotImplementedException();
    public ValueTask DeleteAsync(CounterKey key, CancellationToken cancellationToken) => throw new NotImplementedException();
    public ValueTask SetAsync(CounterKey key, CounterValue value, CounterExpiry expiry, CancellationToken cancellationToken) => throw new NotImplementedException();
    public ValueTask BatchIncrementAsync(ReadOnlyMemory<CounterUpdate> updates, Memory<long> resultDestination, CancellationToken cancellationToken) => throw new NotImplementedException();
}

License

This project is licensed under the MIT License.

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.

NuGet packages (4)

Showing the top 4 NuGet packages that depend on Wiaoj.DistributedCounter.Abstractions:

Package Downloads
Wiaoj.DistributedCounter

Package Description

Wiaoj.DistributedCounter.Redis

Package Description

Wiaoj.RateLimiting

Package Description

Wiaoj.Resilience

Package Description

GitHub repositories

This package is not used by any popular GitHub repositories.

Version Downloads Last Updated
0.1.0-alpha.7 0 9/18/2026
0.1.0-alpha.6 43 9/16/2026
0.1.0-alpha.5 43 9/16/2026
0.1.0-alpha.4 47 9/16/2026
0.1.0-alpha.3 49 9/15/2026
0.1.0-alpha.2 78 9/15/2026
0.1.0-alpha.1 59 9/14/2026
0.0.1-alpha.112-preview 71 9/13/2026
0.0.1-alpha.111-preview 63 9/13/2026
0.0.1-alpha.110-preview 74 9/12/2026
0.0.1-alpha.109-preview 76 9/11/2026
0.0.1-alpha.108-preview 89 9/8/2026
0.0.1-alpha.107-preview 98 9/8/2026
0.0.1-alpha.106-preview 86 9/8/2026
0.0.1-alpha.105-preview 84 9/8/2026
0.0.1-alpha.104-preview 89 9/7/2026
0.0.1-alpha.103-preview 84 9/7/2026
0.0.1-alpha.102-preview 87 9/6/2026
0.0.1-alpha.101-preview 113 9/6/2026
0.0.1-alpha.100-preview 86 9/6/2026
Loading failed