High-performance .NET event streaming service. Ingest messages via HTTP with sub-millisecond latency, persist to SQLite with WAL mode, and relay to configured HTTP endpoints with retry logic. 50K+ messages/second throughput.
Noundry Streams is a high-performance event streaming service that ingests, persists, and relays messages. It uses System.Threading.Channels for lock-free queuing and Tuxedo ORM for data access. The service targets .NET 8.0, 9.0, and 10.0 (multi-target) and uses ASP.NET Core Minimal APIs.
HTTP message ingestion with sub-millisecond latency. Content-agnostic payload handling.
Crash-safe SQLite with WAL mode. Batch writing with configurable intervals.
HTTP endpoint forwarding with configurable retry logic, backoff, and dead letter queue.
src/
NoundryStreams.Core/ # Core library (no ASP.NET dependency)
Configuration/ # NoundryStreamsOptions
Database/ # Repositories (Tuxedo ORM)
Models/ # StreamConfig, RelayConfig, Messages
Services/ # StreamManager, BatchWriter, RelayWorker
Telemetry/ # OpenTelemetry integration
NoundryStreams.Service/ # ASP.NET Core host
Endpoints/ # IngestEndpoints, AdminEndpoints
Middleware/ # ApiKeyAuth, ExceptionHandling
public sealed class StreamConfig
{
public required string StreamId { get; init; } // Unique identifier
public string? DisplayName { get; init; }
public bool OrderingRequired { get; init; } // Sequential processing
public int RetentionHours { get; init; } = 168; // 7 days default
public string? OtelEndpoint { get; init; } // OpenTelemetry OTLP/gRPC
public int ChannelCapacity { get; init; } = 5000; // Bounded channel (backpressure)
public int BatchIntervalMs { get; init; } = 50; // Max wait before batch write
public int BatchMaxSize { get; init; } = 500; // Max messages per transaction
public int RelayWorkerCount { get; init; } = 4; // Parallel workers
public bool Enabled { get; init; } = true;
}
public sealed class RelayConfig
{
public required string StreamId { get; init; }
public required string EndpointUrl { get; init; } // HTTP/HTTPS destination
public string HttpMethod { get; init; } = "POST";
public string? Headers { get; init; } // JSON: {"Authorization": "Bearer x"}
public int TimeoutMs { get; init; } = 5000;
public int RetryAttempts { get; init; } = 3;
public int RetryBackoffMs { get; init; } = 1000; // Base: attempt * backoff
}
public sealed record IncomingMessage
{
public required string StreamId { get; init; }
public required string ContentType { get; init; } // MIME type from HTTP header
public required byte[] Payload { get; init; } // Raw bytes (content-agnostic)
public required long ReceivedAt { get; init; } // Unix timestamp ms
public bool OrderingRequired { get; init; } // Per-message ordering override
}
/streams/{streamId}/ingest
Primary message ingestion endpoint. Accepts raw bytes with any content type.
# Example ingestion request
$ curl -X POST http://localhost:5000/streams/webhook-events/ingest \
-H "Content-Type: application/json" \
-H "X-API-Key: your-api-key" \
-d '{"event": "user.signup", "userId": 12345}'
/ingest
Alternative endpoint with header/query-based stream selection. Priority: X-Stream-Id header > stream query parameter.
| Method | Endpoint | Description |
|---|---|---|
| POST | /admin/streams | Create a new stream |
| GET | /admin/streams | List all active streams |
| GET | /admin/streams/{streamId} | Get stream details |
| POST | /admin/streams/{streamId}/relays | Add relay configuration |
| GET | /admin/streams/{streamId}/metrics | Get real-time stream metrics |
| PUT | /admin/streams/{streamId}/enable | Enable a stream |
| PUT | /admin/streams/{streamId}/disable | Disable a stream |
| POST | /admin/streams/{streamId}/cleanup | Trigger immediate cleanup of expired messages |
| GET | /admin/streams/{streamId}/stats | Get stream statistics |
| GET | /metrics | Prometheus-compatible metrics for all streams |
$ curl -X POST http://localhost:5000/admin/streams \
-H "Content-Type: application/json" \
-d '{
"streamId": "webhook-events",
"displayName": "Webhook Events",
"retentionHours": 168,
"channelCapacity": 10000,
"batchMaxSize": 500,
"relayWorkerCount": 4
}'
# Response: 201 Created
$ curl -X POST http://localhost:5000/admin/streams/webhook-events/relays \
-H "Content-Type: application/json" \
-d '{
"endpointUrl": "https://api.example.com/webhooks",
"httpMethod": "POST",
"headers": {"Authorization": "Bearer token123"},
"timeoutMs": 5000,
"retryAttempts": 3,
"retryBackoffMs": 1000
}'
# Response: 201 Created {"configId": 1}
// appsettings.json
{
"NoundryStreams": {
"DataPath": "./data/noundry-streams.db",
"ApiKey": null,
"Defaults": {
"ChannelCapacity": 10000,
"BatchIntervalMs": 50,
"BatchMaxSize": 500,
"RetentionHours": 168,
"RelayWorkerCount": 4
},
"Sqlite": {
"CacheSizeMB": 256,
"MmapSizeMB": 1024,
"WalAutocheckpoint": 1000
}
},
"Kestrel": {
"Limits": {
"MaxConcurrentConnections": 100000,
"MaxRequestBodySize": 10485760
}
}
}
Orchestrates stream lifecycle. Creates bounded channels, starts BatchWriter and RelayWorker instances per stream. Thread-safe via ConcurrentDictionary.
Reads from channel, batches messages, writes to SQLite in transactions. Collects until BatchMaxSize OR BatchIntervalMs timeout.
Picks up persisted messages and forwards to configured HTTP endpoints. Supports parallel or sequential relay with retry and dead letter queue.
Background service that runs hourly to delete expired processed messages based on stream retention policy.
Thread-safe monotonic per-stream sequence number generator. Supports atomic batch reservation for high throughput.
OpenTelemetry integration for distributed tracing via OTLP/gRPC. Tracks batch writes and relay operations with stream.id tags.
/streams/{id}/ingest -- message written to bounded Channel
Powered by System.Threading.Channels for lock-free queuing, SQLite WAL mode for concurrent reads/writes, and configurable batch sizes for throughput optimization.
-- Applied at database initialization
PRAGMA journal_mode = WAL; -- Write-ahead logging
PRAGMA synchronous = NORMAL; -- Balanced durability/performance
PRAGMA cache_size = -262144; -- 256MB cache
PRAGMA temp_store = MEMORY; -- In-memory temp tables
PRAGMA mmap_size = 1073741824; -- 1GB memory-mapped I/O
PRAGMA page_size = 8192; -- Larger pages for throughput
PRAGMA wal_autocheckpoint = 1000; -- Checkpoint every 1000 pages
PRAGMA busy_timeout = 5000; -- 5s wait on lock contention
public sealed class StreamMetrics
{
public long TotalMessagesReceived { get; set; } // Lifetime ingested
public long TotalMessagesRelayed { get; set; } // Lifetime successful relays
public long TotalMessagesFailed { get; set; } // Lifetime failed relays
public double AvgIngestLatencyMs { get; set; }
public double AvgRelayLatencyMs { get; set; }
public double ChannelUtilization { get; set; } // 0.0 - 1.0
public long DiskUsageBytes { get; set; }
}
Prometheus-compatible metrics are also available at the GET /metrics endpoint for integration with Grafana and other monitoring tools.
GET /healthBasic health check returning status, timestamp, and version.
GET /health/readyReadiness probe. Returns 200 if database is accessible, 503 otherwise.