Skip to content

Commit 7dfd06f

Browse files
authored
feat(persistence): Add durable backplane connectors (Kafka, Azure Service Bus, SQL Server) with DSL support (#18)
1 parent 9541971 commit 7dfd06f

File tree

47 files changed

+4229
-0
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

47 files changed

+4229
-0
lines changed

docs/durable-backplanes.md

Lines changed: 370 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,370 @@
1+
# Durable Data Backplane Connectors
2+
3+
This document describes the durable, production-ready backplane connectors available for ExperimentFramework.
4+
5+
## Overview
6+
7+
Durable backplane connectors allow experimentation telemetry, state, and analysis events to be persisted and streamed reliably to external systems. These connectors are fully compatible with the **ExperimentBuilder YAML/JSON DSL**, enabling declarative configuration of data pipelines.
8+
9+
## Available Backplanes
10+
11+
### 1. Kafka Backplane
12+
13+
**Package**: `ExperimentFramework.DataPlane.Kafka`
14+
15+
A stream-oriented backplane for high-throughput, durable event pipelines.
16+
17+
#### Features
18+
19+
- Configurable topics per event type or unified topic
20+
- Partitioning strategies (by experiment key, subject ID, tenant ID, or round-robin)
21+
- Idempotent producer support
22+
- Batching and async flush behavior
23+
- Schema version propagation for downstream consumers
24+
25+
#### Configuration Example
26+
27+
```yaml
28+
dataPlane:
29+
backplane:
30+
type: kafka
31+
options:
32+
brokers:
33+
- broker1:9092
34+
- broker2:9092
35+
topic: experiment-events # Optional: use single topic for all events
36+
partitionBy: experimentKey # experimentKey, subjectId, tenantId, roundRobin
37+
batchSize: 500
38+
lingerMs: 100
39+
enableIdempotence: true
40+
compressionType: snappy # none, gzip, snappy, lz4, zstd
41+
acks: all # all, 1, 0
42+
clientId: my-experiment-app
43+
```
44+
45+
#### Programmatic Configuration
46+
47+
```csharp
48+
services.AddKafkaDataBackplane(options =>
49+
{
50+
options.Brokers = new List<string> { "localhost:9092" };
51+
options.PartitionStrategy = KafkaPartitionStrategy.ByExperimentKey;
52+
options.BatchSize = 500;
53+
options.LingerMs = 100;
54+
});
55+
```
56+
57+
#### Topic Naming
58+
59+
If no unified `topic` is specified, events are routed to type-specific topics:
60+
61+
- `experiment-exposures` - Exposure events
62+
- `experiment-assignments` - Assignment events
63+
- `experiment-outcomes` - Outcome events
64+
- `experiment-analysis-signals` - Analysis signals
65+
- `experiment-errors` - Error events
66+
67+
#### Use Cases
68+
69+
- Real-time analytics pipelines
70+
- Data warehouse ingestion
71+
- Cross-service experimentation
72+
- Event sourcing
73+
74+
---
75+
76+
### 2. Azure Service Bus Backplane
77+
78+
**Package**: `ExperimentFramework.DataPlane.AzureServiceBus`
79+
80+
A cloud-hosted durable messaging backplane for Azure environments.
81+
82+
#### Features
83+
84+
- Queue and topic/subscription support
85+
- Message grouping with sessions for ordering guarantees
86+
- Dead-letter handling and retry semantics with exponential backoff
87+
- Integration with Azure-native data processing
88+
- Configurable TTL and retention policies
89+
- Batching for improved throughput
90+
91+
#### Configuration Example
92+
93+
```yaml
94+
dataPlane:
95+
backplane:
96+
type: azureServiceBus
97+
options:
98+
connectionString: "Endpoint=sb://myns.servicebus.windows.net/;..."
99+
queueName: experiment-events # or use topicName
100+
batchSize: 100
101+
maxRetryAttempts: 3
102+
messageTimeToLiveMinutes: 1440 # 24 hours
103+
enableSessions: false
104+
sessionStrategy: experimentKey # experimentKey, subjectId, tenantId
105+
```
106+
107+
#### Programmatic Configuration
108+
109+
```csharp
110+
services.AddAzureServiceBusDataBackplane(options =>
111+
{
112+
options.ConnectionString = "Endpoint=sb://...";
113+
options.QueueName = "experiment-events";
114+
options.BatchSize = 100;
115+
options.MaxRetryAttempts = 3;
116+
});
117+
```
118+
119+
#### Destination Modes
120+
121+
**Queue Mode** (single consumer):
122+
```yaml
123+
options:
124+
queueName: experiment-events
125+
```
126+
127+
**Topic Mode** (multiple subscribers):
128+
```yaml
129+
options:
130+
topicName: experiment-events
131+
```
132+
133+
**Type-Specific Destinations**:
134+
```yaml
135+
options:
136+
useTypeSpecificDestinations: true
137+
```
138+
139+
#### Use Cases
140+
141+
- Reliable delivery across Azure services
142+
- Integration with Azure-native workflows (Functions, Logic Apps)
143+
- Multi-subscriber scenarios with topics
144+
- Guaranteed message ordering with sessions
145+
146+
---
147+
148+
### 3. SQL Server Backplane
149+
150+
**Package**: `ExperimentFramework.DataPlane.SqlServer`
151+
152+
A relational, append-only persistence backplane for queryable event storage with EF Core 10.
153+
154+
#### Features
155+
156+
- Normalized tables with EF Core 10 migrations
157+
- Explicit schema versioning
158+
- Optimized indexes for common joins (by event type, timestamp, correlation ID)
159+
- Idempotency support with unique event ID constraint
160+
- Transactional writes with ACID guarantees
161+
- Batching for improved throughput
162+
- Optional auto-migration on startup
163+
164+
#### Configuration Example
165+
166+
```yaml
167+
dataPlane:
168+
backplane:
169+
type: sqlServer
170+
options:
171+
connectionString: "Server=localhost;Database=ExperimentFramework;..."
172+
schema: dbo
173+
tableName: ExperimentEvents
174+
batchSize: 100
175+
enableIdempotency: true
176+
autoMigrate: false # Set true for dev, false for production
177+
```
178+
179+
#### Programmatic Configuration
180+
181+
```csharp
182+
services.AddSqlServerDataBackplane(options =>
183+
{
184+
options.ConnectionString = "Server=localhost;Database=ExperimentFramework;...";
185+
options.Schema = "dbo";
186+
options.TableName = "ExperimentEvents";
187+
options.BatchSize = 100;
188+
options.EnableIdempotency = true;
189+
});
190+
```
191+
192+
#### Database Schema
193+
194+
```sql
195+
CREATE TABLE [dbo].[ExperimentEvents] (
196+
[Id] bigint IDENTITY PRIMARY KEY,
197+
[EventId] nvarchar(100) UNIQUE NOT NULL,
198+
[Timestamp] datetimeoffset NOT NULL,
199+
[EventType] nvarchar(50) NOT NULL,
200+
[SchemaVersion] nvarchar(20) NOT NULL,
201+
[PayloadJson] nvarchar(max) NOT NULL,
202+
[CorrelationId] nvarchar(100),
203+
[MetadataJson] nvarchar(max),
204+
[CreatedAt] datetimeoffset DEFAULT SYSDATETIMEOFFSET()
205+
);
206+
```
207+
208+
#### Migrations
209+
210+
```bash
211+
# Apply migrations
212+
dotnet ef database update --project src/ExperimentFramework.DataPlane.SqlServer
213+
214+
# Generate SQL script
215+
dotnet ef migrations script --output migrations.sql
216+
```
217+
218+
#### Use Cases
219+
220+
- Direct SQL querying of experiment data
221+
- Long-term audit and compliance
222+
- Smaller deployments without streaming infrastructure
223+
- Joining experiments with business data
224+
- Ad-hoc analysis with SQL tools
225+
226+
---
227+
228+
## DSL Integration
229+
230+
All backplanes are configured through the ExperimentBuilder YAML/JSON DSL. The configuration is validated at parse time, providing early feedback on configuration errors.
231+
232+
### Registering Custom Backplane Handlers
233+
234+
Extension packages can register custom backplane handlers:
235+
236+
```csharp
237+
services.AddConfigurationBackplaneHandler<MyCustomBackplaneHandler>();
238+
```
239+
240+
Your handler must implement `IConfigurationBackplaneHandler`:
241+
242+
```csharp
243+
public class MyCustomBackplaneHandler : IConfigurationBackplaneHandler
244+
{
245+
public string BackplaneType => "myCustomBackplane";
246+
247+
public void ConfigureServices(IServiceCollection services,
248+
DataPlaneBackplaneConfig config, ILogger? logger)
249+
{
250+
// Extract options and configure services
251+
}
252+
253+
public IEnumerable<ConfigurationValidationError> Validate(
254+
DataPlaneBackplaneConfig config, string path)
255+
{
256+
// Validate configuration
257+
return Enumerable.Empty<ConfigurationValidationError>();
258+
}
259+
}
260+
```
261+
262+
---
263+
264+
## Data Plane Options
265+
266+
Common data plane options that apply to all backplanes:
267+
268+
```yaml
269+
dataPlane:
270+
# Event type toggles
271+
enableExposureEvents: true
272+
enableAssignmentEvents: true
273+
enableOutcomeEvents: true
274+
enableAnalysisSignals: true
275+
enableErrorEvents: true
276+
277+
# Sampling and performance
278+
samplingRate: 1.0 # 0.0-1.0 (1.0 = 100%)
279+
batchSize: 500
280+
flushIntervalMs: 1000
281+
282+
# Backplane selection
283+
backplane:
284+
type: kafka # or azureServiceBus, sqlServer, inMemory, logging, openTelemetry
285+
options:
286+
# Backplane-specific options
287+
```
288+
289+
---
290+
291+
## Event Schemas
292+
293+
All backplanes preserve standardized event envelopes and schema versions:
294+
295+
### DataPlaneEnvelope
296+
297+
```csharp
298+
public sealed class DataPlaneEnvelope
299+
{
300+
public string EventId { get; init; }
301+
public DateTimeOffset Timestamp { get; init; }
302+
public DataPlaneEventType EventType { get; init; }
303+
public string SchemaVersion { get; init; }
304+
public object Payload { get; init; }
305+
public string? CorrelationId { get; init; }
306+
public IReadOnlyDictionary<string, object>? Metadata { get; init; }
307+
}
308+
```
309+
310+
### Event Types
311+
312+
- **Exposure**: Subject exposed to a variant
313+
- **Assignment**: Subject's assignment changed
314+
- **Outcome**: Experiment outcome recorded
315+
- **AnalysisSignal**: Statistical or science signal
316+
- **Error**: Error during experiment execution
317+
318+
---
319+
320+
## Health Checks
321+
322+
All backplanes implement health checking:
323+
324+
```csharp
325+
var health = await backplane.HealthAsync();
326+
Console.WriteLine($"Healthy: {health.IsHealthy}, Message: {health.Message}");
327+
```
328+
329+
---
330+
331+
## Examples
332+
333+
See the [samples directory](/samples/ExperimentDefinitions/) for complete examples:
334+
335+
- `kafka-backplane-example.yaml` - Kafka configuration
336+
- `azure-service-bus-backplane-example.yaml` - Azure Service Bus configuration
337+
- `sql-server-backplane-example.yaml` - SQL Server configuration
338+
339+
---
340+
341+
## Installation
342+
343+
```bash
344+
# Kafka backplane
345+
dotnet add package ExperimentFramework.DataPlane.Kafka
346+
347+
# Azure Service Bus
348+
dotnet add package ExperimentFramework.DataPlane.AzureServiceBus
349+
350+
# SQL Server
351+
dotnet add package ExperimentFramework.DataPlane.SqlServer
352+
```
353+
354+
---
355+
356+
## Requirements
357+
358+
- .NET 10.0 or later
359+
- ExperimentFramework.DataPlane.Abstractions
360+
- ExperimentFramework.Configuration (for DSL support)
361+
362+
For Kafka:
363+
- Confluent.Kafka NuGet package
364+
- Access to Kafka broker(s)
365+
366+
---
367+
368+
## License
369+
370+
Same as ExperimentFramework main library.

0 commit comments

Comments
 (0)