.claude/skills/analytics-concurrent-collections/SKILL.md
Use when collecting real-time telemetry or analytics with concurrent access, tracking in-process events without external infrastructure, or building lightweight admin dashboards showing usage statistics. Implements thread-safe counters and bounded event streams using ConcurrentDictionary and ConcurrentQueue to prevent memory growth under concurrent writes. Domain: Telemetry, In-Process Analytics. Level: Intermediate. Tags: analytics, concurrent-collections, telemetry, in-memory, event-tracking.
npx skillsauth add klod68/littlerae analytics-concurrent-collectionsInstall this skill globally with one command. Works with Claude Code, Cursor, and Windsurf.
3 of 9 scanners reported clean
Some scanners were skipped, did not run, or reported a non-clean status. Review each row below.
Your application needs lightweight, in-process analytics — tracking events, counting occurrences, computing aggregates — without external infrastructure (Application Insights, database, message queue). The tracking must be thread-safe because multiple requests write concurrently.
Use ConcurrentQueue<T> for ordered event streams and ConcurrentDictionary<TKey, TValue> for aggregated statistics. Bound the queue to prevent unbounded memory growth.
public sealed record TrackingEvent
{
public required string Action { get; init; }
public required int ResultCount { get; init; }
public string? Category { get; init; }
public DateTime Timestamp { get; init; } = DateTime.UtcNow;
}
public sealed class ActionStats
{
public required string Action { get; set; }
public int Count { get; set; }
public double AverageResultCount { get; set; }
public DateTime LastOccurrence { get; set; }
}
using System.Collections.Concurrent;
/// <summary>
/// Thread-safe, in-memory analytics service with bounded event storage.
/// Suitable for demo/dev/small-scale production use.
/// </summary>
public sealed class AnalyticsService
{
private readonly ConcurrentQueue<TrackingEvent> _events = new();
private readonly ConcurrentDictionary<string, ActionStats> _stats = new(StringComparer.OrdinalIgnoreCase);
private const int MaxEventsToKeep = 1000;
/// <summary>
/// Records an event. Thread-safe, lock-free.
/// </summary>
public void Track(string action, int resultCount, string? category = null)
{
if (string.IsNullOrWhiteSpace(action))
return;
var normalized = action.Trim().ToLowerInvariant();
// Append to event stream
_events.Enqueue(new TrackingEvent
{
Action = normalized,
ResultCount = resultCount,
Category = category
});
TrimEventsIfNeeded();
// Update aggregate stats (atomic, lock-free)
_stats.AddOrUpdate(
normalized,
_ => new ActionStats
{
Action = normalized,
Count = 1,
AverageResultCount = resultCount,
LastOccurrence = DateTime.UtcNow
},
(_, existing) =>
{
existing.Count++;
existing.AverageResultCount =
((existing.AverageResultCount * (existing.Count - 1)) + resultCount) / existing.Count;
existing.LastOccurrence = DateTime.UtcNow;
return existing;
});
}
/// <summary>
/// Gets the top N most frequent actions.
/// </summary>
public IEnumerable<ActionStats> GetTopActions(int count = 10)
=> _stats.Values
.OrderByDescending(s => s.Count)
.Take(count);
/// <summary>
/// Gets recent events in reverse chronological order.
/// </summary>
public IEnumerable<TrackingEvent> GetRecentEvents(int count = 50)
=> _events.Reverse().Take(count);
/// <summary>
/// Gets actions that produced zero results (potential content gaps).
/// </summary>
public IEnumerable<ActionStats> GetZeroResultActions()
=> _stats.Values
.Where(s => s.AverageResultCount == 0)
.OrderByDescending(s => s.Count);
/// <summary>
/// Gets total event count.
/// </summary>
public int TotalEventCount => _events.Count;
/// <summary>
/// Gets total distinct actions tracked.
/// </summary>
public int DistinctActionCount => _stats.Count;
private void TrimEventsIfNeeded()
{
while (_events.Count > MaxEventsToKeep)
{
_events.TryDequeue(out _);
}
}
}
// In Program.cs or Startup
builder.Services.AddSingleton<AnalyticsService>();
public class SearchHandler
{
private readonly AnalyticsService _analytics;
public SearchHandler(AnalyticsService analytics)
{
_analytics = analytics;
}
public IEnumerable<Result> Search(string query)
{
var results = _searchEngine.Execute(query);
_analytics.Track(query, results.Count());
return results;
}
}
// Minimal API endpoint
app.MapGet("/api/analytics", (AnalyticsService analytics) => new
{
TotalEvents = analytics.TotalEventCount,
DistinctActions = analytics.DistinctActionCount,
TopActions = analytics.GetTopActions(10),
ZeroResultActions = analytics.GetZeroResultActions().Take(5)
});
| Collection | Purpose | Thread Safety | Performance |
|-----------|---------|--------------|-------------|
| ConcurrentQueue<T> | Ordered event stream (FIFO) | Lock-free enqueue/dequeue | O(1) enqueue |
| ConcurrentDictionary<K,V> | Aggregated stats by key | Lock-free read, fine-grained lock on write | O(1) lookup |
| List<T> + lock | ❌ Alternative | Manual locking | Contention under load |
MaxEventsToKeep = 1000
│
├── Events 1-1000: kept in queue
├── Event 1001 arrives → Event 1 dequeued (FIFO trim)
│
└── Stats dictionary: never trimmed (one entry per distinct action)
└── For high-cardinality actions, add periodic cleanup:
RemoveStale(olderThan: TimeSpan.FromDays(30))
AddOrUpdate lambda is not atomic: The update lambda may execute multiple times under contention. Keep it idempotent.Singleton, not Scoped or Transient, or each request gets its own empty instance.tools
Use when cross-cutting concerns (logging, metrics, validation, authorization) are tangled into command handlers or service methods, when building database command pipelines with reorderable concerns, or when HTTP client pipelines or message handlers need composable, independently-replaceable processing stages. Covers ICommandInterceptor interface, InterceptorPipeline with reverse-chain construction, zero-cost Empty sentinel to skip overhead when no interceptors are registered, and ConfigureAwait(false) discipline for library code. Domain: Architecture, Cross-Cutting Concerns. Level: Intermediate. Tags: interceptor, pipeline, middleware, decorator, cross-cutting-concerns.
development
Use when writing integration tests for Razor Pages, MVC, or Minimal API applications to validate routing, middleware, page rendering, and HTTP behavior without a browser or live server, or when adding fast smoke tests to a CI pipeline. Covers WebApplicationFactory<Program> setup with public partial class Program, in-memory test server, AngleSharp HTML parsing, CSS selector assertions, redirect and status code testing, and a shared static fixture pattern for minimal per-test startup overhead. Domain: Testing, ASP.NET Core. Level: Intermediate. Tags: integration-testing, webapplicationfactory, razor-pages, anglesharp, http-testing.
development
Use when designing indexes for new tables, diagnosing slow queries that are not using indexes efficiently, reviewing index fragmentation and maintenance, or when the current indexing strategy results in key lookups, table scans, or missing index warnings. Covers clustered index key selection (narrow, unique, ever-increasing), non-clustered index design for query patterns, covering indexes with INCLUDE columns, filtered indexes for subset queries, composite index column ordering, DMV-based monitoring for missing and unused indexes, and rebuild vs reorganize maintenance thresholds. Domain: Database, Performance. Level: Intermediate. Tags: index, sql-server, covering-index, filtered-index, performance, dmv, maintenance.
development
Use when building a searchable in-memory catalog or registry for documentation sites, admin panels, or type/API browsers where you need keyword matching, fuzzy search, and ranked results without an external search engine or database. Covers RegistryService with weighted scoring across name, description, keywords, and method names; Levenshtein fuzzy matching; synonym expansion; category and subcategory filtering; and singleton DI registration for datasets of hundreds to low thousands of items. Domain: Search, Data Access Patterns. Level: Intermediate. Tags: search, registry, fuzzy-matching, in-memory, catalog, filtering.