Initial import of the CBDDC codebase with docs and tests. Add a .NET-focused gitignore to keep generated artifacts out of source control.
Some checks failed
CI / verify (push) Has been cancelled
Some checks failed
CI / verify (push) Has been cancelled
This commit is contained in:
751
README.md
Executable file
751
README.md
Executable file
@@ -0,0 +1,751 @@
|
||||
# CBDDC
|
||||
|
||||
<div align="center">
|
||||
|
||||
**Peer-to-Peer Data Synchronization Middleware for .NET**
|
||||
|
||||
[](https://dotnet.microsoft.com/)
|
||||
[](LICENSE)
|
||||
|
||||
## Status
|
||||

|
||||

|
||||
|
||||
CBDDC is not a database - it's a **sync layer** that plugs into your existing data store and enables automatic peer-to-peer replication across nodes in a mesh network.
|
||||
|
||||
[Architecture](#architecture) | [Quick Start](#quick-start) | [Integration Guide](#integrating-with-your-database) | [Documentation](#documentation)
|
||||
|
||||
</div>
|
||||
|
||||
---
|
||||
|
||||
## Table of Contents
|
||||
|
||||
- [Overview](#overview)
|
||||
- [Architecture](#architecture)
|
||||
- [Key Features](#key-features)
|
||||
- [Installation](#installation)
|
||||
- [Quick Start](#quick-start)
|
||||
- [Integrating with Your Database](#integrating-with-your-database)
|
||||
- [Cloud Deployment](#cloud-deployment)
|
||||
- [Production Features](#production-features)
|
||||
- [Use Cases](#use-cases)
|
||||
- [Documentation](#documentation)
|
||||
- [Contributing](#contributing)
|
||||
- [License](#license)
|
||||
|
||||
---
|
||||
|
||||
## Overview
|
||||
|
||||
**CBDDC** is a lightweight, embeddable **data synchronization middleware** for .NET. It observes changes in your database via **Change Data Capture (CDC)**, records them in an append-only **Oplog**, and replicates them across nodes connected via a **P2P mesh network**.
|
||||
|
||||
Your application continues to read and write to its database as usual. CBDDC works in the background.
|
||||
|
||||
> **[LAN] Designed for Local Area Networks (LAN)**
|
||||
> Built for trusted environments: offices, retail stores, edge deployments. Cross-platform (Windows, Linux, macOS).
|
||||
|
||||
> **[Cloud] Cloud Ready**
|
||||
> ASP.NET Core hosting for controlled, server-side deployments.
|
||||
|
||||
---
|
||||
|
||||
## Architecture
|
||||
|
||||
```
|
||||
+---------------------------------------------------+
|
||||
| Your Application |
|
||||
| db.Users.InsertAsync(user) |
|
||||
| db.Users.Find(u => u.Age > 18) |
|
||||
+---------------------------------------------------+
|
||||
| uses your DbContext directly
|
||||
+---------------------------------------------------+
|
||||
| Your Database (BLite) |
|
||||
| +---------------------------------------------+ |
|
||||
| | Users | Orders | Products | ... |
|
||||
| +---------------------------------------------+ |
|
||||
| | CDC (Change Data Capture) |
|
||||
| | |
|
||||
| +---------------------------------------------+ |
|
||||
| | CBDDC Sync Engine | |
|
||||
| | - Oplog (append-only hash-chained journal)| |
|
||||
| | - Vector Clock (causal ordering) | |
|
||||
| | - Conflict Resolution (LWW / Custom Merge)| |
|
||||
| +---------------------------------------------+ |
|
||||
+---------------------------------------------------+
|
||||
| P2P Network (TCP + UDP Discovery)
|
||||
+---------------------------------------------------+
|
||||
| Other Nodes (same setup) |
|
||||
| Node-A <-----> Node-B <-----> Node-C |
|
||||
+---------------------------------------------------+
|
||||
```
|
||||
|
||||
### Core Concepts
|
||||
|
||||
| Concept | Description |
|
||||
|---------|-------------|
|
||||
| **Oplog** | Append-only journal of changes, hash-chained per node for integrity |
|
||||
| **Vector Clock** | Tracks causal ordering - knows who has what across the mesh |
|
||||
| **CDC** | Change Data Capture - watches your registered collections for local writes |
|
||||
| **Document Store** | Your bridge class - maps between your entities and the sync engine |
|
||||
| **Conflict Resolution** | Pluggable strategy (Last-Write-Wins or custom recursive merge) |
|
||||
| **VectorClockService** | Shared singleton keeping the Vector Clock in sync between CDC and OplogStore |
|
||||
|
||||
### Sync Flow
|
||||
|
||||
```
|
||||
Local Write -> CDC Trigger -> OplogEntry Created -> VectorClock Updated
|
||||
|
|
||||
v
|
||||
SyncOrchestrator
|
||||
(gossip every 2s)
|
||||
|
|
||||
+---------+----------+
|
||||
| |
|
||||
Push changes Pull changes
|
||||
to peers from peers
|
||||
| |
|
||||
v v
|
||||
Remote node Apply to local
|
||||
applies via OplogStore +
|
||||
ApplyBatchAsync DocumentStore
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Key Features
|
||||
|
||||
### [Select] Selective Collection Sync
|
||||
Only collections registered via `WatchCollection()` are tracked. Your database can have hundreds of tables - only the ones you opt-in participate in replication.
|
||||
|
||||
### [Gossip] Interest-Aware Gossip
|
||||
Nodes advertise which collections they sync. The orchestrator prioritizes peers sharing common interests, reducing unnecessary traffic.
|
||||
|
||||
### [Offline] Offline First
|
||||
- Read/write operations work offline - they're direct database operations
|
||||
- Automatic sync when peers reconnect
|
||||
- Oplog-based gap recovery and snapshot fallback
|
||||
|
||||
### [Secure] Secure Networking
|
||||
- Noise Protocol handshake with ECDH key exchange
|
||||
- AES-256 encryption for data in transit
|
||||
- HMAC authentication
|
||||
- Brotli compression for bandwidth efficiency
|
||||
|
||||
### [Conflict] Conflict Resolution
|
||||
- **Last Write Wins (LWW)** - default, HLC timestamp-based
|
||||
- **Recursive Merge** - deep JSON merge for concurrent edits
|
||||
- **Custom** - implement `IConflictResolver` for your business logic
|
||||
|
||||
### [Cloud] Cloud Infrastructure
|
||||
- ASP.NET Core hosting (single-cluster mode)
|
||||
- BLite embedded persistence
|
||||
- shared-token authentication
|
||||
|
||||
---
|
||||
|
||||
## Installation
|
||||
|
||||
### Packages
|
||||
|
||||
| Package | Purpose |
|
||||
|---------|---------|
|
||||
| `ZB.MOM.WW.CBDDC.Core` | Interfaces, models, conflict resolution (.NET Standard 2.0+) |
|
||||
| `ZB.MOM.WW.CBDDC.Persistence` | BLite persistence provider, OplogStore, VectorClockService (.NET 10+) |
|
||||
| `CBDDC.Network` | TCP sync, UDP discovery, Protobuf protocol (.NET Standard 2.0+) |
|
||||
|
||||
```bash
|
||||
# BLite (embedded document DB)
|
||||
dotnet add package ZB.MOM.WW.CBDDC.Core
|
||||
dotnet add package ZB.MOM.WW.CBDDC.Persistence
|
||||
dotnet add package CBDDC.Network
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Quick Start
|
||||
|
||||
### 1. Define Your Database Context
|
||||
|
||||
```csharp
|
||||
public class MyDbContext : CBDDCDocumentDbContext
|
||||
{
|
||||
public DocumentCollection<string, Customer> Customers { get; private set; }
|
||||
public DocumentCollection<string, Order> Orders { get; private set; }
|
||||
|
||||
public MyDbContext(string dbPath) : base(dbPath) { }
|
||||
}
|
||||
```
|
||||
|
||||
### 2. Create Your Document Store (the Sync Bridge)
|
||||
|
||||
This is where you tell CBDDC **which collections to sync** and **how to map** between your entities and the sync engine:
|
||||
|
||||
```csharp
|
||||
public class MyDocumentStore : BLiteDocumentStore<MyDbContext>
|
||||
{
|
||||
public MyDocumentStore(
|
||||
MyDbContext context,
|
||||
IPeerNodeConfigurationProvider configProvider,
|
||||
IVectorClockService vectorClockService,
|
||||
ILogger<MyDocumentStore>? logger = null)
|
||||
: base(context, configProvider, vectorClockService, logger: logger)
|
||||
{
|
||||
// Register collections for CDC - only these will be synced
|
||||
WatchCollection("Customers", context.Customers, c => c.Id);
|
||||
WatchCollection("Orders", context.Orders, o => o.Id);
|
||||
}
|
||||
|
||||
// Map incoming sync data back to your entities
|
||||
protected override async Task ApplyContentToEntityAsync(
|
||||
string collection, string key, JsonElement content, CancellationToken ct)
|
||||
{
|
||||
switch (collection)
|
||||
{
|
||||
case "Customers":
|
||||
var customer = content.Deserialize<Customer>()!;
|
||||
customer.Id = key;
|
||||
var existing = _context.Customers
|
||||
.Find(c => c.Id == key).FirstOrDefault();
|
||||
if (existing != null) _context.Customers.Update(customer);
|
||||
else _context.Customers.Insert(customer);
|
||||
break;
|
||||
case "Orders":
|
||||
var order = content.Deserialize<Order>()!;
|
||||
order.Id = key;
|
||||
var existingOrder = _context.Orders
|
||||
.Find(o => o.Id == key).FirstOrDefault();
|
||||
if (existingOrder != null) _context.Orders.Update(order);
|
||||
else _context.Orders.Insert(order);
|
||||
break;
|
||||
}
|
||||
await _context.SaveChangesAsync(ct);
|
||||
}
|
||||
|
||||
protected override Task<JsonElement?> GetEntityAsJsonAsync(
|
||||
string collection, string key, CancellationToken ct)
|
||||
{
|
||||
object? entity = collection switch
|
||||
{
|
||||
"Customers" => _context.Customers.Find(c => c.Id == key).FirstOrDefault(),
|
||||
"Orders" => _context.Orders.Find(o => o.Id == key).FirstOrDefault(),
|
||||
_ => null
|
||||
};
|
||||
return Task.FromResult(entity != null
|
||||
? (JsonElement?)JsonSerializer.SerializeToElement(entity) : null);
|
||||
}
|
||||
|
||||
protected override async Task RemoveEntityAsync(
|
||||
string collection, string key, CancellationToken ct)
|
||||
{
|
||||
switch (collection)
|
||||
{
|
||||
case "Customers": _context.Customers.Delete(key); break;
|
||||
case "Orders": _context.Orders.Delete(key); break;
|
||||
}
|
||||
await _context.SaveChangesAsync(ct);
|
||||
}
|
||||
|
||||
protected override Task<IEnumerable<(string Key, JsonElement Content)>>
|
||||
GetAllEntitiesAsJsonAsync(string collection, CancellationToken ct)
|
||||
{
|
||||
IEnumerable<(string, JsonElement)> result = collection switch
|
||||
{
|
||||
"Customers" => _context.Customers.FindAll()
|
||||
.Select(c => (c.Id, JsonSerializer.SerializeToElement(c))),
|
||||
"Orders" => _context.Orders.FindAll()
|
||||
.Select(o => (o.Id, JsonSerializer.SerializeToElement(o))),
|
||||
_ => Enumerable.Empty<(string, JsonElement)>()
|
||||
};
|
||||
return Task.FromResult(result);
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### 3. Wire It Up
|
||||
|
||||
```csharp
|
||||
var builder = Host.CreateApplicationBuilder();
|
||||
|
||||
// Configure the node
|
||||
builder.Services.AddSingleton<IPeerNodeConfigurationProvider>(
|
||||
new StaticPeerNodeConfigurationProvider(new PeerNodeConfiguration
|
||||
{
|
||||
NodeId = "node-1",
|
||||
TcpPort = 8580,
|
||||
AuthToken = "my-cluster-secret"
|
||||
}));
|
||||
|
||||
// Register CBDDC services
|
||||
builder.Services
|
||||
.AddCBDDCCore()
|
||||
.AddCBDDCBLite<MyDbContext, MyDocumentStore>(
|
||||
sp => new MyDbContext("mydata.blite"))
|
||||
.AddCBDDCNetwork<StaticPeerNodeConfigurationProvider>();
|
||||
|
||||
await builder.Build().RunAsync();
|
||||
```
|
||||
|
||||
### 4. Use Your Database Normally
|
||||
|
||||
```csharp
|
||||
public class MyService
|
||||
{
|
||||
private readonly MyDbContext _db;
|
||||
|
||||
public MyService(MyDbContext db) => _db = db;
|
||||
|
||||
public async Task CreateCustomer(string name)
|
||||
{
|
||||
// Write directly - CBDDC handles sync automatically
|
||||
await _db.Customers.InsertAsync(
|
||||
new Customer { Id = Guid.NewGuid().ToString(), Name = name });
|
||||
await _db.SaveChangesAsync();
|
||||
|
||||
// Changes are automatically:
|
||||
// 1. Detected via CDC
|
||||
// 2. Recorded in the Oplog with HLC timestamp + hash chain
|
||||
// 3. Pushed to connected peers via gossip
|
||||
// 4. Applied on remote nodes via conflict resolution
|
||||
}
|
||||
|
||||
public async Task<List<Customer>> GetYoungCustomers()
|
||||
{
|
||||
// Read directly from your DB - no CBDDC API
|
||||
return _db.Customers.Find(c => c.Age < 30).ToList();
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Integrating with Your Database
|
||||
|
||||
If you have an **existing database** and want to add P2P sync:
|
||||
|
||||
### Step 1 - Wrap your context
|
||||
|
||||
Create a `DbContext` extending `CBDDCDocumentDbContext`. This can wrap your existing collections/tables.
|
||||
|
||||
```csharp
|
||||
public class MyExistingDbContext : CBDDCDocumentDbContext
|
||||
{
|
||||
// Your existing collections
|
||||
public DocumentCollection<string, Product> Products { get; private set; }
|
||||
public DocumentCollection<string, Inventory> Inventory { get; private set; }
|
||||
|
||||
public MyExistingDbContext(string dbPath) : base(dbPath) { }
|
||||
}
|
||||
```
|
||||
|
||||
### Step 2 - Create a DocumentStore
|
||||
|
||||
Extend `BLiteDocumentStore<T>`. This is the **bridge** between your data model and the sync engine.
|
||||
|
||||
```csharp
|
||||
public class MyDocumentStore : BLiteDocumentStore<MyExistingDbContext>
|
||||
{
|
||||
public MyDocumentStore(MyExistingDbContext ctx,
|
||||
IPeerNodeConfigurationProvider cfg,
|
||||
IVectorClockService vc,
|
||||
ILogger<MyDocumentStore>? log = null)
|
||||
: base(ctx, cfg, vc, logger: log)
|
||||
{
|
||||
// Continue to next step...
|
||||
}
|
||||
|
||||
// Implement abstract methods (see below)...
|
||||
}
|
||||
```
|
||||
|
||||
### Step 3 - Register only what you need
|
||||
|
||||
Call `WatchCollection()` in the constructor for each collection you want to replicate. Everything else is ignored by the sync engine.
|
||||
|
||||
```csharp
|
||||
public MyDocumentStore(...)
|
||||
: base(ctx, cfg, vc, logger: log)
|
||||
{
|
||||
// Only these 2 collections will be synced across the mesh
|
||||
WatchCollection("Products", ctx.Products, p => p.Id);
|
||||
WatchCollection("Inventory", ctx.Inventory, i => i.Id);
|
||||
|
||||
// All other collections in your DB are local-only
|
||||
}
|
||||
```
|
||||
|
||||
### Step 4 - Implement the mapping methods
|
||||
|
||||
CBDDC stores data as `JsonElement`. You provide four mapping methods:
|
||||
|
||||
| Method | Purpose |
|
||||
|--------|---------|
|
||||
| `ApplyContentToEntityAsync` | Write incoming sync data to your entities |
|
||||
| `GetEntityAsJsonAsync` | Read your entities for outbound sync |
|
||||
| `RemoveEntityAsync` | Handle remote deletes |
|
||||
| `GetAllEntitiesAsJsonAsync` | Provide full collection for snapshot sync |
|
||||
|
||||
```csharp
|
||||
protected override async Task ApplyContentToEntityAsync(
|
||||
string collection, string key, JsonElement content, CancellationToken ct)
|
||||
{
|
||||
switch (collection)
|
||||
{
|
||||
case "Products":
|
||||
var product = content.Deserialize<Product>()!;
|
||||
product.Id = key;
|
||||
var existing = _context.Products.Find(p => p.Id == key).FirstOrDefault();
|
||||
if (existing != null) _context.Products.Update(product);
|
||||
else _context.Products.Insert(product);
|
||||
break;
|
||||
case "Inventory":
|
||||
var inv = content.Deserialize<Inventory>()!;
|
||||
inv.Id = key;
|
||||
var existingInv = _context.Inventory.Find(i => i.Id == key).FirstOrDefault();
|
||||
if (existingInv != null) _context.Inventory.Update(inv);
|
||||
else _context.Inventory.Insert(inv);
|
||||
break;
|
||||
}
|
||||
await _context.SaveChangesAsync(ct);
|
||||
}
|
||||
|
||||
protected override Task<JsonElement?> GetEntityAsJsonAsync(
|
||||
string collection, string key, CancellationToken ct)
|
||||
{
|
||||
object? entity = collection switch
|
||||
{
|
||||
"Products" => _context.Products.Find(p => p.Id == key).FirstOrDefault(),
|
||||
"Inventory" => _context.Inventory.Find(i => i.Id == key).FirstOrDefault(),
|
||||
_ => null
|
||||
};
|
||||
return Task.FromResult(entity != null
|
||||
? (JsonElement?)JsonSerializer.SerializeToElement(entity) : null);
|
||||
}
|
||||
|
||||
protected override async Task RemoveEntityAsync(
|
||||
string collection, string key, CancellationToken ct)
|
||||
{
|
||||
switch (collection)
|
||||
{
|
||||
case "Products": _context.Products.Delete(key); break;
|
||||
case "Inventory": _context.Inventory.Delete(key); break;
|
||||
}
|
||||
await _context.SaveChangesAsync(ct);
|
||||
}
|
||||
|
||||
protected override Task<IEnumerable<(string Key, JsonElement Content)>>
|
||||
GetAllEntitiesAsJsonAsync(string collection, CancellationToken ct)
|
||||
{
|
||||
IEnumerable<(string, JsonElement)> result = collection switch
|
||||
{
|
||||
"Products" => _context.Products.FindAll()
|
||||
.Select(p => (p.Id, JsonSerializer.SerializeToElement(p))),
|
||||
"Inventory" => _context.Inventory.FindAll()
|
||||
.Select(i => (i.Id, JsonSerializer.SerializeToElement(i))),
|
||||
_ => Enumerable.Empty<(string, JsonElement)>()
|
||||
};
|
||||
return Task.FromResult(result);
|
||||
}
|
||||
|
||||
// Optional: Batch operations for better performance
|
||||
protected override async Task ApplyContentToEntitiesBatchAsync(
|
||||
IEnumerable<(string Collection, string Key, JsonElement Content)> documents,
|
||||
CancellationToken ct)
|
||||
{
|
||||
foreach (var (collection, key, content) in documents)
|
||||
{
|
||||
// Call the single-item method (you can optimize this further)
|
||||
await ApplyContentToEntityAsync(collection, key, content, ct);
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**Your existing CRUD code stays unchanged.** CBDDC plugs in alongside it.
|
||||
|
||||
### What Happens Under the Hood
|
||||
|
||||
```
|
||||
Your Code: db.Users.InsertAsync(user)
|
||||
|
|
||||
v
|
||||
BLite: SaveChangesAsync()
|
||||
|
|
||||
| CDC fires (WatchCollection observer)
|
||||
DocumentStore: CreateOplogEntryAsync()
|
||||
|
|
||||
+-> OplogEntry written (hash-chained, HLC timestamped)
|
||||
+-> VectorClockService.Update() -> sync sees it immediately
|
||||
|
|
||||
v
|
||||
SyncOrchestrator (background, every 2s)
|
||||
+-> Compare VectorClocks with peers
|
||||
+-> Push local changes (interest-filtered)
|
||||
+-> Pull remote changes -> ApplyBatchAsync
|
||||
|
|
||||
v
|
||||
Remote DocumentStore: ApplyContentToEntityAsync()
|
||||
|
|
||||
v
|
||||
Remote Database: Updated!
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Cloud Deployment
|
||||
|
||||
CBDDC supports ASP.NET Core hosting with BLite persistence for cloud deployments.
|
||||
|
||||
### Example: ASP.NET Core with BLite
|
||||
|
||||
```csharp
|
||||
var builder = WebApplication.CreateBuilder(args);
|
||||
|
||||
builder.Services.AddCBDDCCore()
|
||||
.AddCBDDCBLite<MyDbContext, MyDocumentStore>(sp => new MyDbContext("cbddc.blite"))
|
||||
.AddCBDDCNetwork<MyPeerConfigProvider>();
|
||||
|
||||
builder.Services.AddCBDDCHostingSingleCluster(options =>
|
||||
{
|
||||
options.TcpPort = 5001;
|
||||
});
|
||||
|
||||
var app = builder.Build();
|
||||
app.MapHealthChecks("/health");
|
||||
await app.RunAsync();
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Production Features
|
||||
|
||||
### Configuration
|
||||
|
||||
```json
|
||||
{
|
||||
"CBDDC": {
|
||||
"KnownPeers": [
|
||||
{
|
||||
"NodeId": "gateway-1",
|
||||
"Address": "192.168.1.10:5000",
|
||||
"Type": "StaticRemote"
|
||||
}
|
||||
],
|
||||
"RetentionHours": 24,
|
||||
"SyncIntervalSeconds": 2
|
||||
},
|
||||
"Logging": {
|
||||
"LogLevel": {
|
||||
"Default": "Information",
|
||||
"CBDDC": "Warning"
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Health Monitoring
|
||||
|
||||
```csharp
|
||||
var healthCheck = new CBDDCHealthCheck(store, syncTracker);
|
||||
var status = await healthCheck.CheckAsync();
|
||||
|
||||
Console.WriteLine($"Database: {status.DatabaseHealthy}");
|
||||
Console.WriteLine($"Network: {status.NetworkHealthy}");
|
||||
Console.WriteLine($"Peers: {status.ConnectedPeers}");
|
||||
```
|
||||
|
||||
### Resilience
|
||||
|
||||
- **Exponential Backoff**: Automatic retry for unreachable peers
|
||||
- **Offline Queue**: Buffer local changes when network is down
|
||||
- **Snapshot Recovery**: Fast catch-up after long disconnects
|
||||
- **Hash Chain Validation**: Detect and recover from oplog gaps
|
||||
|
||||
### Performance
|
||||
|
||||
- **VectorClock Cache**: In-memory tracking of node states
|
||||
- **Brotli Compression**: 70-80% bandwidth reduction
|
||||
- **Batch Operations**: Group changes for efficient network transfer
|
||||
- **Interest Filtering**: Only sync collections both peers care about
|
||||
|
||||
### Security
|
||||
|
||||
- **Noise Protocol Handshake**: XX pattern with ECDH key exchange
|
||||
- **AES-256 Encryption**: Protect data in transit
|
||||
- **Auth Tokens**: Shared secret validation
|
||||
- **LAN Isolation**: Designed for trusted network environments
|
||||
|
||||
---
|
||||
|
||||
## Use Cases
|
||||
|
||||
### Ideal For
|
||||
|
||||
- **Retail POS Systems** - Terminals syncing inventory and sales across a store
|
||||
- **Office Applications** - Shared task lists, calendars, CRM data on LAN
|
||||
- **Edge Computing** - Distributed sensors and controllers at a facility
|
||||
- **Offline-First Apps** - Work without internet, sync when connected
|
||||
- **Multi-Site Replication** - Keep regional databases in sync (over VPN)
|
||||
- **Existing Database Modernization** - Add P2P sync without rewriting your app
|
||||
|
||||
### Not Designed For
|
||||
|
||||
- **Public internet without HTTPS/VPN** (P2P mesh mode, use ASP.NET Core mode instead)
|
||||
- **Sub-millisecond consistency requirements** (eventual consistency model, typical convergence < 5s)
|
||||
- **Unstructured data** (designed for document collections with keys)
|
||||
- **Append-only event logs** (oplog pruning after 24h retention)
|
||||
|
||||
---
|
||||
|
||||
## Documentation
|
||||
|
||||
### Getting Started
|
||||
|
||||
- **[Sample Application](samples/ZB.MOM.WW.CBDDC.Sample.Console/)** - Complete two-node sync example with interactive CLI
|
||||
- **[Quick Start Guide](#quick-start)** - 5-minute setup
|
||||
- **[Integration Guide](#integrating-with-your-database)** - Add sync to existing DB
|
||||
|
||||
### Concepts
|
||||
|
||||
- **[Architecture & Concepts](docs/architecture.md)** - HLC, Gossip, Vector Clocks, Hash Chains
|
||||
- **[Conflict Resolution](docs/conflict-resolution.md)** - LWW vs Recursive Merge
|
||||
- **[Oplog & CDC](docs/oplog-cdc.md)** - How change tracking works
|
||||
|
||||
### Deployment
|
||||
|
||||
- **[Production Guide](docs/production-hardening.md)** - Configuration, monitoring, best practices
|
||||
- **[Cloud Deployment](docs/cloud-deployment.md)** - ASP.NET Core hosting
|
||||
- **[Deployment Modes](docs/deployment-modes.md)** - Single-cluster deployment strategy
|
||||
|
||||
### API
|
||||
|
||||
- **[API Reference](docs/api-reference.md)** - Complete API documentation
|
||||
- **[Persistence Providers](docs/persistence-providers.md)** - BLite, custom
|
||||
|
||||
---
|
||||
|
||||
## Examples
|
||||
|
||||
See [`samples/ZB.MOM.WW.CBDDC.Sample.Console/`](samples/ZB.MOM.WW.CBDDC.Sample.Console/) for a complete working example with:
|
||||
|
||||
- **Two-node sync** simulation on different ports
|
||||
- **Interactive CLI** for testing operations
|
||||
- **Conflict resolution demo** (switchable LWW/Merge)
|
||||
- **User and TodoList** entities with full CRUD
|
||||
- **Health monitoring** and cache inspection
|
||||
- **Automatic peer discovery** via UDP
|
||||
|
||||
Run two instances:
|
||||
```bash
|
||||
# Terminal 1
|
||||
cd samples/ZB.MOM.WW.CBDDC.Sample.Console
|
||||
dotnet run -- node-1 8580
|
||||
|
||||
# Terminal 2
|
||||
dotnet run -- node-2 8581
|
||||
|
||||
# Create a user on node-1 with command "n"
|
||||
# Watch it appear on node-2 automatically!
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Roadmap
|
||||
|
||||
- [x] Core P2P mesh networking (v0.1.0)
|
||||
- [x] Secure networking (ECDH + AES-256) (v0.6.0)
|
||||
- [x] Conflict resolution strategies (LWW, Recursive Merge) (v0.6.0)
|
||||
- [x] Hash-chain sync with gap recovery (v0.7.0)
|
||||
- [x] Brotli compression (v0.7.0)
|
||||
- [x] Persistence snapshots (v0.8.6)
|
||||
- [x] ASP.NET Core hosting (v0.8.0)
|
||||
- [x] **VectorClockService refactor** (v1.0.0)
|
||||
- [x] **CDC-aware sync** (v1.0.0)
|
||||
- [ ] Query optimization & advanced indexing
|
||||
- [ ] Admin UI / monitoring dashboard
|
||||
- [ ] Mobile support (Xamarin/MAUI)
|
||||
|
||||
---
|
||||
|
||||
## Contributing
|
||||
|
||||
We welcome contributions! CBDDC is open-source and we'd love your help.
|
||||
|
||||
### How to Contribute
|
||||
|
||||
1. **Fork the repository**
|
||||
2. **Create a feature branch** (`git checkout -b feature/amazing-feature`)
|
||||
3. **Make your changes** with clear commit messages
|
||||
4. **Add tests** for new functionality
|
||||
5. **Ensure all tests pass** (`dotnet test`)
|
||||
6. **Submit a Pull Request**
|
||||
|
||||
### Development Setup
|
||||
|
||||
```bash
|
||||
# Clone the repository
|
||||
git clone https://github.com/CBDDC/ZB.MOM.WW.CBDDC.Net.git
|
||||
cd CBDDC.Net
|
||||
|
||||
# Restore dependencies
|
||||
dotnet restore
|
||||
|
||||
# Build
|
||||
dotnet build
|
||||
|
||||
# Run all tests (69 tests)
|
||||
dotnet test
|
||||
|
||||
# Run sample
|
||||
cd samples/ZB.MOM.WW.CBDDC.Sample.Console
|
||||
dotnet run
|
||||
```
|
||||
|
||||
### Areas We Need Help
|
||||
|
||||
- **[Bug] Bug Reports** - Found an issue? Let us know!
|
||||
- **[Docs] Documentation** - Improve guides and examples
|
||||
- **[Feature] Features** - Implement items from the roadmap
|
||||
- **[Test] Testing** - Add integration and performance tests
|
||||
- **[Sample] Samples** - Build example applications
|
||||
|
||||
### Code of Conduct
|
||||
|
||||
Be respectful, inclusive, and constructive. We're all here to learn and build great software together.
|
||||
|
||||
---
|
||||
|
||||
## License
|
||||
|
||||
CBDDC is licensed under the **MIT License**.
|
||||
|
||||
```
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2026 MrDevRobot
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software...
|
||||
```
|
||||
|
||||
See [LICENSE](LICENSE) file for full details.
|
||||
|
||||
---
|
||||
|
||||
## Give it a Star!
|
||||
|
||||
If you find CBDDC useful, please **give it a star** on GitHub! It helps others discover the project and motivates us to keep improving it.
|
||||
|
||||
<div align="center">
|
||||
|
||||
### [Star on GitHub](https://github.com/CBDDC/ZB.MOM.WW.CBDDC.Net)
|
||||
|
||||
**Thank you for your support!**
|
||||
|
||||
**Built with care for the .NET community**
|
||||
|
||||
[Report Bug](https://github.com/CBDDC/ZB.MOM.WW.CBDDC.Net/issues) | [Request Feature](https://github.com/CBDDC/ZB.MOM.WW.CBDDC.Net/issues) | [Discussions](https://github.com/CBDDC/ZB.MOM.WW.CBDDC.Net/discussions)
|
||||
|
||||
</div>
|
||||
Reference in New Issue
Block a user