Add tests and implement timeline ingestion options with NATS and Redis subscribers
- Introduced `BinaryReachabilityLifterTests` to validate binary lifting functionality. - Created `PackRunWorkerOptions` for configuring worker paths and execution persistence. - Added `TimelineIngestionOptions` for configuring NATS and Redis ingestion transports. - Implemented `NatsTimelineEventSubscriber` for subscribing to NATS events. - Developed `RedisTimelineEventSubscriber` for reading from Redis Streams. - Added `TimelineEnvelopeParser` to normalize incoming event envelopes. - Created unit tests for `TimelineEnvelopeParser` to ensure correct field mapping. - Implemented `TimelineAuthorizationAuditSink` for logging authorization outcomes.
This commit is contained in:
@@ -1,6 +1,7 @@
|
||||
using Microsoft.Extensions.Configuration;
|
||||
using StellaOps.TimelineIndexer.Core.Abstractions;
|
||||
using StellaOps.TimelineIndexer.Infrastructure.DependencyInjection;
|
||||
using StellaOps.TimelineIndexer.Infrastructure.Options;
|
||||
using StellaOps.TimelineIndexer.Infrastructure.Subscriptions;
|
||||
using StellaOps.TimelineIndexer.Worker;
|
||||
|
||||
@@ -11,6 +12,12 @@ builder.Configuration.AddJsonFile("appsettings.Development.json", optional: true
|
||||
builder.Configuration.AddEnvironmentVariables(prefix: "TIMELINE_");
|
||||
|
||||
builder.Services.AddTimelineIndexerPostgres(builder.Configuration);
|
||||
builder.Services.AddOptions<TimelineIngestionOptions>()
|
||||
.Bind(builder.Configuration.GetSection("Ingestion"));
|
||||
|
||||
builder.Services.AddSingleton<TimelineEnvelopeParser>();
|
||||
builder.Services.AddSingleton<ITimelineEventSubscriber, NatsTimelineEventSubscriber>();
|
||||
builder.Services.AddSingleton<ITimelineEventSubscriber, RedisTimelineEventSubscriber>();
|
||||
builder.Services.AddSingleton<ITimelineEventSubscriber, NullTimelineEventSubscriber>();
|
||||
builder.Services.AddHostedService<TimelineIngestionWorker>();
|
||||
|
||||
|
||||
@@ -12,17 +12,20 @@ namespace StellaOps.TimelineIndexer.Worker;
|
||||
public sealed class TimelineIngestionWorker(
|
||||
IEnumerable<ITimelineEventSubscriber> subscribers,
|
||||
ITimelineIngestionService ingestionService,
|
||||
ILogger<TimelineIngestionWorker> logger) : BackgroundService
|
||||
ILogger<TimelineIngestionWorker> logger,
|
||||
TimeProvider? timeProvider = null) : BackgroundService
|
||||
{
|
||||
private static readonly Meter Meter = new("StellaOps.TimelineIndexer", "1.0.0");
|
||||
private static readonly Counter<long> IngestedCounter = Meter.CreateCounter<long>("timeline.ingested");
|
||||
private static readonly Counter<long> DuplicateCounter = Meter.CreateCounter<long>("timeline.duplicates");
|
||||
private static readonly Counter<long> FailedCounter = Meter.CreateCounter<long>("timeline.failed");
|
||||
private static readonly Histogram<double> LagHistogram = Meter.CreateHistogram<double>("timeline.ingest.lag.seconds");
|
||||
|
||||
private readonly IEnumerable<ITimelineEventSubscriber> _subscribers = subscribers;
|
||||
private readonly ITimelineIngestionService _ingestion = ingestionService;
|
||||
private readonly ILogger<TimelineIngestionWorker> _logger = logger;
|
||||
private readonly ConcurrentDictionary<(string tenant, string eventId), byte> _sessionSeen = new();
|
||||
private readonly TimeProvider _timeProvider = timeProvider ?? TimeProvider.System;
|
||||
|
||||
protected override Task ExecuteAsync(CancellationToken stoppingToken)
|
||||
{
|
||||
@@ -48,6 +51,7 @@ public sealed class TimelineIngestionWorker(
|
||||
if (result.Inserted)
|
||||
{
|
||||
IngestedCounter.Add(1);
|
||||
LagHistogram.Record((_timeProvider.GetUtcNow() - envelope.OccurredAt).TotalSeconds);
|
||||
_logger.LogInformation("Ingested timeline event {EventId} from {Source} (tenant {Tenant})", envelope.EventId, envelope.Source, envelope.TenantId);
|
||||
}
|
||||
else
|
||||
|
||||
@@ -1,8 +1,34 @@
|
||||
{
|
||||
"Logging": {
|
||||
"LogLevel": {
|
||||
"Default": "Information",
|
||||
"Microsoft.Hosting.Lifetime": "Information"
|
||||
}
|
||||
}
|
||||
}
|
||||
{
|
||||
"Logging": {
|
||||
"LogLevel": {
|
||||
"Default": "Debug",
|
||||
"Microsoft.Hosting.Lifetime": "Information"
|
||||
}
|
||||
},
|
||||
"Postgres": {
|
||||
"Timeline": {
|
||||
"ConnectionString": "Host=localhost;Database=timeline;Username=timeline;Password=timeline;",
|
||||
"SchemaName": "timeline",
|
||||
"CommandTimeoutSeconds": 30
|
||||
}
|
||||
},
|
||||
"Ingestion": {
|
||||
"Nats": {
|
||||
"Enabled": false,
|
||||
"Url": "nats://localhost:4222",
|
||||
"Subject": "orch.event",
|
||||
"QueueGroup": "timeline-indexer",
|
||||
"Prefetch": 64
|
||||
},
|
||||
"Redis": {
|
||||
"Enabled": false,
|
||||
"ConnectionString": "localhost:6379",
|
||||
"Stream": "timeline.events",
|
||||
"ConsumerGroup": "timeline-indexer",
|
||||
"ConsumerName": "timeline-worker",
|
||||
"ValueField": "data",
|
||||
"MaxBatchSize": 128,
|
||||
"PollIntervalMilliseconds": 250
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,8 +1,34 @@
|
||||
{
|
||||
"Logging": {
|
||||
"LogLevel": {
|
||||
"Default": "Information",
|
||||
"Microsoft.Hosting.Lifetime": "Information"
|
||||
}
|
||||
}
|
||||
}
|
||||
{
|
||||
"Logging": {
|
||||
"LogLevel": {
|
||||
"Default": "Information",
|
||||
"Microsoft.Hosting.Lifetime": "Information"
|
||||
}
|
||||
},
|
||||
"Postgres": {
|
||||
"Timeline": {
|
||||
"ConnectionString": "Host=localhost;Database=timeline;Username=timeline;Password=timeline;",
|
||||
"SchemaName": "timeline",
|
||||
"CommandTimeoutSeconds": 30
|
||||
}
|
||||
},
|
||||
"Ingestion": {
|
||||
"Nats": {
|
||||
"Enabled": false,
|
||||
"Url": "nats://localhost:4222",
|
||||
"Subject": "orch.event",
|
||||
"QueueGroup": "timeline-indexer",
|
||||
"Prefetch": 64
|
||||
},
|
||||
"Redis": {
|
||||
"Enabled": false,
|
||||
"ConnectionString": "localhost:6379",
|
||||
"Stream": "timeline.events",
|
||||
"ConsumerGroup": "timeline-indexer",
|
||||
"ConsumerName": "timeline-worker",
|
||||
"ValueField": "data",
|
||||
"MaxBatchSize": 128,
|
||||
"PollIntervalMilliseconds": 250
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user