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

This commit is contained in:
Joseph Doherty
2026-02-20 13:03:21 -05:00
commit 08bfc17218
218 changed files with 33910 additions and 0 deletions

32
.github/ISSUE_TEMPLATE/bug_report.md vendored Executable file
View File

@@ -0,0 +1,32 @@
---
name: Bug report
about: Create a report to help us improve
title: ''
labels: bug
assignees: ''
---
**Describe the bug**
A clear and concise description of what the bug is.
**To Reproduce**
Steps to reproduce the behavior:
1. Go to '...'
2. Click on '...'
3. Scroll down to '...'
4. See error
**Expected behavior**
A clear and concise description of what you expected to happen.
**Screenshots**
If applicable, add screenshots to help explain your problem.
**Environment (please complete the following information):**
- OS: [e.g. Windows, Ubuntu]
- EntglDb Version [e.g. 1.0.0]
- .NET Version [e.g. 8.0]
**Additional context**
Add any other context about the problem here.

20
.github/ISSUE_TEMPLATE/feature_request.md vendored Executable file
View File

@@ -0,0 +1,20 @@
---
name: Feature request
about: Suggest an idea for this project
title: ''
labels: enhancement
assignees: ''
---
**Is your feature request related to a problem? Please describe.**
A clear and concise description of what the problem is. Ex. I'm always frustrated when [...]
**Describe the solution you'd like**
A clear and concise description of what you want to happen.
**Describe alternatives you've considered**
A clear and concise description of any alternative solutions or features you've considered.
**Additional context**
Add any other context or screenshots about the feature request here.

25
.github/pull_request_template.md vendored Executable file
View File

@@ -0,0 +1,25 @@
# Description
Please include a summary of the change and which issue is fixed. Please also include relevant motivation and context.
Fixes # (issue)
## Type of change
- [ ] Bug fix (non-breaking change which fixes an issue)
- [ ] New feature (non-breaking change which adds functionality)
- [ ] Breaking change (fix or feature that would cause existing functionality to not work as expected)
- [ ] Documentation update
## How Has This Been Tested?
Please describe the tests that you ran to verify your changes.
- [ ] Test A
- [ ] Test B
## Checklist:
- [ ] My code follows the style guidelines of this project
- [ ] I have performed a self-review of my own code
- [ ] I have commented my code, particularly in hard-to-understand areas
- [ ] I have made corresponding changes to the documentation
- [ ] My changes generate no new warnings
- [ ] I have added tests that prove my fix is effective or that my feature works
- [ ] New and existing unit tests pass locally with my changes

52
.github/workflows/ci.yml vendored Normal file
View File

@@ -0,0 +1,52 @@
name: CI
on:
pull_request:
push:
branches:
- main
jobs:
verify:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Setup .NET
uses: actions/setup-dotnet@v4
with:
dotnet-version: 10.0.x
- name: Restore
run: dotnet restore CBDDC.slnx
- name: Build (Warnings as Errors)
run: dotnet build CBDDC.slnx --no-restore -warnaserror
- name: Test
run: dotnet test CBDDC.slnx --no-build
- name: Format Check
run: dotnet format CBDDC.slnx --verify-no-changes
- name: Vulnerability Audit
shell: bash
run: |
dotnet package list --project CBDDC.slnx --include-transitive --vulnerable --format json > vuln.json
if grep -q '"severity"' vuln.json; then
echo "Vulnerable packages detected."
cat vuln.json
exit 1
fi
- name: Deprecation Audit
shell: bash
run: |
dotnet package list --project CBDDC.slnx --deprecated > deprecated.txt
if grep -q "has the following deprecated packages" deprecated.txt; then
echo "Deprecated packages detected."
cat deprecated.txt
exit 1
fi

504
.gitignore vendored Executable file
View File

@@ -0,0 +1,504 @@
## Ignore Visual Studio temporary files, build results, and
## files generated by popular Visual Studio add-ons.
##
## Get latest from `dotnet new gitignore`
# dotenv files
.env
# User-specific files
*.rsuser
*.suo
*.user
*.userosscache
*.sln.docstates
# User-specific files (MonoDevelop/Xamarin Studio)
*.userprefs
# Mono auto generated files
mono_crash.*
# Build results
[Dd]ebug/
[Dd]ebugPublic/
[Rr]elease/
[Rr]eleases/
x64/
x86/
[Ww][Ii][Nn]32/
[Aa][Rr][Mm]/
[Aa][Rr][Mm]64/
bld/
[Bb]in/
[Oo]bj/
[Ll]og/
[Ll]ogs/
# Visual Studio 2015/2017 cache/options directory
.vs/
# Uncomment if you have tasks that create the project's static files in wwwroot
#wwwroot/
# Visual Studio 2017 auto generated files
Generated\ Files/
# MSTest test Results
[Tt]est[Rr]esult*/
[Bb]uild[Ll]og.*
# NUnit
*.VisualState.xml
TestResult.xml
nunit-*.xml
# Build Results of an ATL Project
[Dd]ebugPS/
[Rr]eleasePS/
dlldata.c
# Benchmark Results
BenchmarkDotNet.Artifacts/
# .NET
project.lock.json
project.fragment.lock.json
artifacts/
# Tye
.tye/
# ASP.NET Scaffolding
ScaffoldingReadMe.txt
# StyleCop
StyleCopReport.xml
# Files built by Visual Studio
*_i.c
*_p.c
*_h.h
*.ilk
*.meta
*.obj
*.iobj
*.pch
*.pdb
*.ipdb
*.pgc
*.pgd
*.rsp
# but not Directory.Build.rsp, as it configures directory-level build defaults
!Directory.Build.rsp
*.sbr
*.tlb
*.tli
*.tlh
*.tmp
*.tmp_proj
*_wpftmp.csproj
*.log
*.tlog
*.vspscc
*.vssscc
.builds
*.pidb
*.svclog
*.scc
# Chutzpah Test files
_Chutzpah*
# Visual C++ cache files
ipch/
*.aps
*.ncb
*.opendb
*.opensdf
*.sdf
*.cachefile
*.VC.db
*.VC.VC.opendb
# Visual Studio profiler
*.psess
*.vsp
*.vspx
*.sap
# Visual Studio Trace Files
*.e2e
# TFS 2012 Local Workspace
$tf/
# Guidance Automation Toolkit
*.gpState
# ReSharper is a .NET coding add-in
_ReSharper*/
*.[Rr]e[Ss]harper
*.DotSettings.user
# TeamCity is a build add-in
_TeamCity*
# DotCover is a Code Coverage Tool
*.dotCover
# AxoCover is a Code Coverage Tool
.axoCover/*
!.axoCover/settings.json
# Coverlet is a free, cross platform Code Coverage Tool
coverage*.json
coverage*.xml
coverage*.info
# Visual Studio code coverage results
*.coverage
*.coveragexml
# NCrunch
_NCrunch_*
.*crunch*.local.xml
nCrunchTemp_*
# MightyMoose
*.mm.*
AutoTest.Net/
# Web workbench (sass)
.sass-cache/
# Installshield output folder
[Ee]xpress/
# DocProject is a documentation generator add-in
DocProject/buildhelp/
DocProject/Help/*.HxT
DocProject/Help/*.HxC
DocProject/Help/*.hhc
DocProject/Help/*.hhk
DocProject/Help/*.hhp
DocProject/Help/Html2
DocProject/Help/html
# Click-Once directory
publish/
# Publish Web Output
*.[Pp]ublish.xml
*.azurePubxml
# Note: Comment the next line if you want to checkin your web deploy settings,
# but database connection strings (with potential passwords) will be unencrypted
*.pubxml
*.publishproj
# Microsoft Azure Web App publish settings. Comment the next line if you want to
# checkin your Azure Web App publish settings, but sensitive information contained
# in these scripts will be unencrypted
PublishScripts/
# NuGet Packages
*.nupkg
# NuGet Symbol Packages
*.snupkg
# The packages folder can be ignored because of Package Restore
**/[Pp]ackages/*
# except build/, which is used as an MSBuild target.
!**/[Pp]ackages/build/
# Uncomment if necessary however generally it will be regenerated when needed
#!**/[Pp]ackages/repositories.config
# NuGet v3's project.json files produces more ignorable files
*.nuget.props
*.nuget.targets
# Microsoft Azure Build Output
csx/
*.build.csdef
# Microsoft Azure Emulator
ecf/
rcf/
# Windows Store app package directories and files
AppPackages/
BundleArtifacts/
Package.StoreAssociation.xml
_pkginfo.txt
*.appx
*.appxbundle
*.appxupload
# Visual Studio cache files
# files ending in .cache can be ignored
*.[Cc]ache
# but keep track of directories ending in .cache
!?*.[Cc]ache/
# Others
ClientBin/
~$*
*~
*.dbmdl
*.dbproj.schemaview
*.jfm
*.pfx
*.publishsettings
orleans.codegen.cs
# Including strong name files can present a security risk
# (https://github.com/github/gitignore/pull/2483#issue-259490424)
#*.snk
# Since there are multiple workflows, uncomment next line to ignore bower_components
# (https://github.com/github/gitignore/pull/1529#issuecomment-104372622)
#bower_components/
# RIA/Silverlight projects
Generated_Code/
# Backup & report files from converting an old project file
# to a newer Visual Studio version. Backup files are not needed,
# because we have git ;-)
_UpgradeReport_Files/
Backup*/
UpgradeLog*.XML
UpgradeLog*.htm
ServiceFabricBackup/
*.rptproj.bak
# SQL Server files
*.mdf
*.ldf
*.ndf
# Business Intelligence projects
*.rdl.data
*.bim.layout
*.bim_*.settings
*.rptproj.rsuser
*- [Bb]ackup.rdl
*- [Bb]ackup ([0-9]).rdl
*- [Bb]ackup ([0-9][0-9]).rdl
# Microsoft Fakes
FakesAssemblies/
# GhostDoc plugin setting file
*.GhostDoc.xml
# Node.js Tools for Visual Studio
.ntvs_analysis.dat
node_modules/
# Visual Studio 6 build log
*.plg
# Visual Studio 6 workspace options file
*.opt
# Visual Studio 6 auto-generated workspace file (contains which files were open etc.)
*.vbw
# Visual Studio 6 auto-generated project file (contains which files were open etc.)
*.vbp
# Visual Studio 6 workspace and project file (working project files containing files to include in project)
*.dsw
*.dsp
# Visual Studio 6 technical files
*.ncb
*.aps
# Visual Studio LightSwitch build output
**/*.HTMLClient/GeneratedArtifacts
**/*.DesktopClient/GeneratedArtifacts
**/*.DesktopClient/ModelManifest.xml
**/*.Server/GeneratedArtifacts
**/*.Server/ModelManifest.xml
_Pvt_Extensions
# Paket dependency manager
.paket/paket.exe
paket-files/
# FAKE - F# Make
.fake/
# CodeRush personal settings
.cr/personal
# Python Tools for Visual Studio (PTVS)
__pycache__/
*.pyc
# Cake - Uncomment if you are using it
# tools/**
# !tools/packages.config
# Tabs Studio
*.tss
# Telerik's JustMock configuration file
*.jmconfig
# BizTalk build output
*.btp.cs
*.btm.cs
*.odx.cs
*.xsd.cs
# OpenCover UI analysis results
OpenCover/
# Azure Stream Analytics local run output
ASALocalRun/
# MSBuild Binary and Structured Log
*.binlog
# NVidia Nsight GPU debugger configuration file
*.nvuser
# MFractors (Xamarin productivity tool) working folder
.mfractor/
# Local History for Visual Studio
.localhistory/
# Visual Studio History (VSHistory) files
.vshistory/
# BeatPulse healthcheck temp database
healthchecksdb
# Backup folder for Package Reference Convert tool in Visual Studio 2017
MigrationBackup/
# Ionide (cross platform F# VS Code tools) working folder
.ionide/
# Fody - auto-generated XML schema
FodyWeavers.xsd
# VS Code files for those working on multiple tools
.vscode/*
!.vscode/settings.json
!.vscode/tasks.json
!.vscode/launch.json
!.vscode/extensions.json
*.code-workspace
# Local History for Visual Studio Code
.history/
# Windows Installer files from build outputs
*.cab
*.msi
*.msix
*.msm
*.msp
# JetBrains Rider
*.sln.iml
.idea/
##
## Visual studio for Mac
##
# globs
Makefile.in
*.userprefs
*.usertasks
config.make
config.status
aclocal.m4
install-sh
autom4te.cache/
*.tar.gz
tarballs/
test-results/
# content below from: https://github.com/github/gitignore/blob/main/Global/macOS.gitignore
# General
.DS_Store
.AppleDouble
.LSOverride
# Icon must end with two \r
Icon
# Thumbnails
._*
# Files that might appear in the root of a volume
.DocumentRevisions-V100
.fseventsd
.Spotlight-V100
.TemporaryItems
.Trashes
.VolumeIcon.icns
.com.apple.timemachine.donotpresent
# Directories potentially created on remote AFP share
.AppleDB
.AppleDesktop
Network Trash Folder
Temporary Items
.apdisk
# content below from: https://github.com/github/gitignore/blob/main/Global/Windows.gitignore
# Windows thumbnail cache files
Thumbs.db
ehthumbs.db
ehthumbs_vista.db
# Dump file
*.stackdump
# Folder config file
[Dd]esktop.ini
# Recycle Bin used on file shares
$RECYCLE.BIN/
# Windows Installer files
*.cab
*.msi
*.msix
*.msm
*.msp
# Windows shortcuts
*.lnk
# Vim temporary swap files
*.swp
# CBDDC generated artifacts
nupkgs/
*.log
stress_errors.log
stress_errors_node-*.log
# Local sample/test database files (SQLite and sidecar files)
*.db
*.db-shm
*.db-wal
*.sqlite
*.sqlite3
EntglDb-*.db
# VitePress documentation output
docs/node_modules/
docs/.vitepress/cache/
docs/.vitepress/dist/
# Local package restore/build output at repo root
/packages

7
CBDDC-docs-fixed.md Normal file
View File

@@ -0,0 +1,7 @@
# Documentation Analysis Report
Files Scanned: 138
Files With Issues: 0
Total Issues: 0
Status: No documentation issues found

456
CBDDC-docs-issues.md Normal file
View File

@@ -0,0 +1,456 @@
# Documentation Analysis Report
Files Scanned: 138
Files With Issues: 10
Total Issues: 45
## Issues
FILE: /Users/dohertj2/Desktop/CBDDC/src/ZB.MOM.WW.CBDDC.Network/OplogPruneCutoffDecision.cs
LINE: 52
CATEGORY: MissingParam
SEVERITY: Warning
MEMBER: Method
SIGNATURE: WithCutoff(HlcTimestamp retentionCutoff, HlcTimestamp? confirmationCutoff, HlcTimestamp effectiveCutoff, string reason)
MESSAGE: Method 'WithCutoff(HlcTimestamp retentionCutoff, HlcTimestamp? confirmationCutoff, HlcTimestamp effectiveCutoff, string reason)' is missing <param name="reason"> documentation.
---
FILE: /Users/dohertj2/Desktop/CBDDC/src/ZB.MOM.WW.CBDDC.Network/OplogPruneCutoffDecision.cs
LINE: 52
CATEGORY: MissingParam
SEVERITY: Warning
MEMBER: Method
SIGNATURE: WithCutoff(HlcTimestamp retentionCutoff, HlcTimestamp? confirmationCutoff, HlcTimestamp effectiveCutoff, string reason)
MESSAGE: Method 'WithCutoff(HlcTimestamp retentionCutoff, HlcTimestamp? confirmationCutoff, HlcTimestamp effectiveCutoff, string reason)' is missing <param name="effectiveCutoff"> documentation.
---
FILE: /Users/dohertj2/Desktop/CBDDC/src/ZB.MOM.WW.CBDDC.Network/OplogPruneCutoffDecision.cs
LINE: 52
CATEGORY: MissingParam
SEVERITY: Warning
MEMBER: Method
SIGNATURE: WithCutoff(HlcTimestamp retentionCutoff, HlcTimestamp? confirmationCutoff, HlcTimestamp effectiveCutoff, string reason)
MESSAGE: Method 'WithCutoff(HlcTimestamp retentionCutoff, HlcTimestamp? confirmationCutoff, HlcTimestamp effectiveCutoff, string reason)' is missing <param name="confirmationCutoff"> documentation.
---
FILE: /Users/dohertj2/Desktop/CBDDC/src/ZB.MOM.WW.CBDDC.Network/OplogPruneCutoffDecision.cs
LINE: 52
CATEGORY: MissingParam
SEVERITY: Warning
MEMBER: Method
SIGNATURE: WithCutoff(HlcTimestamp retentionCutoff, HlcTimestamp? confirmationCutoff, HlcTimestamp effectiveCutoff, string reason)
MESSAGE: Method 'WithCutoff(HlcTimestamp retentionCutoff, HlcTimestamp? confirmationCutoff, HlcTimestamp effectiveCutoff, string reason)' is missing <param name="retentionCutoff"> documentation.
---
FILE: /Users/dohertj2/Desktop/CBDDC/src/ZB.MOM.WW.CBDDC.Network/OplogPruneCutoffDecision.cs
LINE: 69
CATEGORY: MissingParam
SEVERITY: Warning
MEMBER: Method
SIGNATURE: NoCutoff(HlcTimestamp retentionCutoff, string reason)
MESSAGE: Method 'NoCutoff(HlcTimestamp retentionCutoff, string reason)' is missing <param name="reason"> documentation.
---
FILE: /Users/dohertj2/Desktop/CBDDC/src/ZB.MOM.WW.CBDDC.Network/OplogPruneCutoffDecision.cs
LINE: 69
CATEGORY: MissingParam
SEVERITY: Warning
MEMBER: Method
SIGNATURE: NoCutoff(HlcTimestamp retentionCutoff, string reason)
MESSAGE: Method 'NoCutoff(HlcTimestamp retentionCutoff, string reason)' is missing <param name="retentionCutoff"> documentation.
---
FILE: /Users/dohertj2/Desktop/CBDDC/src/ZB.MOM.WW.CBDDC.Network/Protocol/ProtocolHandler.cs
LINE: 35
CATEGORY: MissingDoc
SEVERITY: Error
MEMBER: Constructor
SIGNATURE: ProtocolHandler(ILogger logger, INetworkTelemetryService? telemetry)
MESSAGE: Constructor 'ProtocolHandler(ILogger logger, INetworkTelemetryService? telemetry)' is missing XML documentation.
---
FILE: /Users/dohertj2/Desktop/CBDDC/src/ZB.MOM.WW.CBDDC.Network/Protocol/ProtocolHandler.cs
LINE: 221
CATEGORY: MissingDoc
SEVERITY: Error
MEMBER: Constructor
SIGNATURE: ForwardingLogger(ILogger inner)
MESSAGE: Constructor 'ForwardingLogger(ILogger inner)' is missing XML documentation.
---
FILE: /Users/dohertj2/Desktop/CBDDC/src/ZB.MOM.WW.CBDDC.Network/Protocol/ProtocolHandler.cs
LINE: 226
CATEGORY: MissingDoc
SEVERITY: Error
MEMBER: Method
SIGNATURE: BeginScope(TState state)
MESSAGE: Method 'BeginScope(TState state)' is missing XML documentation.
---
FILE: /Users/dohertj2/Desktop/CBDDC/src/ZB.MOM.WW.CBDDC.Network/Protocol/ProtocolHandler.cs
LINE: 231
CATEGORY: MissingDoc
SEVERITY: Error
MEMBER: Method
SIGNATURE: IsEnabled(LogLevel logLevel)
MESSAGE: Method 'IsEnabled(LogLevel logLevel)' is missing XML documentation.
---
FILE: /Users/dohertj2/Desktop/CBDDC/src/ZB.MOM.WW.CBDDC.Network/Protocol/ProtocolHandler.cs
LINE: 236
CATEGORY: MissingDoc
SEVERITY: Error
MEMBER: Method
SIGNATURE: Log(LogLevel logLevel, EventId eventId, TState state, Exception? exception, Func<TState, Exception?, string> formatter)
MESSAGE: Method 'Log(LogLevel logLevel, EventId eventId, TState state, Exception? exception, Func<TState, Exception?, string> formatter)' is missing XML documentation.
---
FILE: /Users/dohertj2/Desktop/CBDDC/src/ZB.MOM.WW.CBDDC.Network/SyncOrchestrator.cs
LINE: 252
CATEGORY: MissingDoc
SEVERITY: Error
MEMBER: Method
SIGNATURE: RunMaintenanceIfDueAsync(PeerNodeConfiguration config, DateTime now, CancellationToken token)
MESSAGE: Method 'RunMaintenanceIfDueAsync(PeerNodeConfiguration config, DateTime now, CancellationToken token)' is missing XML documentation.
---
FILE: /Users/dohertj2/Desktop/CBDDC/src/ZB.MOM.WW.CBDDC.Network/SyncOrchestrator.cs
LINE: 529
CATEGORY: MissingDoc
SEVERITY: Error
MEMBER: Method
SIGNATURE: BuildMergedPeerList(IEnumerable<PeerNode> discoveredPeers, IEnumerable<PeerNode> knownPeers, string localNodeId)
MESSAGE: Method 'BuildMergedPeerList(IEnumerable<PeerNode> discoveredPeers, IEnumerable<PeerNode> knownPeers, string localNodeId)' is missing XML documentation.
---
FILE: /Users/dohertj2/Desktop/CBDDC/src/ZB.MOM.WW.CBDDC.Network/SyncOrchestrator.cs
LINE: 542
CATEGORY: MissingDoc
SEVERITY: Error
MEMBER: Method
SIGNATURE: EnsurePeersRegisteredAsync(IEnumerable<PeerNode> peers, string localNodeId, CancellationToken token)
MESSAGE: Method 'EnsurePeersRegisteredAsync(IEnumerable<PeerNode> peers, string localNodeId, CancellationToken token)' is missing XML documentation.
---
FILE: /Users/dohertj2/Desktop/CBDDC/src/ZB.MOM.WW.CBDDC.Network/SyncOrchestrator.cs
LINE: 571
CATEGORY: MissingDoc
SEVERITY: Error
MEMBER: Method
SIGNATURE: AdvanceConfirmationsFromVectorClockAsync(string peerNodeId, VectorClock localVectorClock, VectorClock remoteVectorClock, CancellationToken token)
MESSAGE: Method 'AdvanceConfirmationsFromVectorClockAsync(string peerNodeId, VectorClock localVectorClock, VectorClock remoteVectorClock, CancellationToken token)' is missing XML documentation.
---
FILE: /Users/dohertj2/Desktop/CBDDC/src/ZB.MOM.WW.CBDDC.Network/SyncOrchestrator.cs
LINE: 606
CATEGORY: MissingDoc
SEVERITY: Error
MEMBER: Method
SIGNATURE: AdvanceConfirmationForPushedBatchAsync(string peerNodeId, string sourceNodeId, IReadOnlyCollection<OplogEntry> pushedChanges, CancellationToken token)
MESSAGE: Method 'AdvanceConfirmationForPushedBatchAsync(string peerNodeId, string sourceNodeId, IReadOnlyCollection<OplogEntry> pushedChanges, CancellationToken token)' is missing XML documentation.
---
FILE: /Users/dohertj2/Desktop/CBDDC/src/ZB.MOM.WW.CBDDC.Persistence/BLite/BLiteDocumentStore.cs
LINE: 94
CATEGORY: MissingDoc
SEVERITY: Error
MEMBER: Constructor
SIGNATURE: ForwardingLogger(ILogger inner)
MESSAGE: Constructor 'ForwardingLogger(ILogger inner)' is missing XML documentation.
---
FILE: /Users/dohertj2/Desktop/CBDDC/src/ZB.MOM.WW.CBDDC.Persistence/BLite/BLiteDocumentStore.cs
LINE: 99
CATEGORY: MissingDoc
SEVERITY: Error
MEMBER: Method
SIGNATURE: BeginScope(TState state)
MESSAGE: Method 'BeginScope(TState state)' is missing XML documentation.
---
FILE: /Users/dohertj2/Desktop/CBDDC/src/ZB.MOM.WW.CBDDC.Persistence/BLite/BLiteDocumentStore.cs
LINE: 104
CATEGORY: MissingDoc
SEVERITY: Error
MEMBER: Method
SIGNATURE: IsEnabled(LogLevel logLevel)
MESSAGE: Method 'IsEnabled(LogLevel logLevel)' is missing XML documentation.
---
FILE: /Users/dohertj2/Desktop/CBDDC/src/ZB.MOM.WW.CBDDC.Persistence/BLite/BLiteDocumentStore.cs
LINE: 109
CATEGORY: MissingDoc
SEVERITY: Error
MEMBER: Method
SIGNATURE: Log(LogLevel logLevel, EventId eventId, TState state, Exception? exception, Func<TState, Exception?, string> formatter)
MESSAGE: Method 'Log(LogLevel logLevel, EventId eventId, TState state, Exception? exception, Func<TState, Exception?, string> formatter)' is missing XML documentation.
---
FILE: /Users/dohertj2/Desktop/CBDDC/tests/ZB.MOM.WW.CBDDC.Core.Tests/PeerManagementServiceTests.cs
LINE: 8
CATEGORY: MissingDoc
SEVERITY: Error
MEMBER: Method
SIGNATURE: RemovePeerTrackingAsync_WhenRemoveRemoteConfigTrue_RemovesTrackingAndRemoteConfig()
MESSAGE: Method 'RemovePeerTrackingAsync_WhenRemoveRemoteConfigTrue_RemovesTrackingAndRemoteConfig()' is missing XML documentation.
---
FILE: /Users/dohertj2/Desktop/CBDDC/tests/ZB.MOM.WW.CBDDC.Core.Tests/PeerManagementServiceTests.cs
LINE: 22
CATEGORY: MissingDoc
SEVERITY: Error
MEMBER: Method
SIGNATURE: RemovePeerTrackingAsync_WhenRemoveRemoteConfigFalse_RemovesTrackingOnly()
MESSAGE: Method 'RemovePeerTrackingAsync_WhenRemoveRemoteConfigFalse_RemovesTrackingOnly()' is missing XML documentation.
---
FILE: /Users/dohertj2/Desktop/CBDDC/tests/ZB.MOM.WW.CBDDC.Core.Tests/PeerManagementServiceTests.cs
LINE: 35
CATEGORY: MissingDoc
SEVERITY: Error
MEMBER: Method
SIGNATURE: RemoveRemotePeerAsync_DelegatesToTrackingRemovalWithRemoteConfig()
MESSAGE: Method 'RemoveRemotePeerAsync_DelegatesToTrackingRemovalWithRemoteConfig()' is missing XML documentation.
---
FILE: /Users/dohertj2/Desktop/CBDDC/tests/ZB.MOM.WW.CBDDC.Core.Tests/PeerManagementServiceTests.cs
LINE: 49
CATEGORY: MissingDoc
SEVERITY: Error
MEMBER: Method
SIGNATURE: RemovePeerTrackingAsync_WhenNodeIdInvalid_ThrowsArgumentException()
MESSAGE: Method 'RemovePeerTrackingAsync_WhenNodeIdInvalid_ThrowsArgumentException()' is missing XML documentation.
---
FILE: /Users/dohertj2/Desktop/CBDDC/tests/ZB.MOM.WW.CBDDC.Hosting.Tests/CBDDCHealthCheckTests.cs
LINE: 11
CATEGORY: MissingDoc
SEVERITY: Error
MEMBER: Method
SIGNATURE: CheckHealthAsync_WhenPersistenceOkAndPeersWithinLagThreshold_ReturnsHealthyWithPayload()
MESSAGE: Method 'CheckHealthAsync_WhenPersistenceOkAndPeersWithinLagThreshold_ReturnsHealthyWithPayload()' is missing XML documentation.
---
FILE: /Users/dohertj2/Desktop/CBDDC/tests/ZB.MOM.WW.CBDDC.Hosting.Tests/CBDDCHealthCheckTests.cs
LINE: 68
CATEGORY: MissingDoc
SEVERITY: Error
MEMBER: Method
SIGNATURE: CheckHealthAsync_WhenPeersLaggingOrUnconfirmed_ReturnsDegradedWithPayload()
MESSAGE: Method 'CheckHealthAsync_WhenPeersLaggingOrUnconfirmed_ReturnsDegradedWithPayload()' is missing XML documentation.
---
FILE: /Users/dohertj2/Desktop/CBDDC/tests/ZB.MOM.WW.CBDDC.Hosting.Tests/CBDDCHealthCheckTests.cs
LINE: 126
CATEGORY: MissingDoc
SEVERITY: Error
MEMBER: Method
SIGNATURE: CheckHealthAsync_WhenCriticalLagBreached_ReturnsUnhealthyWithPayload()
MESSAGE: Method 'CheckHealthAsync_WhenCriticalLagBreached_ReturnsUnhealthyWithPayload()' is missing XML documentation.
---
FILE: /Users/dohertj2/Desktop/CBDDC/tests/ZB.MOM.WW.CBDDC.Hosting.Tests/CBDDCHealthCheckTests.cs
LINE: 161
CATEGORY: MissingDoc
SEVERITY: Error
MEMBER: Method
SIGNATURE: CheckHealthAsync_WhenPeerHasMultipleSourceConfirmations_UsesWorstCaseLag()
MESSAGE: Method 'CheckHealthAsync_WhenPeerHasMultipleSourceConfirmations_UsesWorstCaseLag()' is missing XML documentation.
---
FILE: /Users/dohertj2/Desktop/CBDDC/tests/ZB.MOM.WW.CBDDC.Hosting.Tests/CBDDCHealthCheckTests.cs
LINE: 205
CATEGORY: MissingDoc
SEVERITY: Error
MEMBER: Method
SIGNATURE: CheckHealthAsync_WhenStoreThrows_ReturnsUnhealthy()
MESSAGE: Method 'CheckHealthAsync_WhenStoreThrows_ReturnsUnhealthy()' is missing XML documentation.
---
FILE: /Users/dohertj2/Desktop/CBDDC/tests/ZB.MOM.WW.CBDDC.Network.Tests/SnapshotReconnectRegressionTests.cs
LINE: 32
CATEGORY: MissingParam
SEVERITY: Warning
MEMBER: Constructor
SIGNATURE: TestableSyncOrchestrator(IDiscoveryService discovery, IOplogStore oplogStore, IDocumentStore documentStore, ISnapshotMetadataStore snapshotMetadataStore, ISnapshotService snapshotService, IPeerNodeConfigurationProvider peerNodeConfigurationProvider, IPeerOplogConfirmationStore peerOplogConfirmationStore)
MESSAGE: Constructor 'TestableSyncOrchestrator(IDiscoveryService discovery, IOplogStore oplogStore, IDocumentStore documentStore, ISnapshotMetadataStore snapshotMetadataStore, ISnapshotService snapshotService, IPeerNodeConfigurationProvider peerNodeConfigurationProvider, IPeerOplogConfirmationStore peerOplogConfirmationStore)' is missing <param name="peerOplogConfirmationStore"> documentation.
---
FILE: /Users/dohertj2/Desktop/CBDDC/tests/ZB.MOM.WW.CBDDC.Network.Tests/SyncOrchestratorConfirmationTests.cs
LINE: 15
CATEGORY: MissingDoc
SEVERITY: Error
MEMBER: Method
SIGNATURE: EnsurePeersRegisteredAsync_ShouldRegisterMergedPeers_AndSkipLocalNode()
MESSAGE: Method 'EnsurePeersRegisteredAsync_ShouldRegisterMergedPeers_AndSkipLocalNode()' is missing XML documentation.
---
FILE: /Users/dohertj2/Desktop/CBDDC/tests/ZB.MOM.WW.CBDDC.Network.Tests/SyncOrchestratorConfirmationTests.cs
LINE: 59
CATEGORY: MissingDoc
SEVERITY: Error
MEMBER: Method
SIGNATURE: EnsurePeersRegisteredAsync_WhenNewNodeJoins_ShouldAutoRegisterJoinedNode()
MESSAGE: Method 'EnsurePeersRegisteredAsync_WhenNewNodeJoins_ShouldAutoRegisterJoinedNode()' is missing XML documentation.
---
FILE: /Users/dohertj2/Desktop/CBDDC/tests/ZB.MOM.WW.CBDDC.Network.Tests/SyncOrchestratorConfirmationTests.cs
LINE: 94
CATEGORY: MissingDoc
SEVERITY: Error
MEMBER: Method
SIGNATURE: AdvanceConfirmationsFromVectorClockAsync_ShouldAdvanceOnlyForRemoteAtOrAhead()
MESSAGE: Method 'AdvanceConfirmationsFromVectorClockAsync_ShouldAdvanceOnlyForRemoteAtOrAhead()' is missing XML documentation.
---
FILE: /Users/dohertj2/Desktop/CBDDC/tests/ZB.MOM.WW.CBDDC.Network.Tests/SyncOrchestratorConfirmationTests.cs
LINE: 156
CATEGORY: MissingDoc
SEVERITY: Error
MEMBER: Method
SIGNATURE: AdvanceConfirmationForPushedBatchAsync_ShouldUseMaxTimestampAndHash()
MESSAGE: Method 'AdvanceConfirmationForPushedBatchAsync_ShouldUseMaxTimestampAndHash()' is missing XML documentation.
---
FILE: /Users/dohertj2/Desktop/CBDDC/tests/ZB.MOM.WW.CBDDC.Network.Tests/SyncOrchestratorConfirmationTests.cs
LINE: 180
CATEGORY: MissingDoc
SEVERITY: Error
MEMBER: Method
SIGNATURE: AdvanceConfirmationForPushedBatchAsync_ShouldSkipEmptyBatch()
MESSAGE: Method 'AdvanceConfirmationForPushedBatchAsync_ShouldSkipEmptyBatch()' is missing XML documentation.
---
FILE: /Users/dohertj2/Desktop/CBDDC/tests/ZB.MOM.WW.CBDDC.Network.Tests/SyncOrchestratorMaintenancePruningTests.cs
LINE: 14
CATEGORY: MissingDoc
SEVERITY: Error
MEMBER: Method
SIGNATURE: CalculateEffectiveCutoffAsync_MixedPeerStates_ShouldUseSafestConfirmationAcrossPeers()
MESSAGE: Method 'CalculateEffectiveCutoffAsync_MixedPeerStates_ShouldUseSafestConfirmationAcrossPeers()' is missing XML documentation.
---
FILE: /Users/dohertj2/Desktop/CBDDC/tests/ZB.MOM.WW.CBDDC.Network.Tests/SyncOrchestratorMaintenancePruningTests.cs
LINE: 62
CATEGORY: MissingDoc
SEVERITY: Error
MEMBER: Method
SIGNATURE: CalculateEffectiveCutoffAsync_RemovingPeerFromTracking_ShouldImmediatelyRestoreEligibility()
MESSAGE: Method 'CalculateEffectiveCutoffAsync_RemovingPeerFromTracking_ShouldImmediatelyRestoreEligibility()' is missing XML documentation.
---
FILE: /Users/dohertj2/Desktop/CBDDC/tests/ZB.MOM.WW.CBDDC.Network.Tests/SyncOrchestratorMaintenancePruningTests.cs
LINE: 108
CATEGORY: MissingDoc
SEVERITY: Error
MEMBER: Method
SIGNATURE: RunMaintenanceIfDueAsync_TwoNode_ShouldNotPruneBeforePeerConfirmation()
MESSAGE: Method 'RunMaintenanceIfDueAsync_TwoNode_ShouldNotPruneBeforePeerConfirmation()' is missing XML documentation.
---
FILE: /Users/dohertj2/Desktop/CBDDC/tests/ZB.MOM.WW.CBDDC.Network.Tests/SyncOrchestratorMaintenancePruningTests.cs
LINE: 138
CATEGORY: MissingDoc
SEVERITY: Error
MEMBER: Method
SIGNATURE: RunMaintenanceIfDueAsync_TwoNode_ShouldPruneAfterPeerConfirmation()
MESSAGE: Method 'RunMaintenanceIfDueAsync_TwoNode_ShouldPruneAfterPeerConfirmation()' is missing XML documentation.
---
FILE: /Users/dohertj2/Desktop/CBDDC/tests/ZB.MOM.WW.CBDDC.Network.Tests/SyncOrchestratorMaintenancePruningTests.cs
LINE: 184
CATEGORY: MissingDoc
SEVERITY: Error
MEMBER: Method
SIGNATURE: RunMaintenanceIfDueAsync_DeprecatedNodeRemoval_ShouldUnblockPruning()
MESSAGE: Method 'RunMaintenanceIfDueAsync_DeprecatedNodeRemoval_ShouldUnblockPruning()' is missing XML documentation.
---
FILE: /Users/dohertj2/Desktop/CBDDC/tests/ZB.MOM.WW.CBDDC.Sample.Console.Tests/PeerOplogConfirmationStoreTests.cs
LINE: 14
CATEGORY: MissingDoc
SEVERITY: Error
MEMBER: Constructor
SIGNATURE: PeerOplogConfirmationStoreTests()
MESSAGE: Constructor 'PeerOplogConfirmationStoreTests()' is missing XML documentation.
---
FILE: /Users/dohertj2/Desktop/CBDDC/tests/ZB.MOM.WW.CBDDC.Sample.Console.Tests/PeerOplogConfirmationStoreTests.cs
LINE: 23
CATEGORY: MissingDoc
SEVERITY: Error
MEMBER: Method
SIGNATURE: EnsurePeerRegisteredAsync_IsIdempotent()
MESSAGE: Method 'EnsurePeerRegisteredAsync_IsIdempotent()' is missing XML documentation.
---
FILE: /Users/dohertj2/Desktop/CBDDC/tests/ZB.MOM.WW.CBDDC.Sample.Console.Tests/PeerOplogConfirmationStoreTests.cs
LINE: 37
CATEGORY: MissingDoc
SEVERITY: Error
MEMBER: Method
SIGNATURE: ConfirmationStore_CrudFlow_Works()
MESSAGE: Method 'ConfirmationStore_CrudFlow_Works()' is missing XML documentation.
---
FILE: /Users/dohertj2/Desktop/CBDDC/tests/ZB.MOM.WW.CBDDC.Sample.Console.Tests/PeerOplogConfirmationStoreTests.cs
LINE: 67
CATEGORY: MissingDoc
SEVERITY: Error
MEMBER: Method
SIGNATURE: RemovePeerTrackingAsync_DeactivatesPeerTracking()
MESSAGE: Method 'RemovePeerTrackingAsync_DeactivatesPeerTracking()' is missing XML documentation.
---
FILE: /Users/dohertj2/Desktop/CBDDC/tests/ZB.MOM.WW.CBDDC.Sample.Console.Tests/PeerOplogConfirmationStoreTests.cs
LINE: 87
CATEGORY: MissingDoc
SEVERITY: Error
MEMBER: Method
SIGNATURE: Dispose()
MESSAGE: Method 'Dispose()' is missing XML documentation.

19
CBDDC.Libs.slnx Normal file
View File

@@ -0,0 +1,19 @@
<Solution>
<Configurations>
<Platform Name="Any CPU" />
<Platform Name="x64" />
<Platform Name="x86" />
</Configurations>
<Folder Name="/src/">
<Project Path="src/ZB.MOM.WW.CBDDC.Hosting/ZB.MOM.WW.CBDDC.Hosting.csproj" />
<Project Path="src/ZB.MOM.WW.CBDDC.Core/ZB.MOM.WW.CBDDC.Core.csproj" />
<Project Path="src/ZB.MOM.WW.CBDDC.Network/ZB.MOM.WW.CBDDC.Network.csproj" />
<Project Path="src/ZB.MOM.WW.CBDDC.Persistence/ZB.MOM.WW.CBDDC.Persistence.csproj" />
</Folder>
<Folder Name="/tests/">
<Project Path="tests/ZB.MOM.WW.CBDDC.Core.Tests/ZB.MOM.WW.CBDDC.Core.Tests.csproj" />
<Project Path="tests/ZB.MOM.WW.CBDDC.Hosting.Tests/ZB.MOM.WW.CBDDC.Hosting.Tests.csproj" />
<Project Path="tests/ZB.MOM.WW.CBDDC.Network.Tests/ZB.MOM.WW.CBDDC.Network.Tests.csproj" />
<Project Path="tests/ZB.MOM.WW.CBDDC.Sample.Console.Tests/ZB.MOM.WW.CBDDC.Sample.Console.Tests.csproj" />
</Folder>
</Solution>

23
CBDDC.slnx Normal file
View File

@@ -0,0 +1,23 @@
<Solution>
<Configurations>
<Platform Name="Any CPU" />
<Platform Name="x64" />
<Platform Name="x86" />
</Configurations>
<Folder Name="/samples/">
<Project Path="samples/ZB.MOM.WW.CBDDC.Sample.Console/ZB.MOM.WW.CBDDC.Sample.Console.csproj" />
</Folder>
<Folder Name="/src/">
<Project Path="src/ZB.MOM.WW.CBDDC.Hosting/ZB.MOM.WW.CBDDC.Hosting.csproj" />
<Project Path="src/ZB.MOM.WW.CBDDC.Core/ZB.MOM.WW.CBDDC.Core.csproj" />
<Project Path="src/ZB.MOM.WW.CBDDC.Network/ZB.MOM.WW.CBDDC.Network.csproj" />
<Project Path="src/ZB.MOM.WW.CBDDC.Persistence/ZB.MOM.WW.CBDDC.Persistence.csproj" />
</Folder>
<Folder Name="/tests/">
<Project Path="tests/ZB.MOM.WW.CBDDC.Core.Tests/ZB.MOM.WW.CBDDC.Core.Tests.csproj" />
<Project Path="tests/ZB.MOM.WW.CBDDC.E2E.Tests/ZB.MOM.WW.CBDDC.E2E.Tests.csproj" />
<Project Path="tests/ZB.MOM.WW.CBDDC.Hosting.Tests/ZB.MOM.WW.CBDDC.Hosting.Tests.csproj" />
<Project Path="tests/ZB.MOM.WW.CBDDC.Network.Tests/ZB.MOM.WW.CBDDC.Network.Tests.csproj" />
<Project Path="tests/ZB.MOM.WW.CBDDC.Sample.Console.Tests/ZB.MOM.WW.CBDDC.Sample.Console.Tests.csproj" />
</Folder>
</Solution>

7
Core-docs-fixed.md Normal file
View File

@@ -0,0 +1,7 @@
# Documentation Analysis Report
Files Scanned: 43
Files With Issues: 0
Total Issues: 0
Status: No documentation issues found

1896
Core-docs-issues.md Normal file

File diff suppressed because it is too large Load Diff

7
Directory.Build.props Normal file
View File

@@ -0,0 +1,7 @@
<Project>
<PropertyGroup>
<EnableNETAnalyzers>true</EnableNETAnalyzers>
<AnalysisLevel>latest</AnalysisLevel>
<EnforceCodeStyleInBuild>true</EnforceCodeStyleInBuild>
</PropertyGroup>
</Project>

7
Network-docs-fixed.md Normal file
View File

@@ -0,0 +1,7 @@
# Documentation Analysis Report
Files Scanned: 29
Files With Issues: 0
Total Issues: 0
Status: No documentation issues found

1176
Network-docs-issues.md Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,7 @@
# Documentation Analysis Report
Files Scanned: 8
Files With Issues: 0
Total Issues: 0
Status: No documentation issues found

546
Persistence-docs-issues.md Normal file
View File

@@ -0,0 +1,546 @@
# Documentation Analysis Report
Files Scanned: 8
Files With Issues: 5
Total Issues: 54
## Issues
FILE: /Users/dohertj2/Desktop/CBDDC/src/ZB.MOM.WW.CBDDC.Persistence/NodeCacheEntry.cs
LINE: 7
CATEGORY: MissingDoc
SEVERITY: Error
MEMBER: Property
SIGNATURE: Timestamp
MESSAGE: Property 'Timestamp' is missing XML documentation
---
FILE: /Users/dohertj2/Desktop/CBDDC/src/ZB.MOM.WW.CBDDC.Persistence/NodeCacheEntry.cs
LINE: 8
CATEGORY: MissingDoc
SEVERITY: Error
MEMBER: Property
SIGNATURE: Hash
MESSAGE: Property 'Hash' is missing XML documentation
---
FILE: /Users/dohertj2/Desktop/CBDDC/src/ZB.MOM.WW.CBDDC.Persistence/OplogStore.cs
LINE: 22
CATEGORY: MissingDoc
SEVERITY: Error
MEMBER: Event
SIGNATURE: ChangesApplied
MESSAGE: Event 'ChangesApplied' is missing XML documentation.
---
FILE: /Users/dohertj2/Desktop/CBDDC/src/ZB.MOM.WW.CBDDC.Persistence/OplogStore.cs
LINE: 24
CATEGORY: MissingDoc
SEVERITY: Error
MEMBER: Method
SIGNATURE: OnChangesApplied(IEnumerable<OplogEntry> appliedEntries)
MESSAGE: Method 'OnChangesApplied(IEnumerable<OplogEntry> appliedEntries)' is missing XML documentation.
---
FILE: /Users/dohertj2/Desktop/CBDDC/src/ZB.MOM.WW.CBDDC.Persistence/OplogStore.cs
LINE: 29
CATEGORY: MissingDoc
SEVERITY: Error
MEMBER: Constructor
SIGNATURE: OplogStore(IDocumentStore documentStore, IConflictResolver conflictResolver, IVectorClockService vectorClockService, ISnapshotMetadataStore? snapshotMetadataStore)
MESSAGE: Constructor 'OplogStore(IDocumentStore documentStore, IConflictResolver conflictResolver, IVectorClockService vectorClockService, ISnapshotMetadataStore? snapshotMetadataStore)' is missing XML documentation.
---
FILE: /Users/dohertj2/Desktop/CBDDC/src/ZB.MOM.WW.CBDDC.Persistence/OplogStore.cs
LINE: 209
CATEGORY: MissingDoc
SEVERITY: Error
MEMBER: Method
SIGNATURE: DropAsync(CancellationToken cancellationToken)
MESSAGE: Method 'DropAsync(CancellationToken cancellationToken)' is missing XML documentation.
---
FILE: /Users/dohertj2/Desktop/CBDDC/src/ZB.MOM.WW.CBDDC.Persistence/OplogStore.cs
LINE: 211
CATEGORY: MissingDoc
SEVERITY: Error
MEMBER: Method
SIGNATURE: ExportAsync(CancellationToken cancellationToken)
MESSAGE: Method 'ExportAsync(CancellationToken cancellationToken)' is missing XML documentation.
---
FILE: /Users/dohertj2/Desktop/CBDDC/src/ZB.MOM.WW.CBDDC.Persistence/OplogStore.cs
LINE: 213
CATEGORY: MissingDoc
SEVERITY: Error
MEMBER: Method
SIGNATURE: ImportAsync(IEnumerable<OplogEntry> items, CancellationToken cancellationToken)
MESSAGE: Method 'ImportAsync(IEnumerable<OplogEntry> items, CancellationToken cancellationToken)' is missing XML documentation.
---
FILE: /Users/dohertj2/Desktop/CBDDC/src/ZB.MOM.WW.CBDDC.Persistence/OplogStore.cs
LINE: 215
CATEGORY: MissingDoc
SEVERITY: Error
MEMBER: Method
SIGNATURE: MergeAsync(IEnumerable<OplogEntry> items, CancellationToken cancellationToken)
MESSAGE: Method 'MergeAsync(IEnumerable<OplogEntry> items, CancellationToken cancellationToken)' is missing XML documentation.
---
FILE: /Users/dohertj2/Desktop/CBDDC/src/ZB.MOM.WW.CBDDC.Persistence/PeerConfigurationStore.cs
LINE: 23
CATEGORY: MissingDoc
SEVERITY: Error
MEMBER: Method
SIGNATURE: DropAsync(CancellationToken cancellationToken)
MESSAGE: Method 'DropAsync(CancellationToken cancellationToken)' is missing XML documentation.
---
FILE: /Users/dohertj2/Desktop/CBDDC/src/ZB.MOM.WW.CBDDC.Persistence/PeerConfigurationStore.cs
LINE: 25
CATEGORY: MissingDoc
SEVERITY: Error
MEMBER: Method
SIGNATURE: ExportAsync(CancellationToken cancellationToken)
MESSAGE: Method 'ExportAsync(CancellationToken cancellationToken)' is missing XML documentation.
---
FILE: /Users/dohertj2/Desktop/CBDDC/src/ZB.MOM.WW.CBDDC.Persistence/PeerConfigurationStore.cs
LINE: 27
CATEGORY: MissingDoc
SEVERITY: Error
MEMBER: Method
SIGNATURE: ImportAsync(IEnumerable<RemotePeerConfiguration> items, CancellationToken cancellationToken)
MESSAGE: Method 'ImportAsync(IEnumerable<RemotePeerConfiguration> items, CancellationToken cancellationToken)' is missing XML documentation.
---
FILE: /Users/dohertj2/Desktop/CBDDC/src/ZB.MOM.WW.CBDDC.Persistence/PeerConfigurationStore.cs
LINE: 35
CATEGORY: MissingDoc
SEVERITY: Error
MEMBER: Method
SIGNATURE: MergeAsync(IEnumerable<RemotePeerConfiguration> items, CancellationToken cancellationToken)
MESSAGE: Method 'MergeAsync(IEnumerable<RemotePeerConfiguration> items, CancellationToken cancellationToken)' is missing XML documentation.
---
FILE: /Users/dohertj2/Desktop/CBDDC/src/ZB.MOM.WW.CBDDC.Persistence/Snapshot/SnapshotDto.cs
LINE: 10
CATEGORY: MissingDoc
SEVERITY: Error
MEMBER: Property
SIGNATURE: Version
MESSAGE: Property 'Version' is missing XML documentation
---
FILE: /Users/dohertj2/Desktop/CBDDC/src/ZB.MOM.WW.CBDDC.Persistence/Snapshot/SnapshotDto.cs
LINE: 11
CATEGORY: MissingDoc
SEVERITY: Error
MEMBER: Property
SIGNATURE: CreatedAt
MESSAGE: Property 'CreatedAt' is missing XML documentation
---
FILE: /Users/dohertj2/Desktop/CBDDC/src/ZB.MOM.WW.CBDDC.Persistence/Snapshot/SnapshotDto.cs
LINE: 12
CATEGORY: MissingDoc
SEVERITY: Error
MEMBER: Property
SIGNATURE: NodeId
MESSAGE: Property 'NodeId' is missing XML documentation
---
FILE: /Users/dohertj2/Desktop/CBDDC/src/ZB.MOM.WW.CBDDC.Persistence/Snapshot/SnapshotDto.cs
LINE: 13
CATEGORY: MissingDoc
SEVERITY: Error
MEMBER: Property
SIGNATURE: Documents
MESSAGE: Property 'Documents' is missing XML documentation
---
FILE: /Users/dohertj2/Desktop/CBDDC/src/ZB.MOM.WW.CBDDC.Persistence/Snapshot/SnapshotDto.cs
LINE: 14
CATEGORY: MissingDoc
SEVERITY: Error
MEMBER: Property
SIGNATURE: Oplog
MESSAGE: Property 'Oplog' is missing XML documentation
---
FILE: /Users/dohertj2/Desktop/CBDDC/src/ZB.MOM.WW.CBDDC.Persistence/Snapshot/SnapshotDto.cs
LINE: 15
CATEGORY: MissingDoc
SEVERITY: Error
MEMBER: Property
SIGNATURE: SnapshotMetadata
MESSAGE: Property 'SnapshotMetadata' is missing XML documentation
---
FILE: /Users/dohertj2/Desktop/CBDDC/src/ZB.MOM.WW.CBDDC.Persistence/Snapshot/SnapshotDto.cs
LINE: 16
CATEGORY: MissingDoc
SEVERITY: Error
MEMBER: Property
SIGNATURE: RemotePeers
MESSAGE: Property 'RemotePeers' is missing XML documentation
---
FILE: /Users/dohertj2/Desktop/CBDDC/src/ZB.MOM.WW.CBDDC.Persistence/Snapshot/SnapshotDto.cs
LINE: 21
CATEGORY: MissingDoc
SEVERITY: Error
MEMBER: Property
SIGNATURE: Collection
MESSAGE: Property 'Collection' is missing XML documentation
---
FILE: /Users/dohertj2/Desktop/CBDDC/src/ZB.MOM.WW.CBDDC.Persistence/Snapshot/SnapshotDto.cs
LINE: 22
CATEGORY: MissingDoc
SEVERITY: Error
MEMBER: Property
SIGNATURE: Key
MESSAGE: Property 'Key' is missing XML documentation
---
FILE: /Users/dohertj2/Desktop/CBDDC/src/ZB.MOM.WW.CBDDC.Persistence/Snapshot/SnapshotDto.cs
LINE: 23
CATEGORY: MissingDoc
SEVERITY: Error
MEMBER: Property
SIGNATURE: JsonData
MESSAGE: Property 'JsonData' is missing XML documentation
---
FILE: /Users/dohertj2/Desktop/CBDDC/src/ZB.MOM.WW.CBDDC.Persistence/Snapshot/SnapshotDto.cs
LINE: 24
CATEGORY: MissingDoc
SEVERITY: Error
MEMBER: Property
SIGNATURE: IsDeleted
MESSAGE: Property 'IsDeleted' is missing XML documentation
---
FILE: /Users/dohertj2/Desktop/CBDDC/src/ZB.MOM.WW.CBDDC.Persistence/Snapshot/SnapshotDto.cs
LINE: 25
CATEGORY: MissingDoc
SEVERITY: Error
MEMBER: Property
SIGNATURE: HlcWall
MESSAGE: Property 'HlcWall' is missing XML documentation
---
FILE: /Users/dohertj2/Desktop/CBDDC/src/ZB.MOM.WW.CBDDC.Persistence/Snapshot/SnapshotDto.cs
LINE: 26
CATEGORY: MissingDoc
SEVERITY: Error
MEMBER: Property
SIGNATURE: HlcLogic
MESSAGE: Property 'HlcLogic' is missing XML documentation
---
FILE: /Users/dohertj2/Desktop/CBDDC/src/ZB.MOM.WW.CBDDC.Persistence/Snapshot/SnapshotDto.cs
LINE: 27
CATEGORY: MissingDoc
SEVERITY: Error
MEMBER: Property
SIGNATURE: HlcNode
MESSAGE: Property 'HlcNode' is missing XML documentation
---
FILE: /Users/dohertj2/Desktop/CBDDC/src/ZB.MOM.WW.CBDDC.Persistence/Snapshot/SnapshotDto.cs
LINE: 32
CATEGORY: MissingDoc
SEVERITY: Error
MEMBER: Property
SIGNATURE: Collection
MESSAGE: Property 'Collection' is missing XML documentation
---
FILE: /Users/dohertj2/Desktop/CBDDC/src/ZB.MOM.WW.CBDDC.Persistence/Snapshot/SnapshotDto.cs
LINE: 33
CATEGORY: MissingDoc
SEVERITY: Error
MEMBER: Property
SIGNATURE: Key
MESSAGE: Property 'Key' is missing XML documentation
---
FILE: /Users/dohertj2/Desktop/CBDDC/src/ZB.MOM.WW.CBDDC.Persistence/Snapshot/SnapshotDto.cs
LINE: 34
CATEGORY: MissingDoc
SEVERITY: Error
MEMBER: Property
SIGNATURE: Operation
MESSAGE: Property 'Operation' is missing XML documentation
---
FILE: /Users/dohertj2/Desktop/CBDDC/src/ZB.MOM.WW.CBDDC.Persistence/Snapshot/SnapshotDto.cs
LINE: 35
CATEGORY: MissingDoc
SEVERITY: Error
MEMBER: Property
SIGNATURE: JsonData
MESSAGE: Property 'JsonData' is missing XML documentation
---
FILE: /Users/dohertj2/Desktop/CBDDC/src/ZB.MOM.WW.CBDDC.Persistence/Snapshot/SnapshotDto.cs
LINE: 36
CATEGORY: MissingDoc
SEVERITY: Error
MEMBER: Property
SIGNATURE: HlcWall
MESSAGE: Property 'HlcWall' is missing XML documentation
---
FILE: /Users/dohertj2/Desktop/CBDDC/src/ZB.MOM.WW.CBDDC.Persistence/Snapshot/SnapshotDto.cs
LINE: 37
CATEGORY: MissingDoc
SEVERITY: Error
MEMBER: Property
SIGNATURE: HlcLogic
MESSAGE: Property 'HlcLogic' is missing XML documentation
---
FILE: /Users/dohertj2/Desktop/CBDDC/src/ZB.MOM.WW.CBDDC.Persistence/Snapshot/SnapshotDto.cs
LINE: 38
CATEGORY: MissingDoc
SEVERITY: Error
MEMBER: Property
SIGNATURE: HlcNode
MESSAGE: Property 'HlcNode' is missing XML documentation
---
FILE: /Users/dohertj2/Desktop/CBDDC/src/ZB.MOM.WW.CBDDC.Persistence/Snapshot/SnapshotDto.cs
LINE: 39
CATEGORY: MissingDoc
SEVERITY: Error
MEMBER: Property
SIGNATURE: Hash
MESSAGE: Property 'Hash' is missing XML documentation
---
FILE: /Users/dohertj2/Desktop/CBDDC/src/ZB.MOM.WW.CBDDC.Persistence/Snapshot/SnapshotDto.cs
LINE: 40
CATEGORY: MissingDoc
SEVERITY: Error
MEMBER: Property
SIGNATURE: PreviousHash
MESSAGE: Property 'PreviousHash' is missing XML documentation
---
FILE: /Users/dohertj2/Desktop/CBDDC/src/ZB.MOM.WW.CBDDC.Persistence/Snapshot/SnapshotDto.cs
LINE: 45
CATEGORY: MissingDoc
SEVERITY: Error
MEMBER: Property
SIGNATURE: NodeId
MESSAGE: Property 'NodeId' is missing XML documentation
---
FILE: /Users/dohertj2/Desktop/CBDDC/src/ZB.MOM.WW.CBDDC.Persistence/Snapshot/SnapshotDto.cs
LINE: 46
CATEGORY: MissingDoc
SEVERITY: Error
MEMBER: Property
SIGNATURE: HlcWall
MESSAGE: Property 'HlcWall' is missing XML documentation
---
FILE: /Users/dohertj2/Desktop/CBDDC/src/ZB.MOM.WW.CBDDC.Persistence/Snapshot/SnapshotDto.cs
LINE: 47
CATEGORY: MissingDoc
SEVERITY: Error
MEMBER: Property
SIGNATURE: HlcLogic
MESSAGE: Property 'HlcLogic' is missing XML documentation
---
FILE: /Users/dohertj2/Desktop/CBDDC/src/ZB.MOM.WW.CBDDC.Persistence/Snapshot/SnapshotDto.cs
LINE: 48
CATEGORY: MissingDoc
SEVERITY: Error
MEMBER: Property
SIGNATURE: Hash
MESSAGE: Property 'Hash' is missing XML documentation
---
FILE: /Users/dohertj2/Desktop/CBDDC/src/ZB.MOM.WW.CBDDC.Persistence/Snapshot/SnapshotDto.cs
LINE: 53
CATEGORY: MissingDoc
SEVERITY: Error
MEMBER: Property
SIGNATURE: NodeId
MESSAGE: Property 'NodeId' is missing XML documentation
---
FILE: /Users/dohertj2/Desktop/CBDDC/src/ZB.MOM.WW.CBDDC.Persistence/Snapshot/SnapshotDto.cs
LINE: 54
CATEGORY: MissingDoc
SEVERITY: Error
MEMBER: Property
SIGNATURE: Address
MESSAGE: Property 'Address' is missing XML documentation
---
FILE: /Users/dohertj2/Desktop/CBDDC/src/ZB.MOM.WW.CBDDC.Persistence/Snapshot/SnapshotDto.cs
LINE: 55
CATEGORY: MissingDoc
SEVERITY: Error
MEMBER: Property
SIGNATURE: Type
MESSAGE: Property 'Type' is missing XML documentation
---
FILE: /Users/dohertj2/Desktop/CBDDC/src/ZB.MOM.WW.CBDDC.Persistence/Snapshot/SnapshotDto.cs
LINE: 56
CATEGORY: MissingDoc
SEVERITY: Error
MEMBER: Property
SIGNATURE: OAuth2Json
MESSAGE: Property 'OAuth2Json' is missing XML documentation
---
FILE: /Users/dohertj2/Desktop/CBDDC/src/ZB.MOM.WW.CBDDC.Persistence/Snapshot/SnapshotDto.cs
LINE: 57
CATEGORY: MissingDoc
SEVERITY: Error
MEMBER: Property
SIGNATURE: IsEnabled
MESSAGE: Property 'IsEnabled' is missing XML documentation
---
FILE: /Users/dohertj2/Desktop/CBDDC/src/ZB.MOM.WW.CBDDC.Persistence/SnapshotMetadataStore.cs
LINE: 11
CATEGORY: MissingDoc
SEVERITY: Error
MEMBER: Method
SIGNATURE: DropAsync(CancellationToken cancellationToken)
MESSAGE: Method 'DropAsync(CancellationToken cancellationToken)' is missing XML documentation.
---
FILE: /Users/dohertj2/Desktop/CBDDC/src/ZB.MOM.WW.CBDDC.Persistence/SnapshotMetadataStore.cs
LINE: 13
CATEGORY: MissingDoc
SEVERITY: Error
MEMBER: Method
SIGNATURE: ExportAsync(CancellationToken cancellationToken)
MESSAGE: Method 'ExportAsync(CancellationToken cancellationToken)' is missing XML documentation.
---
FILE: /Users/dohertj2/Desktop/CBDDC/src/ZB.MOM.WW.CBDDC.Persistence/SnapshotMetadataStore.cs
LINE: 15
CATEGORY: MissingDoc
SEVERITY: Error
MEMBER: Method
SIGNATURE: GetSnapshotMetadataAsync(string nodeId, CancellationToken cancellationToken)
MESSAGE: Method 'GetSnapshotMetadataAsync(string nodeId, CancellationToken cancellationToken)' is missing XML documentation.
---
FILE: /Users/dohertj2/Desktop/CBDDC/src/ZB.MOM.WW.CBDDC.Persistence/SnapshotMetadataStore.cs
LINE: 17
CATEGORY: MissingDoc
SEVERITY: Error
MEMBER: Method
SIGNATURE: GetSnapshotHashAsync(string nodeId, CancellationToken cancellationToken)
MESSAGE: Method 'GetSnapshotHashAsync(string nodeId, CancellationToken cancellationToken)' is missing XML documentation.
---
FILE: /Users/dohertj2/Desktop/CBDDC/src/ZB.MOM.WW.CBDDC.Persistence/SnapshotMetadataStore.cs
LINE: 19
CATEGORY: MissingDoc
SEVERITY: Error
MEMBER: Method
SIGNATURE: ImportAsync(IEnumerable<SnapshotMetadata> items, CancellationToken cancellationToken)
MESSAGE: Method 'ImportAsync(IEnumerable<SnapshotMetadata> items, CancellationToken cancellationToken)' is missing XML documentation.
---
FILE: /Users/dohertj2/Desktop/CBDDC/src/ZB.MOM.WW.CBDDC.Persistence/SnapshotMetadataStore.cs
LINE: 21
CATEGORY: MissingDoc
SEVERITY: Error
MEMBER: Method
SIGNATURE: InsertSnapshotMetadataAsync(SnapshotMetadata metadata, CancellationToken cancellationToken)
MESSAGE: Method 'InsertSnapshotMetadataAsync(SnapshotMetadata metadata, CancellationToken cancellationToken)' is missing XML documentation.
---
FILE: /Users/dohertj2/Desktop/CBDDC/src/ZB.MOM.WW.CBDDC.Persistence/SnapshotMetadataStore.cs
LINE: 23
CATEGORY: MissingDoc
SEVERITY: Error
MEMBER: Method
SIGNATURE: MergeAsync(IEnumerable<SnapshotMetadata> items, CancellationToken cancellationToken)
MESSAGE: Method 'MergeAsync(IEnumerable<SnapshotMetadata> items, CancellationToken cancellationToken)' is missing XML documentation.
---
FILE: /Users/dohertj2/Desktop/CBDDC/src/ZB.MOM.WW.CBDDC.Persistence/SnapshotMetadataStore.cs
LINE: 25
CATEGORY: MissingDoc
SEVERITY: Error
MEMBER: Method
SIGNATURE: UpdateSnapshotMetadataAsync(SnapshotMetadata existingMeta, CancellationToken cancellationToken)
MESSAGE: Method 'UpdateSnapshotMetadataAsync(SnapshotMetadata existingMeta, CancellationToken cancellationToken)' is missing XML documentation.
---
FILE: /Users/dohertj2/Desktop/CBDDC/src/ZB.MOM.WW.CBDDC.Persistence/SnapshotMetadataStore.cs
LINE: 27
CATEGORY: MissingDoc
SEVERITY: Error
MEMBER: Method
SIGNATURE: GetAllSnapshotMetadataAsync(CancellationToken cancellationToken)
MESSAGE: Method 'GetAllSnapshotMetadataAsync(CancellationToken cancellationToken)' is missing XML documentation.

751
README.md Executable file
View File

@@ -0,0 +1,751 @@
# CBDDC
<div align="center">
**Peer-to-Peer Data Synchronization Middleware for .NET**
[![.NET Version](https://img.shields.io/badge/.NET-8.0%20%7C%2010.0-purple)](https://dotnet.microsoft.com/)
[![License](https://img.shields.io/badge/License-MIT-blue.svg)](LICENSE)
## Status
![Version](https://img.shields.io/badge/version-1.0.0-blue.svg)
![Build](https://img.shields.io/badge/build-passing-brightgreen.svg)
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>

18
SECURITY.md Executable file
View File

@@ -0,0 +1,18 @@
# Security Policy
## Supported Versions
| Version | Supported |
| ------- | ------------------ |
| 1.0.x | :white_check_mark: |
| < 1.0 | :x: |
## Reporting a Vulnerability
If you discover a security vulnerability in CBDDC, please **DO NOT** open a public issue.
Instead, please report it via privacy-preserving means:
- Email the maintainers directly (if contact info is available).
- Or use GitHub's "Report a vulnerability" feature if enabled.
We will make every effort to acknowledge your report within 48 hours and provide a timeline for a fix.

3326
batch4-docs-verify.md Normal file

File diff suppressed because it is too large Load Diff

8
docs-batch-summary.txt Normal file
View File

@@ -0,0 +1,8 @@
Total files: 59
Total issues: 360
Batch 1: files=7, errors=46, warnings=16, total=62
Batch 2: files=6, errors=47, warnings=12, total=59
Batch 3: files=11, errors=47, warnings=12, total=59
Batch 4: files=11, errors=43, warnings=29, total=72
Batch 5: files=12, errors=50, warnings=1, total=51
Batch 6: files=12, errors=49, warnings=8, total=57

111
docs-batches.json Normal file
View File

@@ -0,0 +1,111 @@
{
"batches": [
{
"batch": 1,
"files": [
"/Users/dohertj2/Desktop/CBDDC/src/ZB.MOM.WW.CBDDC.Core/QueryNode.cs",
"/Users/dohertj2/Desktop/CBDDC/src/ZB.MOM.WW.CBDDC.Network/Telemetry/INetworkTelemetryService.cs",
"/Users/dohertj2/Desktop/CBDDC/src/ZB.MOM.WW.CBDDC.Core/VectorClock.cs",
"/Users/dohertj2/Desktop/CBDDC/src/ZB.MOM.WW.CBDDC.Core/ChangesAppliedEventArgs.cs",
"/Users/dohertj2/Desktop/CBDDC/src/ZB.MOM.WW.CBDDC.Core/Storage/IDocumentStore.cs",
"/Users/dohertj2/Desktop/CBDDC/src/ZB.MOM.WW.CBDDC.Core/Resilience/RetryPolicy.cs",
"/Users/dohertj2/Desktop/CBDDC/src/ZB.MOM.WW.CBDDC.Network/Leadership/ILeaderElectionService.cs"
],
"errors": 46,
"warnings": 16,
"total": 62
},
{
"batch": 2,
"files": [
"/Users/dohertj2/Desktop/CBDDC/src/ZB.MOM.WW.CBDDC.Persistence/Snapshot/SnapshotDto.cs",
"/Users/dohertj2/Desktop/CBDDC/src/ZB.MOM.WW.CBDDC.Network/SyncOrchestrator.cs",
"/Users/dohertj2/Desktop/CBDDC/src/ZB.MOM.WW.CBDDC.Persistence/PeerConfigurationStore.cs",
"/Users/dohertj2/Desktop/CBDDC/src/ZB.MOM.WW.CBDDC.Network/IDiscoveryService.cs",
"/Users/dohertj2/Desktop/CBDDC/src/ZB.MOM.WW.CBDDC.Core/Storage/IDocumentMetadataStore.cs",
"/Users/dohertj2/Desktop/CBDDC/src/ZB.MOM.WW.CBDDC.Core/Diagnostics/SyncStatusTracker.cs"
],
"errors": 47,
"warnings": 12,
"total": 59
},
{
"batch": 3,
"files": [
"/Users/dohertj2/Desktop/CBDDC/src/ZB.MOM.WW.CBDDC.Core/Exceptions/CBDDCExceptions.cs",
"/Users/dohertj2/Desktop/CBDDC/src/ZB.MOM.WW.CBDDC.Core/Document.cs",
"/Users/dohertj2/Desktop/CBDDC/src/ZB.MOM.WW.CBDDC.Core/Management/PeerManagementService.cs",
"/Users/dohertj2/Desktop/CBDDC/src/ZB.MOM.WW.CBDDC.Core/Network/StaticPeerNodeConfigurationProvider.cs",
"/Users/dohertj2/Desktop/CBDDC/src/ZB.MOM.WW.CBDDC.Core/Cache/DocumentCache.cs",
"/Users/dohertj2/Desktop/CBDDC/src/ZB.MOM.WW.CBDDC.Network/CompositeDiscoveryService.cs",
"/Users/dohertj2/Desktop/CBDDC/src/ZB.MOM.WW.CBDDC.Core/Network/IPeerNodeConfigurationProvider.cs",
"/Users/dohertj2/Desktop/CBDDC/src/ZB.MOM.WW.CBDDC.Network/ISyncOrchestrator.cs",
"/Users/dohertj2/Desktop/CBDDC/src/ZB.MOM.WW.CBDDC.Core/Sync/OfflineQueue.cs",
"/Users/dohertj2/Desktop/CBDDC/src/ZB.MOM.WW.CBDDC.Core/Sync/RecursiveNodeMergeConflictResolver.cs",
"/Users/dohertj2/Desktop/CBDDC/src/ZB.MOM.WW.CBDDC.Network/PeerDbNetworkExtensions.cs"
],
"errors": 47,
"warnings": 12,
"total": 59
},
{
"batch": 4,
"files": [
"/Users/dohertj2/Desktop/CBDDC/src/ZB.MOM.WW.CBDDC.Network/Telemetry/NetworkTelemetryService.cs",
"/Users/dohertj2/Desktop/CBDDC/src/ZB.MOM.WW.CBDDC.Network/TcpPeerClient.cs",
"/Users/dohertj2/Desktop/CBDDC/src/ZB.MOM.WW.CBDDC.Core/Sync/IConflictResolver.cs",
"/Users/dohertj2/Desktop/CBDDC/src/ZB.MOM.WW.CBDDC.Core/Sync/PendingOperation.cs",
"/Users/dohertj2/Desktop/CBDDC/src/ZB.MOM.WW.CBDDC.Core/SnapshotMetadata.cs",
"/Users/dohertj2/Desktop/CBDDC/src/ZB.MOM.WW.CBDDC.Network/Security/IPeerHandshakeService.cs",
"/Users/dohertj2/Desktop/CBDDC/src/ZB.MOM.WW.CBDDC.Network/Protocol/ProtocolHandler.cs",
"/Users/dohertj2/Desktop/CBDDC/src/ZB.MOM.WW.CBDDC.Persistence/NodeCacheEntry.cs",
"/Users/dohertj2/Desktop/CBDDC/src/ZB.MOM.WW.CBDDC.Core/Diagnostics/ICBDDCHealthCheck.cs",
"/Users/dohertj2/Desktop/CBDDC/src/ZB.MOM.WW.CBDDC.Network/Security/IAuthenticator.cs",
"/Users/dohertj2/Desktop/CBDDC/src/ZB.MOM.WW.CBDDC.Network/TcpSyncServer.cs"
],
"errors": 43,
"warnings": 29,
"total": 72
},
{
"batch": 5,
"files": [
"/Users/dohertj2/Desktop/CBDDC/src/ZB.MOM.WW.CBDDC.Core/HlcTimestamp.cs",
"/Users/dohertj2/Desktop/CBDDC/src/ZB.MOM.WW.CBDDC.Core/Diagnostics/ISyncStatusTracker.cs",
"/Users/dohertj2/Desktop/CBDDC/src/ZB.MOM.WW.CBDDC.Persistence/OplogStore.cs",
"/Users/dohertj2/Desktop/CBDDC/src/ZB.MOM.WW.CBDDC.Core/Cache/IDocumentCache.cs",
"/Users/dohertj2/Desktop/CBDDC/src/ZB.MOM.WW.CBDDC.Network/Leadership/BullyLeaderElectionService.cs",
"/Users/dohertj2/Desktop/CBDDC/src/ZB.MOM.WW.CBDDC.Core/Sync/IOfflineQueue.cs",
"/Users/dohertj2/Desktop/CBDDC/src/ZB.MOM.WW.CBDDC.Network/CBDDCNodeService.cs",
"/Users/dohertj2/Desktop/CBDDC/src/ZB.MOM.WW.CBDDC.Network/ISyncServer.cs",
"/Users/dohertj2/Desktop/CBDDC/src/ZB.MOM.WW.CBDDC.Network/Security/SecureHandshakeService.cs",
"/Users/dohertj2/Desktop/CBDDC/src/ZB.MOM.WW.CBDDC.Core/CBDDCServiceCollectionExtensions.cs",
"/Users/dohertj2/Desktop/CBDDC/src/ZB.MOM.WW.CBDDC.Core/Storage/CorruptDatabaseException.cs",
"/Users/dohertj2/Desktop/CBDDC/src/ZB.MOM.WW.CBDDC.Network/Security/JwtOAuth2Validator.cs"
],
"errors": 50,
"warnings": 1,
"total": 51
},
{
"batch": 6,
"files": [
"/Users/dohertj2/Desktop/CBDDC/src/ZB.MOM.WW.CBDDC.Core/OplogEntry.cs",
"/Users/dohertj2/Desktop/CBDDC/src/ZB.MOM.WW.CBDDC.Persistence/SnapshotMetadataStore.cs",
"/Users/dohertj2/Desktop/CBDDC/src/ZB.MOM.WW.CBDDC.Network/UdpDiscoveryService.cs",
"/Users/dohertj2/Desktop/CBDDC/src/ZB.MOM.WW.CBDDC.Network/ICBDDCNode.cs",
"/Users/dohertj2/Desktop/CBDDC/src/ZB.MOM.WW.CBDDC.Network/Security/OAuth2ClientCredentialsTokenProvider.cs",
"/Users/dohertj2/Desktop/CBDDC/src/ZB.MOM.WW.CBDDC.Network/CBDDCNode.cs",
"/Users/dohertj2/Desktop/CBDDC/src/ZB.MOM.WW.CBDDC.Network/CompressionHelper.cs",
"/Users/dohertj2/Desktop/CBDDC/src/ZB.MOM.WW.CBDDC.Core/Resilience/IRetryPolicy.cs",
"/Users/dohertj2/Desktop/CBDDC/src/ZB.MOM.WW.CBDDC.Network/Security/CryptoHelper.cs",
"/Users/dohertj2/Desktop/CBDDC/src/ZB.MOM.WW.CBDDC.Core/Diagnostics/CBDDCHealthCheck.cs",
"/Users/dohertj2/Desktop/CBDDC/src/ZB.MOM.WW.CBDDC.Core/Sync/LastWriteWinsConflictResolver.cs",
"/Users/dohertj2/Desktop/CBDDC/src/ZB.MOM.WW.CBDDC.Core/Storage/IVectorClockService.cs"
],
"errors": 49,
"warnings": 8,
"total": 57
}
]
}

59
docs-file-counts.tsv Normal file
View File

@@ -0,0 +1,59 @@
/Users/dohertj2/Desktop/CBDDC/src/ZB.MOM.WW.CBDDC.Core/QueryNode.cs 33 0 33
/Users/dohertj2/Desktop/CBDDC/src/ZB.MOM.WW.CBDDC.Persistence/Snapshot/SnapshotDto.cs 32 0 32
/Users/dohertj2/Desktop/CBDDC/src/ZB.MOM.WW.CBDDC.Core/Exceptions/CBDDCExceptions.cs 16 0 16
/Users/dohertj2/Desktop/CBDDC/src/ZB.MOM.WW.CBDDC.Network/Telemetry/NetworkTelemetryService.cs 11 0 11
/Users/dohertj2/Desktop/CBDDC/src/ZB.MOM.WW.CBDDC.Core/HlcTimestamp.cs 10 1 11
/Users/dohertj2/Desktop/CBDDC/src/ZB.MOM.WW.CBDDC.Core/OplogEntry.cs 9 0 9
/Users/dohertj2/Desktop/CBDDC/src/ZB.MOM.WW.CBDDC.Persistence/SnapshotMetadataStore.cs 9 0 9
/Users/dohertj2/Desktop/CBDDC/src/ZB.MOM.WW.CBDDC.Core/Diagnostics/ISyncStatusTracker.cs 8 0 8
/Users/dohertj2/Desktop/CBDDC/src/ZB.MOM.WW.CBDDC.Network/TcpPeerClient.cs 7 23 30
/Users/dohertj2/Desktop/CBDDC/src/ZB.MOM.WW.CBDDC.Core/Document.cs 7 0 7
/Users/dohertj2/Desktop/CBDDC/src/ZB.MOM.WW.CBDDC.Network/UdpDiscoveryService.cs 7 0 7
/Users/dohertj2/Desktop/CBDDC/src/ZB.MOM.WW.CBDDC.Persistence/OplogStore.cs 7 0 7
/Users/dohertj2/Desktop/CBDDC/src/ZB.MOM.WW.CBDDC.Core/Management/PeerManagementService.cs 6 0 6
/Users/dohertj2/Desktop/CBDDC/src/ZB.MOM.WW.CBDDC.Core/Sync/IConflictResolver.cs 6 0 6
/Users/dohertj2/Desktop/CBDDC/src/ZB.MOM.WW.CBDDC.Network/ICBDDCNode.cs 6 0 6
/Users/dohertj2/Desktop/CBDDC/src/ZB.MOM.WW.CBDDC.Core/Cache/IDocumentCache.cs 5 0 5
/Users/dohertj2/Desktop/CBDDC/src/ZB.MOM.WW.CBDDC.Core/Network/StaticPeerNodeConfigurationProvider.cs 5 0 5
/Users/dohertj2/Desktop/CBDDC/src/ZB.MOM.WW.CBDDC.Core/Sync/PendingOperation.cs 5 0 5
/Users/dohertj2/Desktop/CBDDC/src/ZB.MOM.WW.CBDDC.Network/Leadership/BullyLeaderElectionService.cs 5 0 5
/Users/dohertj2/Desktop/CBDDC/src/ZB.MOM.WW.CBDDC.Network/Security/OAuth2ClientCredentialsTokenProvider.cs 5 0 5
/Users/dohertj2/Desktop/CBDDC/src/ZB.MOM.WW.CBDDC.Network/SyncOrchestrator.cs 5 0 5
/Users/dohertj2/Desktop/CBDDC/src/ZB.MOM.WW.CBDDC.Network/Telemetry/INetworkTelemetryService.cs 5 0 5
/Users/dohertj2/Desktop/CBDDC/src/ZB.MOM.WW.CBDDC.Core/Cache/DocumentCache.cs 4 7 11
/Users/dohertj2/Desktop/CBDDC/src/ZB.MOM.WW.CBDDC.Core/SnapshotMetadata.cs 4 0 4
/Users/dohertj2/Desktop/CBDDC/src/ZB.MOM.WW.CBDDC.Core/Sync/IOfflineQueue.cs 4 0 4
/Users/dohertj2/Desktop/CBDDC/src/ZB.MOM.WW.CBDDC.Network/CBDDCNode.cs 4 0 4
/Users/dohertj2/Desktop/CBDDC/src/ZB.MOM.WW.CBDDC.Persistence/PeerConfigurationStore.cs 4 0 4
/Users/dohertj2/Desktop/CBDDC/src/ZB.MOM.WW.CBDDC.Core/VectorClock.cs 3 7 10
/Users/dohertj2/Desktop/CBDDC/src/ZB.MOM.WW.CBDDC.Network/Security/IPeerHandshakeService.cs 3 4 7
/Users/dohertj2/Desktop/CBDDC/src/ZB.MOM.WW.CBDDC.Network/CBDDCNodeService.cs 3 0 3
/Users/dohertj2/Desktop/CBDDC/src/ZB.MOM.WW.CBDDC.Network/CompositeDiscoveryService.cs 3 0 3
/Users/dohertj2/Desktop/CBDDC/src/ZB.MOM.WW.CBDDC.Network/CompressionHelper.cs 3 0 3
/Users/dohertj2/Desktop/CBDDC/src/ZB.MOM.WW.CBDDC.Network/IDiscoveryService.cs 3 0 3
/Users/dohertj2/Desktop/CBDDC/src/ZB.MOM.WW.CBDDC.Network/ISyncServer.cs 3 0 3
/Users/dohertj2/Desktop/CBDDC/src/ZB.MOM.WW.CBDDC.Network/Protocol/ProtocolHandler.cs 3 0 3
/Users/dohertj2/Desktop/CBDDC/src/ZB.MOM.WW.CBDDC.Core/ChangesAppliedEventArgs.cs 2 0 2
/Users/dohertj2/Desktop/CBDDC/src/ZB.MOM.WW.CBDDC.Core/Network/IPeerNodeConfigurationProvider.cs 2 0 2
/Users/dohertj2/Desktop/CBDDC/src/ZB.MOM.WW.CBDDC.Core/Resilience/IRetryPolicy.cs 2 0 2
/Users/dohertj2/Desktop/CBDDC/src/ZB.MOM.WW.CBDDC.Core/Storage/IDocumentMetadataStore.cs 2 0 2
/Users/dohertj2/Desktop/CBDDC/src/ZB.MOM.WW.CBDDC.Core/Storage/IDocumentStore.cs 2 0 2
/Users/dohertj2/Desktop/CBDDC/src/ZB.MOM.WW.CBDDC.Network/ISyncOrchestrator.cs 2 0 2
/Users/dohertj2/Desktop/CBDDC/src/ZB.MOM.WW.CBDDC.Network/Security/CryptoHelper.cs 2 0 2
/Users/dohertj2/Desktop/CBDDC/src/ZB.MOM.WW.CBDDC.Network/Security/SecureHandshakeService.cs 2 0 2
/Users/dohertj2/Desktop/CBDDC/src/ZB.MOM.WW.CBDDC.Persistence/NodeCacheEntry.cs 2 0 2
/Users/dohertj2/Desktop/CBDDC/src/ZB.MOM.WW.CBDDC.Core/Diagnostics/SyncStatusTracker.cs 1 12 13
/Users/dohertj2/Desktop/CBDDC/src/ZB.MOM.WW.CBDDC.Core/Resilience/RetryPolicy.cs 1 7 8
/Users/dohertj2/Desktop/CBDDC/src/ZB.MOM.WW.CBDDC.Core/Sync/OfflineQueue.cs 1 3 4
/Users/dohertj2/Desktop/CBDDC/src/ZB.MOM.WW.CBDDC.Core/Diagnostics/CBDDCHealthCheck.cs 1 1 2
/Users/dohertj2/Desktop/CBDDC/src/ZB.MOM.WW.CBDDC.Core/CBDDCServiceCollectionExtensions.cs 1 0 1
/Users/dohertj2/Desktop/CBDDC/src/ZB.MOM.WW.CBDDC.Core/Diagnostics/ICBDDCHealthCheck.cs 1 0 1
/Users/dohertj2/Desktop/CBDDC/src/ZB.MOM.WW.CBDDC.Core/Storage/CorruptDatabaseException.cs 1 0 1
/Users/dohertj2/Desktop/CBDDC/src/ZB.MOM.WW.CBDDC.Core/Sync/LastWriteWinsConflictResolver.cs 1 0 1
/Users/dohertj2/Desktop/CBDDC/src/ZB.MOM.WW.CBDDC.Core/Sync/RecursiveNodeMergeConflictResolver.cs 1 0 1
/Users/dohertj2/Desktop/CBDDC/src/ZB.MOM.WW.CBDDC.Network/Security/IAuthenticator.cs 1 0 1
/Users/dohertj2/Desktop/CBDDC/src/ZB.MOM.WW.CBDDC.Network/Security/JwtOAuth2Validator.cs 1 0 1
/Users/dohertj2/Desktop/CBDDC/src/ZB.MOM.WW.CBDDC.Core/Storage/IVectorClockService.cs 0 7 7
/Users/dohertj2/Desktop/CBDDC/src/ZB.MOM.WW.CBDDC.Network/Leadership/ILeaderElectionService.cs 0 2 2
/Users/dohertj2/Desktop/CBDDC/src/ZB.MOM.WW.CBDDC.Network/PeerDbNetworkExtensions.cs 0 2 2
/Users/dohertj2/Desktop/CBDDC/src/ZB.MOM.WW.CBDDC.Network/TcpSyncServer.cs 0 2 2
1 /Users/dohertj2/Desktop/CBDDC/src/ZB.MOM.WW.CBDDC.Core/QueryNode.cs 33 0 33
2 /Users/dohertj2/Desktop/CBDDC/src/ZB.MOM.WW.CBDDC.Persistence/Snapshot/SnapshotDto.cs 32 0 32
3 /Users/dohertj2/Desktop/CBDDC/src/ZB.MOM.WW.CBDDC.Core/Exceptions/CBDDCExceptions.cs 16 0 16
4 /Users/dohertj2/Desktop/CBDDC/src/ZB.MOM.WW.CBDDC.Network/Telemetry/NetworkTelemetryService.cs 11 0 11
5 /Users/dohertj2/Desktop/CBDDC/src/ZB.MOM.WW.CBDDC.Core/HlcTimestamp.cs 10 1 11
6 /Users/dohertj2/Desktop/CBDDC/src/ZB.MOM.WW.CBDDC.Core/OplogEntry.cs 9 0 9
7 /Users/dohertj2/Desktop/CBDDC/src/ZB.MOM.WW.CBDDC.Persistence/SnapshotMetadataStore.cs 9 0 9
8 /Users/dohertj2/Desktop/CBDDC/src/ZB.MOM.WW.CBDDC.Core/Diagnostics/ISyncStatusTracker.cs 8 0 8
9 /Users/dohertj2/Desktop/CBDDC/src/ZB.MOM.WW.CBDDC.Network/TcpPeerClient.cs 7 23 30
10 /Users/dohertj2/Desktop/CBDDC/src/ZB.MOM.WW.CBDDC.Core/Document.cs 7 0 7
11 /Users/dohertj2/Desktop/CBDDC/src/ZB.MOM.WW.CBDDC.Network/UdpDiscoveryService.cs 7 0 7
12 /Users/dohertj2/Desktop/CBDDC/src/ZB.MOM.WW.CBDDC.Persistence/OplogStore.cs 7 0 7
13 /Users/dohertj2/Desktop/CBDDC/src/ZB.MOM.WW.CBDDC.Core/Management/PeerManagementService.cs 6 0 6
14 /Users/dohertj2/Desktop/CBDDC/src/ZB.MOM.WW.CBDDC.Core/Sync/IConflictResolver.cs 6 0 6
15 /Users/dohertj2/Desktop/CBDDC/src/ZB.MOM.WW.CBDDC.Network/ICBDDCNode.cs 6 0 6
16 /Users/dohertj2/Desktop/CBDDC/src/ZB.MOM.WW.CBDDC.Core/Cache/IDocumentCache.cs 5 0 5
17 /Users/dohertj2/Desktop/CBDDC/src/ZB.MOM.WW.CBDDC.Core/Network/StaticPeerNodeConfigurationProvider.cs 5 0 5
18 /Users/dohertj2/Desktop/CBDDC/src/ZB.MOM.WW.CBDDC.Core/Sync/PendingOperation.cs 5 0 5
19 /Users/dohertj2/Desktop/CBDDC/src/ZB.MOM.WW.CBDDC.Network/Leadership/BullyLeaderElectionService.cs 5 0 5
20 /Users/dohertj2/Desktop/CBDDC/src/ZB.MOM.WW.CBDDC.Network/Security/OAuth2ClientCredentialsTokenProvider.cs 5 0 5
21 /Users/dohertj2/Desktop/CBDDC/src/ZB.MOM.WW.CBDDC.Network/SyncOrchestrator.cs 5 0 5
22 /Users/dohertj2/Desktop/CBDDC/src/ZB.MOM.WW.CBDDC.Network/Telemetry/INetworkTelemetryService.cs 5 0 5
23 /Users/dohertj2/Desktop/CBDDC/src/ZB.MOM.WW.CBDDC.Core/Cache/DocumentCache.cs 4 7 11
24 /Users/dohertj2/Desktop/CBDDC/src/ZB.MOM.WW.CBDDC.Core/SnapshotMetadata.cs 4 0 4
25 /Users/dohertj2/Desktop/CBDDC/src/ZB.MOM.WW.CBDDC.Core/Sync/IOfflineQueue.cs 4 0 4
26 /Users/dohertj2/Desktop/CBDDC/src/ZB.MOM.WW.CBDDC.Network/CBDDCNode.cs 4 0 4
27 /Users/dohertj2/Desktop/CBDDC/src/ZB.MOM.WW.CBDDC.Persistence/PeerConfigurationStore.cs 4 0 4
28 /Users/dohertj2/Desktop/CBDDC/src/ZB.MOM.WW.CBDDC.Core/VectorClock.cs 3 7 10
29 /Users/dohertj2/Desktop/CBDDC/src/ZB.MOM.WW.CBDDC.Network/Security/IPeerHandshakeService.cs 3 4 7
30 /Users/dohertj2/Desktop/CBDDC/src/ZB.MOM.WW.CBDDC.Network/CBDDCNodeService.cs 3 0 3
31 /Users/dohertj2/Desktop/CBDDC/src/ZB.MOM.WW.CBDDC.Network/CompositeDiscoveryService.cs 3 0 3
32 /Users/dohertj2/Desktop/CBDDC/src/ZB.MOM.WW.CBDDC.Network/CompressionHelper.cs 3 0 3
33 /Users/dohertj2/Desktop/CBDDC/src/ZB.MOM.WW.CBDDC.Network/IDiscoveryService.cs 3 0 3
34 /Users/dohertj2/Desktop/CBDDC/src/ZB.MOM.WW.CBDDC.Network/ISyncServer.cs 3 0 3
35 /Users/dohertj2/Desktop/CBDDC/src/ZB.MOM.WW.CBDDC.Network/Protocol/ProtocolHandler.cs 3 0 3
36 /Users/dohertj2/Desktop/CBDDC/src/ZB.MOM.WW.CBDDC.Core/ChangesAppliedEventArgs.cs 2 0 2
37 /Users/dohertj2/Desktop/CBDDC/src/ZB.MOM.WW.CBDDC.Core/Network/IPeerNodeConfigurationProvider.cs 2 0 2
38 /Users/dohertj2/Desktop/CBDDC/src/ZB.MOM.WW.CBDDC.Core/Resilience/IRetryPolicy.cs 2 0 2
39 /Users/dohertj2/Desktop/CBDDC/src/ZB.MOM.WW.CBDDC.Core/Storage/IDocumentMetadataStore.cs 2 0 2
40 /Users/dohertj2/Desktop/CBDDC/src/ZB.MOM.WW.CBDDC.Core/Storage/IDocumentStore.cs 2 0 2
41 /Users/dohertj2/Desktop/CBDDC/src/ZB.MOM.WW.CBDDC.Network/ISyncOrchestrator.cs 2 0 2
42 /Users/dohertj2/Desktop/CBDDC/src/ZB.MOM.WW.CBDDC.Network/Security/CryptoHelper.cs 2 0 2
43 /Users/dohertj2/Desktop/CBDDC/src/ZB.MOM.WW.CBDDC.Network/Security/SecureHandshakeService.cs 2 0 2
44 /Users/dohertj2/Desktop/CBDDC/src/ZB.MOM.WW.CBDDC.Persistence/NodeCacheEntry.cs 2 0 2
45 /Users/dohertj2/Desktop/CBDDC/src/ZB.MOM.WW.CBDDC.Core/Diagnostics/SyncStatusTracker.cs 1 12 13
46 /Users/dohertj2/Desktop/CBDDC/src/ZB.MOM.WW.CBDDC.Core/Resilience/RetryPolicy.cs 1 7 8
47 /Users/dohertj2/Desktop/CBDDC/src/ZB.MOM.WW.CBDDC.Core/Sync/OfflineQueue.cs 1 3 4
48 /Users/dohertj2/Desktop/CBDDC/src/ZB.MOM.WW.CBDDC.Core/Diagnostics/CBDDCHealthCheck.cs 1 1 2
49 /Users/dohertj2/Desktop/CBDDC/src/ZB.MOM.WW.CBDDC.Core/CBDDCServiceCollectionExtensions.cs 1 0 1
50 /Users/dohertj2/Desktop/CBDDC/src/ZB.MOM.WW.CBDDC.Core/Diagnostics/ICBDDCHealthCheck.cs 1 0 1
51 /Users/dohertj2/Desktop/CBDDC/src/ZB.MOM.WW.CBDDC.Core/Storage/CorruptDatabaseException.cs 1 0 1
52 /Users/dohertj2/Desktop/CBDDC/src/ZB.MOM.WW.CBDDC.Core/Sync/LastWriteWinsConflictResolver.cs 1 0 1
53 /Users/dohertj2/Desktop/CBDDC/src/ZB.MOM.WW.CBDDC.Core/Sync/RecursiveNodeMergeConflictResolver.cs 1 0 1
54 /Users/dohertj2/Desktop/CBDDC/src/ZB.MOM.WW.CBDDC.Network/Security/IAuthenticator.cs 1 0 1
55 /Users/dohertj2/Desktop/CBDDC/src/ZB.MOM.WW.CBDDC.Network/Security/JwtOAuth2Validator.cs 1 0 1
56 /Users/dohertj2/Desktop/CBDDC/src/ZB.MOM.WW.CBDDC.Core/Storage/IVectorClockService.cs 0 7 7
57 /Users/dohertj2/Desktop/CBDDC/src/ZB.MOM.WW.CBDDC.Network/Leadership/ILeaderElectionService.cs 0 2 2
58 /Users/dohertj2/Desktop/CBDDC/src/ZB.MOM.WW.CBDDC.Network/PeerDbNetworkExtensions.cs 0 2 2
59 /Users/dohertj2/Desktop/CBDDC/src/ZB.MOM.WW.CBDDC.Network/TcpSyncServer.cs 0 2 2

168
docs-fix-batch-1.md Normal file
View File

@@ -0,0 +1,168 @@
You are a C# documentation expert. Fix the following XML documentation issues in the listed files.
IMPORTANT: Make ONLY documentation changes. Do NOT modify code logic, behavior, signatures, or formatting unrelated to XML docs.
Model requirement: GPT-5.3-Codex-Spark
Reasoning level: medium
Issue handling rules:
- MissingDoc: add concise XML docs with <summary> (and <returns> where appropriate)
- MissingInheritDoc: use /// <inheritdoc /> for overrides/interface implementations where appropriate
- MissingParam: add missing <param name="...">...</param> tags
- MissingTypeParam: add missing <typeparam name="...">...</typeparam> tags
- MalformedXml: repair XML syntax and invalid tags
Preserve local style and keep descriptions concise.
=== BATCH 1 OF 6 ===
FILE: /Users/dohertj2/Desktop/CBDDC/src/ZB.MOM.WW.CBDDC.Core/QueryNode.cs
Issues:
- Line 9, MissingDoc, Error, Property, Field
-> Property 'Field' is missing XML documentation
- Line 10, MissingDoc, Error, Property, Value
-> Property 'Value' is missing XML documentation
- Line 11, MissingDoc, Error, Constructor, Eq(string field, object value)
-> Constructor 'Eq(string field, object value)' is missing XML documentation.
- Line 16, MissingDoc, Error, Property, Field
-> Property 'Field' is missing XML documentation
- Line 17, MissingDoc, Error, Property, Value
-> Property 'Value' is missing XML documentation
- Line 18, MissingDoc, Error, Constructor, Gt(string field, object value)
-> Constructor 'Gt(string field, object value)' is missing XML documentation.
- Line 23, MissingDoc, Error, Property, Field
-> Property 'Field' is missing XML documentation
- Line 24, MissingDoc, Error, Property, Value
-> Property 'Value' is missing XML documentation
- Line 25, MissingDoc, Error, Constructor, Lt(string field, object value)
-> Constructor 'Lt(string field, object value)' is missing XML documentation.
- Line 30, MissingDoc, Error, Property, Field
-> Property 'Field' is missing XML documentation
- Line 31, MissingDoc, Error, Property, Value
-> Property 'Value' is missing XML documentation
- Line 32, MissingDoc, Error, Constructor, Gte(string field, object value)
-> Constructor 'Gte(string field, object value)' is missing XML documentation.
- Line 37, MissingDoc, Error, Property, Field
-> Property 'Field' is missing XML documentation
- Line 38, MissingDoc, Error, Property, Value
-> Property 'Value' is missing XML documentation
- Line 39, MissingDoc, Error, Constructor, Lte(string field, object value)
-> Constructor 'Lte(string field, object value)' is missing XML documentation.
- Line 44, MissingDoc, Error, Property, Field
-> Property 'Field' is missing XML documentation
- Line 45, MissingDoc, Error, Property, Value
-> Property 'Value' is missing XML documentation
- Line 46, MissingDoc, Error, Constructor, Neq(string field, object value)
-> Constructor 'Neq(string field, object value)' is missing XML documentation.
- Line 51, MissingDoc, Error, Property, Field
-> Property 'Field' is missing XML documentation
- Line 52, MissingDoc, Error, Property, Values
-> Property 'Values' is missing XML documentation
- Line 53, MissingDoc, Error, Constructor, In(string field, object[] values)
-> Constructor 'In(string field, object[] values)' is missing XML documentation.
- Line 58, MissingDoc, Error, Property, Field
-> Property 'Field' is missing XML documentation
- Line 59, MissingDoc, Error, Property, Value
-> Property 'Value' is missing XML documentation
- Line 60, MissingDoc, Error, Constructor, Contains(string field, string value)
-> Constructor 'Contains(string field, string value)' is missing XML documentation.
- Line 65, MissingDoc, Error, Property, Field
-> Property 'Field' is missing XML documentation
- Line 66, MissingDoc, Error, Property, Value
-> Property 'Value' is missing XML documentation
- Line 67, MissingDoc, Error, Constructor, NotContains(string field, string value)
-> Constructor 'NotContains(string field, string value)' is missing XML documentation.
- Line 72, MissingDoc, Error, Property, Left
-> Property 'Left' is missing XML documentation
- Line 73, MissingDoc, Error, Property, Right
-> Property 'Right' is missing XML documentation
- Line 74, MissingDoc, Error, Constructor, And(QueryNode left, QueryNode right)
-> Constructor 'And(QueryNode left, QueryNode right)' is missing XML documentation.
- Line 79, MissingDoc, Error, Property, Left
-> Property 'Left' is missing XML documentation
- Line 80, MissingDoc, Error, Property, Right
-> Property 'Right' is missing XML documentation
- Line 81, MissingDoc, Error, Constructor, Or(QueryNode left, QueryNode right)
-> Constructor 'Or(QueryNode left, QueryNode right)' is missing XML documentation.
FILE: /Users/dohertj2/Desktop/CBDDC/src/ZB.MOM.WW.CBDDC.Network/Telemetry/INetworkTelemetryService.cs
Issues:
- Line 8, MissingDoc, Error, Method, RecordValue(MetricType type, double value)
-> Method 'RecordValue(MetricType type, double value)' is missing XML documentation.
- Line 9, MissingDoc, Error, Method, StartMetric(MetricType type)
-> Method 'StartMetric(MetricType type)' is missing XML documentation.
- Line 10, MissingDoc, Error, Method, GetSnapshot()
-> Method 'GetSnapshot()' is missing XML documentation.
- Line 19, MissingDoc, Error, Constructor, MetricTimer(INetworkTelemetryService service, MetricType type)
-> Constructor 'MetricTimer(INetworkTelemetryService service, MetricType type)' is missing XML documentation.
- Line 26, MissingDoc, Error, Method, Dispose()
-> Method 'Dispose()' is missing XML documentation.
FILE: /Users/dohertj2/Desktop/CBDDC/src/ZB.MOM.WW.CBDDC.Core/VectorClock.cs
Issues:
- Line 15, MissingDoc, Error, Constructor, VectorClock()
-> Constructor 'VectorClock()' is missing XML documentation.
- Line 20, MissingDoc, Error, Constructor, VectorClock(Dictionary<string, HlcTimestamp> clock)
-> Constructor 'VectorClock(Dictionary<string, HlcTimestamp> clock)' is missing XML documentation.
- Line 33, MissingParam, Warning, Method, GetTimestamp(string nodeId)
-> Method 'GetTimestamp(string nodeId)' is missing <param name="nodeId"> documentation.
- Line 41, MissingParam, Warning, Method, SetTimestamp(string nodeId, HlcTimestamp timestamp)
-> Method 'SetTimestamp(string nodeId, HlcTimestamp timestamp)' is missing <param name="timestamp"> documentation.
- Line 41, MissingParam, Warning, Method, SetTimestamp(string nodeId, HlcTimestamp timestamp)
-> Method 'SetTimestamp(string nodeId, HlcTimestamp timestamp)' is missing <param name="nodeId"> documentation.
- Line 49, MissingParam, Warning, Method, Merge(VectorClock other)
-> Method 'Merge(VectorClock other)' is missing <param name="other"> documentation.
- Line 68, MissingParam, Warning, Method, CompareTo(VectorClock other)
-> Method 'CompareTo(VectorClock other)' is missing <param name="other"> documentation.
- Line 110, MissingParam, Warning, Method, GetNodesWithUpdates(VectorClock other)
-> Method 'GetNodesWithUpdates(VectorClock other)' is missing <param name="other"> documentation.
- Line 134, MissingParam, Warning, Method, GetNodesToPush(VectorClock other)
-> Method 'GetNodesToPush(VectorClock other)' is missing <param name="other"> documentation.
- Line 158, MissingDoc, Error, Method, ToString()
-> Method 'ToString()' is missing XML documentation.
FILE: /Users/dohertj2/Desktop/CBDDC/src/ZB.MOM.WW.CBDDC.Core/ChangesAppliedEventArgs.cs
Issues:
- Line 11, MissingDoc, Error, Property, Changes
-> Property 'Changes' is missing XML documentation
- Line 12, MissingDoc, Error, Constructor, ChangesAppliedEventArgs(IEnumerable<OplogEntry> changes)
-> Constructor 'ChangesAppliedEventArgs(IEnumerable<OplogEntry> changes)' is missing XML documentation.
FILE: /Users/dohertj2/Desktop/CBDDC/src/ZB.MOM.WW.CBDDC.Core/Storage/IDocumentStore.cs
Issues:
- Line 13, MissingDoc, Error, Property, InterestedCollection
-> Property 'InterestedCollection' is missing XML documentation
- Line 81, MalformedXml, Error, Method, DeleteBatchDocumentsAsync(IEnumerable<string> documentKeys, CancellationToken cancellationToken)
-> Method 'DeleteBatchDocumentsAsync(IEnumerable<string> documentKeys, CancellationToken cancellationToken)' has malformed XML documentation that cannot be parsed.
FILE: /Users/dohertj2/Desktop/CBDDC/src/ZB.MOM.WW.CBDDC.Core/Resilience/RetryPolicy.cs
Issues:
- Line 18, MissingDoc, Error, Constructor, RetryPolicy(IPeerNodeConfigurationProvider peerNodeConfigurationProvider, ILogger<RetryPolicy> logger)
-> Constructor 'RetryPolicy(IPeerNodeConfigurationProvider peerNodeConfigurationProvider, ILogger<RetryPolicy> logger)' is missing XML documentation.
- Line 28, MissingTypeParam, Warning, Method, ExecuteAsync(Func<Task<T>> operation, string operationName, CancellationToken cancellationToken)
-> Method 'ExecuteAsync(Func<Task<T>> operation, string operationName, CancellationToken cancellationToken)' is missing <typeparam name="T"> documentation.
- Line 28, MissingParam, Warning, Method, ExecuteAsync(Func<Task<T>> operation, string operationName, CancellationToken cancellationToken)
-> Method 'ExecuteAsync(Func<Task<T>> operation, string operationName, CancellationToken cancellationToken)' is missing <param name="cancellationToken"> documentation.
- Line 28, MissingParam, Warning, Method, ExecuteAsync(Func<Task<T>> operation, string operationName, CancellationToken cancellationToken)
-> Method 'ExecuteAsync(Func<Task<T>> operation, string operationName, CancellationToken cancellationToken)' is missing <param name="operationName"> documentation.
- Line 28, MissingParam, Warning, Method, ExecuteAsync(Func<Task<T>> operation, string operationName, CancellationToken cancellationToken)
-> Method 'ExecuteAsync(Func<Task<T>> operation, string operationName, CancellationToken cancellationToken)' is missing <param name="operation"> documentation.
- Line 70, MissingParam, Warning, Method, ExecuteAsync(Func<Task> operation, string operationName, CancellationToken cancellationToken)
-> Method 'ExecuteAsync(Func<Task> operation, string operationName, CancellationToken cancellationToken)' is missing <param name="cancellationToken"> documentation.
- Line 70, MissingParam, Warning, Method, ExecuteAsync(Func<Task> operation, string operationName, CancellationToken cancellationToken)
-> Method 'ExecuteAsync(Func<Task> operation, string operationName, CancellationToken cancellationToken)' is missing <param name="operationName"> documentation.
- Line 70, MissingParam, Warning, Method, ExecuteAsync(Func<Task> operation, string operationName, CancellationToken cancellationToken)
-> Method 'ExecuteAsync(Func<Task> operation, string operationName, CancellationToken cancellationToken)' is missing <param name="operation"> documentation.
FILE: /Users/dohertj2/Desktop/CBDDC/src/ZB.MOM.WW.CBDDC.Network/Leadership/ILeaderElectionService.cs
Issues:
- Line 25, MissingParam, Warning, Constructor, LeadershipChangedEventArgs(string? currentGatewayNodeId, bool isLocalNodeGateway)
-> Constructor 'LeadershipChangedEventArgs(string? currentGatewayNodeId, bool isLocalNodeGateway)' is missing <param name="isLocalNodeGateway"> documentation.
- Line 25, MissingParam, Warning, Constructor, LeadershipChangedEventArgs(string? currentGatewayNodeId, bool isLocalNodeGateway)
-> Constructor 'LeadershipChangedEventArgs(string? currentGatewayNodeId, bool isLocalNodeGateway)' is missing <param name="currentGatewayNodeId"> documentation.
Execution rules:
1. Read each file and apply all listed documentation fixes.
2. Keep changes minimal and doc-focused.
3. Avoid duplicate XML tags or malformed XML.
4. Return a short summary: files modified, issue count fixed per file, and any unresolved items.

159
docs-fix-batch-2.md Normal file
View File

@@ -0,0 +1,159 @@
You are a C# documentation expert. Fix the following XML documentation issues in the listed files.
IMPORTANT: Make ONLY documentation changes. Do NOT modify code logic, behavior, signatures, or formatting unrelated to XML docs.
Model requirement: GPT-5.3-Codex-Spark
Reasoning level: medium
Issue handling rules:
- MissingDoc: add concise XML docs with <summary> (and <returns> where appropriate)
- MissingInheritDoc: use /// <inheritdoc /> for overrides/interface implementations where appropriate
- MissingParam: add missing <param name="...">...</param> tags
- MissingTypeParam: add missing <typeparam name="...">...</typeparam> tags
- MalformedXml: repair XML syntax and invalid tags
Preserve local style and keep descriptions concise.
=== BATCH 2 OF 6 ===
FILE: /Users/dohertj2/Desktop/CBDDC/src/ZB.MOM.WW.CBDDC.Persistence/Snapshot/SnapshotDto.cs
Issues:
- Line 10, MissingDoc, Error, Property, Version
-> Property 'Version' is missing XML documentation
- Line 11, MissingDoc, Error, Property, CreatedAt
-> Property 'CreatedAt' is missing XML documentation
- Line 12, MissingDoc, Error, Property, NodeId
-> Property 'NodeId' is missing XML documentation
- Line 13, MissingDoc, Error, Property, Documents
-> Property 'Documents' is missing XML documentation
- Line 14, MissingDoc, Error, Property, Oplog
-> Property 'Oplog' is missing XML documentation
- Line 15, MissingDoc, Error, Property, SnapshotMetadata
-> Property 'SnapshotMetadata' is missing XML documentation
- Line 16, MissingDoc, Error, Property, RemotePeers
-> Property 'RemotePeers' is missing XML documentation
- Line 21, MissingDoc, Error, Property, Collection
-> Property 'Collection' is missing XML documentation
- Line 22, MissingDoc, Error, Property, Key
-> Property 'Key' is missing XML documentation
- Line 23, MissingDoc, Error, Property, JsonData
-> Property 'JsonData' is missing XML documentation
- Line 24, MissingDoc, Error, Property, IsDeleted
-> Property 'IsDeleted' is missing XML documentation
- Line 25, MissingDoc, Error, Property, HlcWall
-> Property 'HlcWall' is missing XML documentation
- Line 26, MissingDoc, Error, Property, HlcLogic
-> Property 'HlcLogic' is missing XML documentation
- Line 27, MissingDoc, Error, Property, HlcNode
-> Property 'HlcNode' is missing XML documentation
- Line 32, MissingDoc, Error, Property, Collection
-> Property 'Collection' is missing XML documentation
- Line 33, MissingDoc, Error, Property, Key
-> Property 'Key' is missing XML documentation
- Line 34, MissingDoc, Error, Property, Operation
-> Property 'Operation' is missing XML documentation
- Line 35, MissingDoc, Error, Property, JsonData
-> Property 'JsonData' is missing XML documentation
- Line 36, MissingDoc, Error, Property, HlcWall
-> Property 'HlcWall' is missing XML documentation
- Line 37, MissingDoc, Error, Property, HlcLogic
-> Property 'HlcLogic' is missing XML documentation
- Line 38, MissingDoc, Error, Property, HlcNode
-> Property 'HlcNode' is missing XML documentation
- Line 39, MissingDoc, Error, Property, Hash
-> Property 'Hash' is missing XML documentation
- Line 40, MissingDoc, Error, Property, PreviousHash
-> Property 'PreviousHash' is missing XML documentation
- Line 45, MissingDoc, Error, Property, NodeId
-> Property 'NodeId' is missing XML documentation
- Line 46, MissingDoc, Error, Property, HlcWall
-> Property 'HlcWall' is missing XML documentation
- Line 47, MissingDoc, Error, Property, HlcLogic
-> Property 'HlcLogic' is missing XML documentation
- Line 48, MissingDoc, Error, Property, Hash
-> Property 'Hash' is missing XML documentation
- Line 53, MissingDoc, Error, Property, NodeId
-> Property 'NodeId' is missing XML documentation
- Line 54, MissingDoc, Error, Property, Address
-> Property 'Address' is missing XML documentation
- Line 55, MissingDoc, Error, Property, Type
-> Property 'Type' is missing XML documentation
- Line 56, MissingDoc, Error, Property, OAuth2Json
-> Property 'OAuth2Json' is missing XML documentation
- Line 57, MissingDoc, Error, Property, IsEnabled
-> Property 'IsEnabled' is missing XML documentation
FILE: /Users/dohertj2/Desktop/CBDDC/src/ZB.MOM.WW.CBDDC.Network/SyncOrchestrator.cs
Issues:
- Line 44, MissingDoc, Error, Property, FailureCount
-> Property 'FailureCount' is missing XML documentation
- Line 45, MissingDoc, Error, Property, NextRetryTime
-> Property 'NextRetryTime' is missing XML documentation
- Line 50, MissingDoc, Error, Constructor, SyncOrchestrator(IDiscoveryService discovery, IOplogStore oplogStore, IDocumentStore documentStore, ISnapshotMetadataStore snapshotStore, ISnapshotService snapshotService, IPeerNodeConfigurationProvider peerNodeConfigurationProvider, ILoggerFactory loggerFactory, IPeerHandshakeService? handshakeService, INetworkTelemetryService? telemetry)
-> Constructor 'SyncOrchestrator(IDiscoveryService discovery, IOplogStore oplogStore, IDocumentStore documentStore, ISnapshotMetadataStore snapshotStore, ISnapshotService snapshotService, IPeerNodeConfigurationProvider peerNodeConfigurationProvider, ILoggerFactory loggerFactory, IPeerHandshakeService? handshakeService, INetworkTelemetryService? telemetry)' is missing XML documentation.
- Line 73, MissingDoc, Error, Method, Start()
-> Method 'Start()' is missing XML documentation.
- Line 101, MissingDoc, Error, Method, Stop()
-> Method 'Stop()' is missing XML documentation.
FILE: /Users/dohertj2/Desktop/CBDDC/src/ZB.MOM.WW.CBDDC.Persistence/PeerConfigurationStore.cs
Issues:
- Line 23, MissingDoc, Error, Method, DropAsync(CancellationToken cancellationToken)
-> Method 'DropAsync(CancellationToken cancellationToken)' is missing XML documentation.
- Line 25, MissingDoc, Error, Method, ExportAsync(CancellationToken cancellationToken)
-> Method 'ExportAsync(CancellationToken cancellationToken)' is missing XML documentation.
- Line 27, MissingDoc, Error, Method, ImportAsync(IEnumerable<RemotePeerConfiguration> items, CancellationToken cancellationToken)
-> Method 'ImportAsync(IEnumerable<RemotePeerConfiguration> items, CancellationToken cancellationToken)' is missing XML documentation.
- Line 35, MissingDoc, Error, Method, MergeAsync(IEnumerable<RemotePeerConfiguration> items, CancellationToken cancellationToken)
-> Method 'MergeAsync(IEnumerable<RemotePeerConfiguration> items, CancellationToken cancellationToken)' is missing XML documentation.
FILE: /Users/dohertj2/Desktop/CBDDC/src/ZB.MOM.WW.CBDDC.Network/IDiscoveryService.cs
Issues:
- Line 9, MissingDoc, Error, Method, GetActivePeers()
-> Method 'GetActivePeers()' is missing XML documentation.
- Line 10, MissingDoc, Error, Method, Start()
-> Method 'Start()' is missing XML documentation.
- Line 11, MissingDoc, Error, Method, Stop()
-> Method 'Stop()' is missing XML documentation.
FILE: /Users/dohertj2/Desktop/CBDDC/src/ZB.MOM.WW.CBDDC.Core/Storage/IDocumentMetadataStore.cs
Issues:
- Line 89, MissingDoc, Error, Constructor, DocumentMetadata()
-> Constructor 'DocumentMetadata()' is missing XML documentation.
- Line 91, MissingDoc, Error, Constructor, DocumentMetadata(string collection, string key, HlcTimestamp updatedAt, bool isDeleted)
-> Constructor 'DocumentMetadata(string collection, string key, HlcTimestamp updatedAt, bool isDeleted)' is missing XML documentation.
FILE: /Users/dohertj2/Desktop/CBDDC/src/ZB.MOM.WW.CBDDC.Core/Diagnostics/SyncStatusTracker.cs
Issues:
- Line 25, MissingDoc, Error, Constructor, SyncStatusTracker(ILogger<SyncStatusTracker>? logger)
-> Constructor 'SyncStatusTracker(ILogger<SyncStatusTracker>? logger)' is missing XML documentation.
- Line 33, MissingParam, Warning, Method, SetOnlineStatus(bool isOnline)
-> Method 'SetOnlineStatus(bool isOnline)' is missing <param name="isOnline"> documentation.
- Line 48, MissingParam, Warning, Method, RecordSync(int documentCount, long bytesTransferred)
-> Method 'RecordSync(int documentCount, long bytesTransferred)' is missing <param name="bytesTransferred"> documentation.
- Line 48, MissingParam, Warning, Method, RecordSync(int documentCount, long bytesTransferred)
-> Method 'RecordSync(int documentCount, long bytesTransferred)' is missing <param name="documentCount"> documentation.
- Line 63, MissingParam, Warning, Method, RecordError(string message, string? peerNodeId, string? errorCode)
-> Method 'RecordError(string message, string? peerNodeId, string? errorCode)' is missing <param name="errorCode"> documentation.
- Line 63, MissingParam, Warning, Method, RecordError(string message, string? peerNodeId, string? errorCode)
-> Method 'RecordError(string message, string? peerNodeId, string? errorCode)' is missing <param name="peerNodeId"> documentation.
- Line 63, MissingParam, Warning, Method, RecordError(string message, string? peerNodeId, string? errorCode)
-> Method 'RecordError(string message, string? peerNodeId, string? errorCode)' is missing <param name="message"> documentation.
- Line 89, MissingParam, Warning, Method, UpdatePeer(string nodeId, string address, bool isConnected)
-> Method 'UpdatePeer(string nodeId, string address, bool isConnected)' is missing <param name="isConnected"> documentation.
- Line 89, MissingParam, Warning, Method, UpdatePeer(string nodeId, string address, bool isConnected)
-> Method 'UpdatePeer(string nodeId, string address, bool isConnected)' is missing <param name="address"> documentation.
- Line 89, MissingParam, Warning, Method, UpdatePeer(string nodeId, string address, bool isConnected)
-> Method 'UpdatePeer(string nodeId, string address, bool isConnected)' is missing <param name="nodeId"> documentation.
- Line 119, MissingParam, Warning, Method, RecordPeerSuccess(string nodeId)
-> Method 'RecordPeerSuccess(string nodeId)' is missing <param name="nodeId"> documentation.
- Line 134, MissingParam, Warning, Method, RecordPeerFailure(string nodeId)
-> Method 'RecordPeerFailure(string nodeId)' is missing <param name="nodeId"> documentation.
- Line 169, MissingParam, Warning, Method, CleanupInactivePeers(TimeSpan inactiveThreshold)
-> Method 'CleanupInactivePeers(TimeSpan inactiveThreshold)' is missing <param name="inactiveThreshold"> documentation.
Execution rules:
1. Read each file and apply all listed documentation fixes.
2. Keep changes minimal and doc-focused.
3. Avoid duplicate XML tags or malformed XML.
4. Return a short summary: files modified, issue count fixed per file, and any unresolved items.

174
docs-fix-batch-3.md Normal file
View File

@@ -0,0 +1,174 @@
You are a C# documentation expert. Fix the following XML documentation issues in the listed files.
IMPORTANT: Make ONLY documentation changes. Do NOT modify code logic, behavior, signatures, or formatting unrelated to XML docs.
Model requirement: GPT-5.3-Codex-Spark
Reasoning level: medium
Issue handling rules:
- MissingDoc: add concise XML docs with <summary> (and <returns> where appropriate)
- MissingInheritDoc: use /// <inheritdoc /> for overrides/interface implementations where appropriate
- MissingParam: add missing <param name="...">...</param> tags
- MissingTypeParam: add missing <typeparam name="...">...</typeparam> tags
- MalformedXml: repair XML syntax and invalid tags
Preserve local style and keep descriptions concise.
=== BATCH 3 OF 6 ===
FILE: /Users/dohertj2/Desktop/CBDDC/src/ZB.MOM.WW.CBDDC.Core/Exceptions/CBDDCExceptions.cs
Issues:
- Line 15, MissingDoc, Error, Constructor, CBDDCException(string errorCode, string message)
-> Constructor 'CBDDCException(string errorCode, string message)' is missing XML documentation.
- Line 21, MissingDoc, Error, Constructor, CBDDCException(string errorCode, string message, Exception innerException)
-> Constructor 'CBDDCException(string errorCode, string message, Exception innerException)' is missing XML documentation.
- Line 33, MissingDoc, Error, Constructor, NetworkException(string message)
-> Constructor 'NetworkException(string message)' is missing XML documentation.
- Line 36, MissingDoc, Error, Constructor, NetworkException(string message, Exception innerException)
-> Constructor 'NetworkException(string message, Exception innerException)' is missing XML documentation.
- Line 45, MissingDoc, Error, Constructor, PersistenceException(string message)
-> Constructor 'PersistenceException(string message)' is missing XML documentation.
- Line 48, MissingDoc, Error, Constructor, PersistenceException(string message, Exception innerException)
-> Constructor 'PersistenceException(string message, Exception innerException)' is missing XML documentation.
- Line 57, MissingDoc, Error, Constructor, SyncException(string message)
-> Constructor 'SyncException(string message)' is missing XML documentation.
- Line 60, MissingDoc, Error, Constructor, SyncException(string message, Exception innerException)
-> Constructor 'SyncException(string message, Exception innerException)' is missing XML documentation.
- Line 69, MissingDoc, Error, Constructor, ConfigurationException(string message)
-> Constructor 'ConfigurationException(string message)' is missing XML documentation.
- Line 78, MissingDoc, Error, Constructor, DatabaseCorruptionException(string message)
-> Constructor 'DatabaseCorruptionException(string message)' is missing XML documentation.
- Line 81, MissingDoc, Error, Constructor, DatabaseCorruptionException(string message, Exception innerException)
-> Constructor 'DatabaseCorruptionException(string message, Exception innerException)' is missing XML documentation.
- Line 90, MissingDoc, Error, Constructor, TimeoutException(string operation, int timeoutMs)
-> Constructor 'TimeoutException(string operation, int timeoutMs)' is missing XML documentation.
- Line 97, MissingDoc, Error, Property, Key
-> Property 'Key' is missing XML documentation
- Line 98, MissingDoc, Error, Property, Collection
-> Property 'Collection' is missing XML documentation
- Line 100, MissingDoc, Error, Constructor, DocumentNotFoundException(string collection, string key)
-> Constructor 'DocumentNotFoundException(string collection, string key)' is missing XML documentation.
- Line 110, MissingDoc, Error, Constructor, CBDDCConcurrencyException(string message)
-> Constructor 'CBDDCConcurrencyException(string message)' is missing XML documentation.
FILE: /Users/dohertj2/Desktop/CBDDC/src/ZB.MOM.WW.CBDDC.Core/Document.cs
Issues:
- Line 9, MissingDoc, Error, Property, Collection
-> Property 'Collection' is missing XML documentation
- Line 10, MissingDoc, Error, Property, Key
-> Property 'Key' is missing XML documentation
- Line 11, MissingDoc, Error, Property, Content
-> Property 'Content' is missing XML documentation
- Line 12, MissingDoc, Error, Property, UpdatedAt
-> Property 'UpdatedAt' is missing XML documentation
- Line 13, MissingDoc, Error, Property, IsDeleted
-> Property 'IsDeleted' is missing XML documentation
- Line 15, MissingDoc, Error, Constructor, Document(string collection, string key, JsonElement content, HlcTimestamp updatedAt, bool isDeleted)
-> Constructor 'Document(string collection, string key, JsonElement content, HlcTimestamp updatedAt, bool isDeleted)' is missing XML documentation.
- Line 24, MissingDoc, Error, Method, Merge(OplogEntry oplogEntry, IConflictResolver? resolver)
-> Method 'Merge(OplogEntry oplogEntry, IConflictResolver? resolver)' is missing XML documentation.
FILE: /Users/dohertj2/Desktop/CBDDC/src/ZB.MOM.WW.CBDDC.Core/Management/PeerManagementService.cs
Issues:
- Line 39, MissingDoc, Error, Method, AddCloudPeerAsync(string nodeId, string address, OAuth2Configuration oauth2Config, CancellationToken cancellationToken)
-> Method 'AddCloudPeerAsync(string nodeId, string address, OAuth2Configuration oauth2Config, CancellationToken cancellationToken)' is missing XML documentation.
- Line 60, MissingDoc, Error, Method, AddStaticPeerAsync(string nodeId, string address, CancellationToken cancellationToken)
-> Method 'AddStaticPeerAsync(string nodeId, string address, CancellationToken cancellationToken)' is missing XML documentation.
- Line 78, MissingDoc, Error, Method, RemoveRemotePeerAsync(string nodeId, CancellationToken cancellationToken)
-> Method 'RemoveRemotePeerAsync(string nodeId, CancellationToken cancellationToken)' is missing XML documentation.
- Line 86, MissingDoc, Error, Method, GetAllRemotePeersAsync(CancellationToken cancellationToken)
-> Method 'GetAllRemotePeersAsync(CancellationToken cancellationToken)' is missing XML documentation.
- Line 91, MissingDoc, Error, Method, EnablePeerAsync(string nodeId, CancellationToken cancellationToken)
-> Method 'EnablePeerAsync(string nodeId, CancellationToken cancellationToken)' is missing XML documentation.
- Line 110, MissingDoc, Error, Method, DisablePeerAsync(string nodeId, CancellationToken cancellationToken)
-> Method 'DisablePeerAsync(string nodeId, CancellationToken cancellationToken)' is missing XML documentation.
FILE: /Users/dohertj2/Desktop/CBDDC/src/ZB.MOM.WW.CBDDC.Core/Network/StaticPeerNodeConfigurationProvider.cs
Issues:
- Line 8, MissingDoc, Error, Property, Configuration
-> Property 'Configuration' is missing XML documentation
- Line 21, MissingDoc, Error, Constructor, StaticPeerNodeConfigurationProvider(PeerNodeConfiguration configuration)
-> Constructor 'StaticPeerNodeConfigurationProvider(PeerNodeConfiguration configuration)' is missing XML documentation.
- Line 26, MissingDoc, Error, Event, ConfigurationChanged
-> Event 'ConfigurationChanged' is missing XML documentation.
- Line 28, MissingDoc, Error, Method, GetConfiguration()
-> Method 'GetConfiguration()' is missing XML documentation.
- Line 33, MissingDoc, Error, Method, OnConfigurationChanged(PeerNodeConfiguration newConfig)
-> Method 'OnConfigurationChanged(PeerNodeConfiguration newConfig)' is missing XML documentation.
FILE: /Users/dohertj2/Desktop/CBDDC/src/ZB.MOM.WW.CBDDC.Core/Cache/DocumentCache.cs
Issues:
- Line 16, MissingDoc, Error, Property, Document
-> Property 'Document' is missing XML documentation
- Line 17, MissingDoc, Error, Property, Node
-> Property 'Node' is missing XML documentation
- Line 19, MissingDoc, Error, Constructor, CacheEntry(Document document, LinkedListNode<string> node)
-> Constructor 'CacheEntry(Document document, LinkedListNode<string> node)' is missing XML documentation.
- Line 41, MissingDoc, Error, Constructor, DocumentCache(IPeerNodeConfigurationProvider peerNodeConfigurationProvider, ILogger<DocumentCache>? logger)
-> Constructor 'DocumentCache(IPeerNodeConfigurationProvider peerNodeConfigurationProvider, ILogger<DocumentCache>? logger)' is missing XML documentation.
- Line 50, MissingParam, Warning, Method, Get(string collection, string key)
-> Method 'Get(string collection, string key)' is missing <param name="key"> documentation.
- Line 50, MissingParam, Warning, Method, Get(string collection, string key)
-> Method 'Get(string collection, string key)' is missing <param name="collection"> documentation.
- Line 76, MissingParam, Warning, Method, Set(string collection, string key, Document document)
-> Method 'Set(string collection, string key, Document document)' is missing <param name="document"> documentation.
- Line 76, MissingParam, Warning, Method, Set(string collection, string key, Document document)
-> Method 'Set(string collection, string key, Document document)' is missing <param name="key"> documentation.
- Line 76, MissingParam, Warning, Method, Set(string collection, string key, Document document)
-> Method 'Set(string collection, string key, Document document)' is missing <param name="collection"> documentation.
- Line 112, MissingParam, Warning, Method, Remove(string collection, string key)
-> Method 'Remove(string collection, string key)' is missing <param name="key"> documentation.
- Line 112, MissingParam, Warning, Method, Remove(string collection, string key)
-> Method 'Remove(string collection, string key)' is missing <param name="collection"> documentation.
FILE: /Users/dohertj2/Desktop/CBDDC/src/ZB.MOM.WW.CBDDC.Network/CompositeDiscoveryService.cs
Issues:
- Line 53, MissingDoc, Error, Method, GetActivePeers()
-> Method 'GetActivePeers()' is missing XML documentation.
- Line 62, MissingDoc, Error, Method, Start()
-> Method 'Start()' is missing XML documentation.
- Line 97, MissingDoc, Error, Method, Stop()
-> Method 'Stop()' is missing XML documentation.
FILE: /Users/dohertj2/Desktop/CBDDC/src/ZB.MOM.WW.CBDDC.Core/Network/IPeerNodeConfigurationProvider.cs
Issues:
- Line 6, MissingDoc, Error, Delegate, PeerNodeConfigurationChangedEventHandler
-> Delegate 'PeerNodeConfigurationChangedEventHandler' is missing XML documentation.
- Line 21, MalformedXml, Error, Method, GetConfiguration()
-> Method 'GetConfiguration()' has malformed XML documentation that cannot be parsed.
FILE: /Users/dohertj2/Desktop/CBDDC/src/ZB.MOM.WW.CBDDC.Network/ISyncOrchestrator.cs
Issues:
- Line 7, MissingDoc, Error, Method, Start()
-> Method 'Start()' is missing XML documentation.
- Line 8, MissingDoc, Error, Method, Stop()
-> Method 'Stop()' is missing XML documentation.
FILE: /Users/dohertj2/Desktop/CBDDC/src/ZB.MOM.WW.CBDDC.Core/Sync/OfflineQueue.cs
Issues:
- Line 22, MissingDoc, Error, Constructor, OfflineQueue(IPeerNodeConfigurationProvider peerNodeConfigurationProvider, ILogger<OfflineQueue>? logger)
-> Constructor 'OfflineQueue(IPeerNodeConfigurationProvider peerNodeConfigurationProvider, ILogger<OfflineQueue>? logger)' is missing XML documentation.
- Line 45, MissingParam, Warning, Method, Enqueue(PendingOperation operation)
-> Method 'Enqueue(PendingOperation operation)' is missing <param name="operation"> documentation.
- Line 66, MissingParam, Warning, Method, FlushAsync(Func<PendingOperation, Task> executor, CancellationToken cancellationToken)
-> Method 'FlushAsync(Func<PendingOperation, Task> executor, CancellationToken cancellationToken)' is missing <param name="cancellationToken"> documentation.
- Line 66, MissingParam, Warning, Method, FlushAsync(Func<PendingOperation, Task> executor, CancellationToken cancellationToken)
-> Method 'FlushAsync(Func<PendingOperation, Task> executor, CancellationToken cancellationToken)' is missing <param name="executor"> documentation.
FILE: /Users/dohertj2/Desktop/CBDDC/src/ZB.MOM.WW.CBDDC.Core/Sync/RecursiveNodeMergeConflictResolver.cs
Issues:
- Line 11, MissingDoc, Error, Method, Resolve(Document? local, OplogEntry remote)
-> Method 'Resolve(Document? local, OplogEntry remote)' is missing XML documentation.
FILE: /Users/dohertj2/Desktop/CBDDC/src/ZB.MOM.WW.CBDDC.Network/PeerDbNetworkExtensions.cs
Issues:
- Line 19, MissingTypeParam, Warning, Method, AddCBDDCNetwork(IServiceCollection services, bool useHostedService)
-> Method 'AddCBDDCNetwork(IServiceCollection services, bool useHostedService)' is missing <typeparam name="TPeerNodeConfigurationProvider"> documentation.
- Line 19, MissingParam, Warning, Method, AddCBDDCNetwork(IServiceCollection services, bool useHostedService)
-> Method 'AddCBDDCNetwork(IServiceCollection services, bool useHostedService)' is missing <param name="services"> documentation.
Execution rules:
1. Read each file and apply all listed documentation fixes.
2. Keep changes minimal and doc-focused.
3. Avoid duplicate XML tags or malformed XML.
4. Return a short summary: files modified, issue count fixed per file, and any unresolved items.

200
docs-fix-batch-4.md Normal file
View File

@@ -0,0 +1,200 @@
You are a C# documentation expert. Fix the following XML documentation issues in the listed files.
IMPORTANT: Make ONLY documentation changes. Do NOT modify code logic, behavior, signatures, or formatting unrelated to XML docs.
Model requirement: GPT-5.3-Codex-Spark
Reasoning level: medium
Issue handling rules:
- MissingDoc: add concise XML docs with <summary> (and <returns> where appropriate)
- MissingInheritDoc: use /// <inheritdoc /> for overrides/interface implementations where appropriate
- MissingParam: add missing <param name="...">...</param> tags
- MissingTypeParam: add missing <typeparam name="...">...</typeparam> tags
- MalformedXml: repair XML syntax and invalid tags
Preserve local style and keep descriptions concise.
=== BATCH 4 OF 6 ===
FILE: /Users/dohertj2/Desktop/CBDDC/src/ZB.MOM.WW.CBDDC.Network/Telemetry/NetworkTelemetryService.cs
Issues:
- Line 33, MissingDoc, Error, Constructor, NetworkTelemetryService(ILogger<NetworkTelemetryService> logger, string persistencePath)
-> Constructor 'NetworkTelemetryService(ILogger<NetworkTelemetryService> logger, string persistencePath)' is missing XML documentation.
- Line 53, MissingDoc, Error, Method, RecordValue(MetricType type, double value)
-> Method 'RecordValue(MetricType type, double value)' is missing XML documentation.
- Line 58, MissingDoc, Error, Method, StartMetric(MetricType type)
-> Method 'StartMetric(MetricType type)' is missing XML documentation.
- Line 63, MissingDoc, Error, Method, GetSnapshot()
-> Method 'GetSnapshot()' is missing XML documentation.
- Line 173, MissingDoc, Error, Method, ForcePersist()
-> Method 'ForcePersist()' is missing XML documentation.
- Line 201, MissingDoc, Error, Method, Dispose()
-> Method 'Dispose()' is missing XML documentation.
- Line 215, MissingDoc, Error, Constructor, MetricBucket()
-> Constructor 'MetricBucket()' is missing XML documentation.
- Line 222, MissingDoc, Error, Method, Reset()
-> Method 'Reset()' is missing XML documentation.
- Line 228, MissingDoc, Error, Method, Add(MetricType type, double value)
-> Method 'Add(MetricType type, double value)' is missing XML documentation.
- Line 235, MissingDoc, Error, Method, GetSum(MetricType type)
-> Method 'GetSum(MetricType type)' is missing XML documentation.
- Line 236, MissingDoc, Error, Method, GetCount(MetricType type)
-> Method 'GetCount(MetricType type)' is missing XML documentation.
FILE: /Users/dohertj2/Desktop/CBDDC/src/ZB.MOM.WW.CBDDC.Network/TcpPeerClient.cs
Issues:
- Line 37, MissingDoc, Error, Property, IsConnected
-> Property 'IsConnected' is missing XML documentation
- Line 48, MissingDoc, Error, Property, HasHandshaked
-> Property 'HasHandshaked' is missing XML documentation
- Line 52, MissingDoc, Error, Constructor, TcpPeerClient(string peerAddress, ILogger logger, IPeerHandshakeService? handshakeService, INetworkTelemetryService? telemetry)
-> Constructor 'TcpPeerClient(string peerAddress, ILogger logger, IPeerHandshakeService? handshakeService, INetworkTelemetryService? telemetry)' is missing XML documentation.
- Line 62, MissingDoc, Error, Method, ConnectAsync(CancellationToken token)
-> Method 'ConnectAsync(CancellationToken token)' is missing XML documentation.
- Line 143, MissingParam, Warning, Method, HandshakeAsync(string myNodeId, string authToken, IEnumerable<string>? interestingCollections, CancellationToken token)
-> Method 'HandshakeAsync(string myNodeId, string authToken, IEnumerable<string>? interestingCollections, CancellationToken token)' is missing <param name="token"> documentation.
- Line 143, MissingParam, Warning, Method, HandshakeAsync(string myNodeId, string authToken, IEnumerable<string>? interestingCollections, CancellationToken token)
-> Method 'HandshakeAsync(string myNodeId, string authToken, IEnumerable<string>? interestingCollections, CancellationToken token)' is missing <param name="interestingCollections"> documentation.
- Line 143, MissingParam, Warning, Method, HandshakeAsync(string myNodeId, string authToken, IEnumerable<string>? interestingCollections, CancellationToken token)
-> Method 'HandshakeAsync(string myNodeId, string authToken, IEnumerable<string>? interestingCollections, CancellationToken token)' is missing <param name="authToken"> documentation.
- Line 143, MissingParam, Warning, Method, HandshakeAsync(string myNodeId, string authToken, IEnumerable<string>? interestingCollections, CancellationToken token)
-> Method 'HandshakeAsync(string myNodeId, string authToken, IEnumerable<string>? interestingCollections, CancellationToken token)' is missing <param name="myNodeId"> documentation.
- Line 196, MissingParam, Warning, Method, GetClockAsync(CancellationToken token)
-> Method 'GetClockAsync(CancellationToken token)' is missing <param name="token"> documentation.
- Line 213, MissingParam, Warning, Method, GetVectorClockAsync(CancellationToken token)
-> Method 'GetVectorClockAsync(CancellationToken token)' is missing <param name="token"> documentation.
- Line 237, MissingParam, Warning, Method, PullChangesAsync(HlcTimestamp since, CancellationToken token)
-> Method 'PullChangesAsync(HlcTimestamp since, CancellationToken token)' is missing <param name="token"> documentation.
- Line 237, MissingParam, Warning, Method, PullChangesAsync(HlcTimestamp since, CancellationToken token)
-> Method 'PullChangesAsync(HlcTimestamp since, CancellationToken token)' is missing <param name="since"> documentation.
- Line 245, MissingParam, Warning, Method, PullChangesAsync(HlcTimestamp since, IEnumerable<string>? collections, CancellationToken token)
-> Method 'PullChangesAsync(HlcTimestamp since, IEnumerable<string>? collections, CancellationToken token)' is missing <param name="token"> documentation.
- Line 245, MissingParam, Warning, Method, PullChangesAsync(HlcTimestamp since, IEnumerable<string>? collections, CancellationToken token)
-> Method 'PullChangesAsync(HlcTimestamp since, IEnumerable<string>? collections, CancellationToken token)' is missing <param name="collections"> documentation.
- Line 245, MissingParam, Warning, Method, PullChangesAsync(HlcTimestamp since, IEnumerable<string>? collections, CancellationToken token)
-> Method 'PullChangesAsync(HlcTimestamp since, IEnumerable<string>? collections, CancellationToken token)' is missing <param name="since"> documentation.
- Line 281, MissingParam, Warning, Method, PullChangesFromNodeAsync(string nodeId, HlcTimestamp since, CancellationToken token)
-> Method 'PullChangesFromNodeAsync(string nodeId, HlcTimestamp since, CancellationToken token)' is missing <param name="token"> documentation.
- Line 281, MissingParam, Warning, Method, PullChangesFromNodeAsync(string nodeId, HlcTimestamp since, CancellationToken token)
-> Method 'PullChangesFromNodeAsync(string nodeId, HlcTimestamp since, CancellationToken token)' is missing <param name="since"> documentation.
- Line 281, MissingParam, Warning, Method, PullChangesFromNodeAsync(string nodeId, HlcTimestamp since, CancellationToken token)
-> Method 'PullChangesFromNodeAsync(string nodeId, HlcTimestamp since, CancellationToken token)' is missing <param name="nodeId"> documentation.
- Line 289, MissingParam, Warning, Method, PullChangesFromNodeAsync(string nodeId, HlcTimestamp since, IEnumerable<string>? collections, CancellationToken token)
-> Method 'PullChangesFromNodeAsync(string nodeId, HlcTimestamp since, IEnumerable<string>? collections, CancellationToken token)' is missing <param name="token"> documentation.
- Line 289, MissingParam, Warning, Method, PullChangesFromNodeAsync(string nodeId, HlcTimestamp since, IEnumerable<string>? collections, CancellationToken token)
-> Method 'PullChangesFromNodeAsync(string nodeId, HlcTimestamp since, IEnumerable<string>? collections, CancellationToken token)' is missing <param name="collections"> documentation.
- Line 289, MissingParam, Warning, Method, PullChangesFromNodeAsync(string nodeId, HlcTimestamp since, IEnumerable<string>? collections, CancellationToken token)
-> Method 'PullChangesFromNodeAsync(string nodeId, HlcTimestamp since, IEnumerable<string>? collections, CancellationToken token)' is missing <param name="since"> documentation.
- Line 289, MissingParam, Warning, Method, PullChangesFromNodeAsync(string nodeId, HlcTimestamp since, IEnumerable<string>? collections, CancellationToken token)
-> Method 'PullChangesFromNodeAsync(string nodeId, HlcTimestamp since, IEnumerable<string>? collections, CancellationToken token)' is missing <param name="nodeId"> documentation.
- Line 325, MissingParam, Warning, Method, GetChainRangeAsync(string startHash, string endHash, CancellationToken token)
-> Method 'GetChainRangeAsync(string startHash, string endHash, CancellationToken token)' is missing <param name="token"> documentation.
- Line 325, MissingParam, Warning, Method, GetChainRangeAsync(string startHash, string endHash, CancellationToken token)
-> Method 'GetChainRangeAsync(string startHash, string endHash, CancellationToken token)' is missing <param name="endHash"> documentation.
- Line 325, MissingParam, Warning, Method, GetChainRangeAsync(string startHash, string endHash, CancellationToken token)
-> Method 'GetChainRangeAsync(string startHash, string endHash, CancellationToken token)' is missing <param name="startHash"> documentation.
- Line 351, MissingParam, Warning, Method, PushChangesAsync(IEnumerable<OplogEntry> entries, CancellationToken token)
-> Method 'PushChangesAsync(IEnumerable<OplogEntry> entries, CancellationToken token)' is missing <param name="token"> documentation.
- Line 351, MissingParam, Warning, Method, PushChangesAsync(IEnumerable<OplogEntry> entries, CancellationToken token)
-> Method 'PushChangesAsync(IEnumerable<OplogEntry> entries, CancellationToken token)' is missing <param name="entries"> documentation.
- Line 387, MissingDoc, Error, Method, GetSnapshotAsync(Stream destination, CancellationToken token)
-> Method 'GetSnapshotAsync(Stream destination, CancellationToken token)' is missing XML documentation.
- Line 406, MissingDoc, Error, Method, Dispose()
-> Method 'Dispose()' is missing XML documentation.
- Line 438, MissingDoc, Error, Constructor, SnapshotRequiredException()
-> Constructor 'SnapshotRequiredException()' is missing XML documentation.
FILE: /Users/dohertj2/Desktop/CBDDC/src/ZB.MOM.WW.CBDDC.Core/Sync/IConflictResolver.cs
Issues:
- Line 8, MissingDoc, Error, Property, ShouldApply
-> Property 'ShouldApply' is missing XML documentation
- Line 9, MissingDoc, Error, Property, MergedDocument
-> Property 'MergedDocument' is missing XML documentation
- Line 11, MissingDoc, Error, Constructor, ConflictResolutionResult(bool shouldApply, Document? mergedDocument)
-> Constructor 'ConflictResolutionResult(bool shouldApply, Document? mergedDocument)' is missing XML documentation.
- Line 17, MissingDoc, Error, Method, Apply(Document document)
-> Method 'Apply(Document document)' is missing XML documentation.
- Line 18, MissingDoc, Error, Method, Ignore()
-> Method 'Ignore()' is missing XML documentation.
- Line 23, MissingDoc, Error, Method, Resolve(Document? local, OplogEntry remote)
-> Method 'Resolve(Document? local, OplogEntry remote)' is missing XML documentation.
FILE: /Users/dohertj2/Desktop/CBDDC/src/ZB.MOM.WW.CBDDC.Core/Sync/PendingOperation.cs
Issues:
- Line 12, MissingDoc, Error, Property, Type
-> Property 'Type' is missing XML documentation
- Line 13, MissingDoc, Error, Property, Collection
-> Property 'Collection' is missing XML documentation
- Line 14, MissingDoc, Error, Property, Key
-> Property 'Key' is missing XML documentation
- Line 15, MissingDoc, Error, Property, Data
-> Property 'Data' is missing XML documentation
- Line 16, MissingDoc, Error, Property, QueuedAt
-> Property 'QueuedAt' is missing XML documentation
FILE: /Users/dohertj2/Desktop/CBDDC/src/ZB.MOM.WW.CBDDC.Core/SnapshotMetadata.cs
Issues:
- Line 5, MissingDoc, Error, Property, NodeId
-> Property 'NodeId' is missing XML documentation
- Line 6, MissingDoc, Error, Property, TimestampPhysicalTime
-> Property 'TimestampPhysicalTime' is missing XML documentation
- Line 7, MissingDoc, Error, Property, TimestampLogicalCounter
-> Property 'TimestampLogicalCounter' is missing XML documentation
- Line 8, MissingDoc, Error, Property, Hash
-> Property 'Hash' is missing XML documentation
FILE: /Users/dohertj2/Desktop/CBDDC/src/ZB.MOM.WW.CBDDC.Network/Security/IPeerHandshakeService.cs
Issues:
- Line 12, MissingParam, Warning, Method, HandshakeAsync(System.IO.Stream stream, bool isInitiator, string myNodeId, CancellationToken token)
-> Method 'HandshakeAsync(System.IO.Stream stream, bool isInitiator, string myNodeId, CancellationToken token)' is missing <param name="token"> documentation.
- Line 12, MissingParam, Warning, Method, HandshakeAsync(System.IO.Stream stream, bool isInitiator, string myNodeId, CancellationToken token)
-> Method 'HandshakeAsync(System.IO.Stream stream, bool isInitiator, string myNodeId, CancellationToken token)' is missing <param name="myNodeId"> documentation.
- Line 12, MissingParam, Warning, Method, HandshakeAsync(System.IO.Stream stream, bool isInitiator, string myNodeId, CancellationToken token)
-> Method 'HandshakeAsync(System.IO.Stream stream, bool isInitiator, string myNodeId, CancellationToken token)' is missing <param name="isInitiator"> documentation.
- Line 12, MissingParam, Warning, Method, HandshakeAsync(System.IO.Stream stream, bool isInitiator, string myNodeId, CancellationToken token)
-> Method 'HandshakeAsync(System.IO.Stream stream, bool isInitiator, string myNodeId, CancellationToken token)' is missing <param name="stream"> documentation.
- Line 17, MissingDoc, Error, Property, EncryptKey
-> Property 'EncryptKey' is missing XML documentation
- Line 18, MissingDoc, Error, Property, DecryptKey
-> Property 'DecryptKey' is missing XML documentation
- Line 22, MissingDoc, Error, Constructor, CipherState(byte[] encryptKey, byte[] decryptKey)
-> Constructor 'CipherState(byte[] encryptKey, byte[] decryptKey)' is missing XML documentation.
FILE: /Users/dohertj2/Desktop/CBDDC/src/ZB.MOM.WW.CBDDC.Network/Protocol/ProtocolHandler.cs
Issues:
- Line 24, MissingDoc, Error, Constructor, ProtocolHandler(ILogger logger, INetworkTelemetryService? telemetry)
-> Constructor 'ProtocolHandler(ILogger logger, INetworkTelemetryService? telemetry)' is missing XML documentation.
- Line 30, MissingDoc, Error, Method, SendMessageAsync(Stream stream, MessageType type, IMessage message, bool useCompression, CipherState? cipherState, CancellationToken token)
-> Method 'SendMessageAsync(Stream stream, MessageType type, IMessage message, bool useCompression, CipherState? cipherState, CancellationToken token)' is missing XML documentation.
- Line 109, MissingDoc, Error, Method, ReadMessageAsync(Stream stream, CipherState? cipherState, CancellationToken token)
-> Method 'ReadMessageAsync(Stream stream, CipherState? cipherState, CancellationToken token)' is missing XML documentation.
FILE: /Users/dohertj2/Desktop/CBDDC/src/ZB.MOM.WW.CBDDC.Persistence/NodeCacheEntry.cs
Issues:
- Line 7, MissingDoc, Error, Property, Timestamp
-> Property 'Timestamp' is missing XML documentation
- Line 8, MissingDoc, Error, Property, Hash
-> Property 'Hash' is missing XML documentation
FILE: /Users/dohertj2/Desktop/CBDDC/src/ZB.MOM.WW.CBDDC.Core/Diagnostics/ICBDDCHealthCheck.cs
Issues:
- Line 8, MissingDoc, Error, Method, CheckAsync(CancellationToken cancellationToken)
-> Method 'CheckAsync(CancellationToken cancellationToken)' is missing XML documentation.
FILE: /Users/dohertj2/Desktop/CBDDC/src/ZB.MOM.WW.CBDDC.Network/Security/IAuthenticator.cs
Issues:
- Line 7, MissingDoc, Error, Method, ValidateAsync(string nodeId, string token)
-> Method 'ValidateAsync(string nodeId, string token)' is missing XML documentation.
FILE: /Users/dohertj2/Desktop/CBDDC/src/ZB.MOM.WW.CBDDC.Network/TcpSyncServer.cs
Issues:
- Line 58, MissingParam, Warning, Constructor, TcpSyncServer(IOplogStore oplogStore, IDocumentStore documentStore, ISnapshotService snapshotStore, IPeerNodeConfigurationProvider peerNodeConfigurationProvider, ILogger<TcpSyncServer> logger, IAuthenticator authenticator, IPeerHandshakeService handshakeService, INetworkTelemetryService? telemetry)
-> Constructor 'TcpSyncServer(IOplogStore oplogStore, IDocumentStore documentStore, ISnapshotService snapshotStore, IPeerNodeConfigurationProvider peerNodeConfigurationProvider, ILogger<TcpSyncServer> logger, IAuthenticator authenticator, IPeerHandshakeService handshakeService, INetworkTelemetryService? telemetry)' is missing <param name="telemetry"> documentation.
- Line 58, MissingParam, Warning, Constructor, TcpSyncServer(IOplogStore oplogStore, IDocumentStore documentStore, ISnapshotService snapshotStore, IPeerNodeConfigurationProvider peerNodeConfigurationProvider, ILogger<TcpSyncServer> logger, IAuthenticator authenticator, IPeerHandshakeService handshakeService, INetworkTelemetryService? telemetry)
-> Constructor 'TcpSyncServer(IOplogStore oplogStore, IDocumentStore documentStore, ISnapshotService snapshotStore, IPeerNodeConfigurationProvider peerNodeConfigurationProvider, ILogger<TcpSyncServer> logger, IAuthenticator authenticator, IPeerHandshakeService handshakeService, INetworkTelemetryService? telemetry)' is missing <param name="documentStore"> documentation.
Execution rules:
1. Read each file and apply all listed documentation fixes.
2. Keep changes minimal and doc-focused.
3. Avoid duplicate XML tags or malformed XML.
4. Return a short summary: files modified, issue count fixed per file, and any unresolved items.

161
docs-fix-batch-5.md Normal file
View File

@@ -0,0 +1,161 @@
You are a C# documentation expert. Fix the following XML documentation issues in the listed files.
IMPORTANT: Make ONLY documentation changes. Do NOT modify code logic, behavior, signatures, or formatting unrelated to XML docs.
Model requirement: GPT-5.3-Codex-Spark
Reasoning level: medium
Issue handling rules:
- MissingDoc: add concise XML docs with <summary> (and <returns> where appropriate)
- MissingInheritDoc: use /// <inheritdoc /> for overrides/interface implementations where appropriate
- MissingParam: add missing <param name="...">...</param> tags
- MissingTypeParam: add missing <typeparam name="...">...</typeparam> tags
- MalformedXml: repair XML syntax and invalid tags
Preserve local style and keep descriptions concise.
=== BATCH 5 OF 6 ===
FILE: /Users/dohertj2/Desktop/CBDDC/src/ZB.MOM.WW.CBDDC.Core/HlcTimestamp.cs
Issues:
- Line 12, MissingDoc, Error, Property, PhysicalTime
-> Property 'PhysicalTime' is missing XML documentation
- Line 13, MissingDoc, Error, Property, LogicalCounter
-> Property 'LogicalCounter' is missing XML documentation
- Line 14, MissingDoc, Error, Property, NodeId
-> Property 'NodeId' is missing XML documentation
- Line 16, MissingDoc, Error, Constructor, HlcTimestamp(long physicalTime, int logicalCounter, string nodeId)
-> Constructor 'HlcTimestamp(long physicalTime, int logicalCounter, string nodeId)' is missing XML documentation.
- Line 27, MissingParam, Warning, Method, CompareTo(HlcTimestamp other)
-> Method 'CompareTo(HlcTimestamp other)' is missing <param name="other"> documentation.
- Line 39, MissingDoc, Error, Method, CompareTo(object? obj)
-> Method 'CompareTo(object? obj)' is missing XML documentation.
- Line 46, MissingDoc, Error, Method, Equals(HlcTimestamp other)
-> Method 'Equals(HlcTimestamp other)' is missing XML documentation.
- Line 53, MissingDoc, Error, Method, Equals(object? obj)
-> Method 'Equals(object? obj)' is missing XML documentation.
- Line 58, MissingDoc, Error, Method, GetHashCode()
-> Method 'GetHashCode()' is missing XML documentation.
- Line 80, MissingDoc, Error, Method, ToString()
-> Method 'ToString()' is missing XML documentation.
- Line 82, MissingDoc, Error, Method, Parse(string s)
-> Method 'Parse(string s)' is missing XML documentation.
FILE: /Users/dohertj2/Desktop/CBDDC/src/ZB.MOM.WW.CBDDC.Core/Diagnostics/ISyncStatusTracker.cs
Issues:
- Line 7, MissingDoc, Error, Method, CleanupInactivePeers(TimeSpan inactiveThreshold)
-> Method 'CleanupInactivePeers(TimeSpan inactiveThreshold)' is missing XML documentation.
- Line 8, MissingDoc, Error, Method, GetStatus()
-> Method 'GetStatus()' is missing XML documentation.
- Line 9, MissingDoc, Error, Method, RecordError(string message, string? peerNodeId, string? errorCode)
-> Method 'RecordError(string message, string? peerNodeId, string? errorCode)' is missing XML documentation.
- Line 10, MissingDoc, Error, Method, RecordPeerFailure(string nodeId)
-> Method 'RecordPeerFailure(string nodeId)' is missing XML documentation.
- Line 11, MissingDoc, Error, Method, RecordPeerSuccess(string nodeId)
-> Method 'RecordPeerSuccess(string nodeId)' is missing XML documentation.
- Line 12, MissingDoc, Error, Method, RecordSync(int documentCount, long bytesTransferred)
-> Method 'RecordSync(int documentCount, long bytesTransferred)' is missing XML documentation.
- Line 13, MissingDoc, Error, Method, SetOnlineStatus(bool isOnline)
-> Method 'SetOnlineStatus(bool isOnline)' is missing XML documentation.
- Line 14, MissingDoc, Error, Method, UpdatePeer(string nodeId, string address, bool isConnected)
-> Method 'UpdatePeer(string nodeId, string address, bool isConnected)' is missing XML documentation.
FILE: /Users/dohertj2/Desktop/CBDDC/src/ZB.MOM.WW.CBDDC.Persistence/OplogStore.cs
Issues:
- Line 22, MissingDoc, Error, Event, ChangesApplied
-> Event 'ChangesApplied' is missing XML documentation.
- Line 24, MissingDoc, Error, Method, OnChangesApplied(IEnumerable<OplogEntry> appliedEntries)
-> Method 'OnChangesApplied(IEnumerable<OplogEntry> appliedEntries)' is missing XML documentation.
- Line 29, MissingDoc, Error, Constructor, OplogStore(IDocumentStore documentStore, IConflictResolver conflictResolver, IVectorClockService vectorClockService, ISnapshotMetadataStore? snapshotMetadataStore)
-> Constructor 'OplogStore(IDocumentStore documentStore, IConflictResolver conflictResolver, IVectorClockService vectorClockService, ISnapshotMetadataStore? snapshotMetadataStore)' is missing XML documentation.
- Line 209, MissingDoc, Error, Method, DropAsync(CancellationToken cancellationToken)
-> Method 'DropAsync(CancellationToken cancellationToken)' is missing XML documentation.
- Line 211, MissingDoc, Error, Method, ExportAsync(CancellationToken cancellationToken)
-> Method 'ExportAsync(CancellationToken cancellationToken)' is missing XML documentation.
- Line 213, MissingDoc, Error, Method, ImportAsync(IEnumerable<OplogEntry> items, CancellationToken cancellationToken)
-> Method 'ImportAsync(IEnumerable<OplogEntry> items, CancellationToken cancellationToken)' is missing XML documentation.
- Line 215, MissingDoc, Error, Method, MergeAsync(IEnumerable<OplogEntry> items, CancellationToken cancellationToken)
-> Method 'MergeAsync(IEnumerable<OplogEntry> items, CancellationToken cancellationToken)' is missing XML documentation.
FILE: /Users/dohertj2/Desktop/CBDDC/src/ZB.MOM.WW.CBDDC.Core/Cache/IDocumentCache.cs
Issues:
- Line 7, MissingDoc, Error, Method, Clear()
-> Method 'Clear()' is missing XML documentation.
- Line 8, MissingDoc, Error, Method, Get(string collection, string key)
-> Method 'Get(string collection, string key)' is missing XML documentation.
- Line 9, MissingDoc, Error, Method, GetStatistics()
-> Method 'GetStatistics()' is missing XML documentation.
- Line 10, MissingDoc, Error, Method, Remove(string collection, string key)
-> Method 'Remove(string collection, string key)' is missing XML documentation.
- Line 11, MissingDoc, Error, Method, Set(string collection, string key, Document document)
-> Method 'Set(string collection, string key, Document document)' is missing XML documentation.
FILE: /Users/dohertj2/Desktop/CBDDC/src/ZB.MOM.WW.CBDDC.Network/Leadership/BullyLeaderElectionService.cs
Issues:
- Line 29, MissingDoc, Error, Property, IsCloudGateway
-> Property 'IsCloudGateway' is missing XML documentation
- Line 30, MissingDoc, Error, Property, CurrentGatewayNodeId
-> Property 'CurrentGatewayNodeId' is missing XML documentation
- Line 32, MissingDoc, Error, Event, LeadershipChanged
-> Event 'LeadershipChanged' is missing XML documentation.
- Line 53, MissingDoc, Error, Method, Start()
-> Method 'Start()' is missing XML documentation.
- Line 70, MissingDoc, Error, Method, Stop()
-> Method 'Stop()' is missing XML documentation.
FILE: /Users/dohertj2/Desktop/CBDDC/src/ZB.MOM.WW.CBDDC.Core/Sync/IOfflineQueue.cs
Issues:
- Line 9, MissingDoc, Error, Property, Count
-> Property 'Count' is missing XML documentation
- Line 11, MissingDoc, Error, Method, Clear()
-> Method 'Clear()' is missing XML documentation.
- Line 12, MissingDoc, Error, Method, Enqueue(PendingOperation operation)
-> Method 'Enqueue(PendingOperation operation)' is missing XML documentation.
- Line 13, MissingDoc, Error, Method, FlushAsync(Func<PendingOperation, Task> executor, CancellationToken cancellationToken)
-> Method 'FlushAsync(Func<PendingOperation, Task> executor, CancellationToken cancellationToken)' is missing XML documentation.
FILE: /Users/dohertj2/Desktop/CBDDC/src/ZB.MOM.WW.CBDDC.Network/CBDDCNodeService.cs
Issues:
- Line 17, MissingDoc, Error, Constructor, CBDDCNodeService(ICBDDCNode node, ILogger<CBDDCNodeService> logger)
-> Constructor 'CBDDCNodeService(ICBDDCNode node, ILogger<CBDDCNodeService> logger)' is missing XML documentation.
- Line 23, MissingDoc, Error, Method, StartAsync(CancellationToken cancellationToken)
-> Method 'StartAsync(CancellationToken cancellationToken)' is missing XML documentation.
- Line 47, MissingDoc, Error, Method, StopAsync(CancellationToken cancellationToken)
-> Method 'StopAsync(CancellationToken cancellationToken)' is missing XML documentation.
FILE: /Users/dohertj2/Desktop/CBDDC/src/ZB.MOM.WW.CBDDC.Network/ISyncServer.cs
Issues:
- Line 15, MissingDoc, Error, Method, Start()
-> Method 'Start()' is missing XML documentation.
- Line 17, MissingDoc, Error, Method, Stop()
-> Method 'Stop()' is missing XML documentation.
- Line 19, MissingDoc, Error, Property, ListeningEndpoint
-> Property 'ListeningEndpoint' is missing XML documentation
FILE: /Users/dohertj2/Desktop/CBDDC/src/ZB.MOM.WW.CBDDC.Network/Security/SecureHandshakeService.cs
Issues:
- Line 14, MissingDoc, Error, Constructor, SecureHandshakeService(ILogger<SecureHandshakeService>? logger)
-> Constructor 'SecureHandshakeService(ILogger<SecureHandshakeService>? logger)' is missing XML documentation.
- Line 24, MissingDoc, Error, Method, HandshakeAsync(Stream stream, bool isInitiator, string myNodeId, CancellationToken token)
-> Method 'HandshakeAsync(Stream stream, bool isInitiator, string myNodeId, CancellationToken token)' is missing XML documentation.
FILE: /Users/dohertj2/Desktop/CBDDC/src/ZB.MOM.WW.CBDDC.Core/CBDDCServiceCollectionExtensions.cs
Issues:
- Line 14, MissingDoc, Error, Method, AddCBDDCCore(IServiceCollection services)
-> Method 'AddCBDDCCore(IServiceCollection services)' is missing XML documentation.
FILE: /Users/dohertj2/Desktop/CBDDC/src/ZB.MOM.WW.CBDDC.Core/Storage/CorruptDatabaseException.cs
Issues:
- Line 7, MissingDoc, Error, Constructor, CorruptDatabaseException(string message, Exception innerException)
-> Constructor 'CorruptDatabaseException(string message, Exception innerException)' is missing XML documentation.
FILE: /Users/dohertj2/Desktop/CBDDC/src/ZB.MOM.WW.CBDDC.Network/Security/JwtOAuth2Validator.cs
Issues:
- Line 41, MissingDoc, Error, Method, ValidateTokenAsync(string jwtToken, CancellationToken cancellationToken)
-> Method 'ValidateTokenAsync(string jwtToken, CancellationToken cancellationToken)' is missing XML documentation.
Execution rules:
1. Read each file and apply all listed documentation fixes.
2. Keep changes minimal and doc-focused.
3. Avoid duplicate XML tags or malformed XML.
4. Return a short summary: files modified, issue count fixed per file, and any unresolved items.

173
docs-fix-batch-6.md Normal file
View File

@@ -0,0 +1,173 @@
You are a C# documentation expert. Fix the following XML documentation issues in the listed files.
IMPORTANT: Make ONLY documentation changes. Do NOT modify code logic, behavior, signatures, or formatting unrelated to XML docs.
Model requirement: GPT-5.3-Codex-Spark
Reasoning level: medium
Issue handling rules:
- MissingDoc: add concise XML docs with <summary> (and <returns> where appropriate)
- MissingInheritDoc: use /// <inheritdoc /> for overrides/interface implementations where appropriate
- MissingParam: add missing <param name="...">...</param> tags
- MissingTypeParam: add missing <typeparam name="...">...</typeparam> tags
- MalformedXml: repair XML syntax and invalid tags
Preserve local style and keep descriptions concise.
=== BATCH 6 OF 6 ===
FILE: /Users/dohertj2/Desktop/CBDDC/src/ZB.MOM.WW.CBDDC.Core/OplogEntry.cs
Issues:
- Line 15, MissingDoc, Error, Method, ComputeHash(OplogEntry entry)
-> Method 'ComputeHash(OplogEntry entry)' is missing XML documentation.
- Line 45, MissingDoc, Error, Property, Collection
-> Property 'Collection' is missing XML documentation
- Line 46, MissingDoc, Error, Property, Key
-> Property 'Key' is missing XML documentation
- Line 47, MissingDoc, Error, Property, Operation
-> Property 'Operation' is missing XML documentation
- Line 48, MissingDoc, Error, Property, Payload
-> Property 'Payload' is missing XML documentation
- Line 49, MissingDoc, Error, Property, Timestamp
-> Property 'Timestamp' is missing XML documentation
- Line 50, MissingDoc, Error, Property, Hash
-> Property 'Hash' is missing XML documentation
- Line 51, MissingDoc, Error, Property, PreviousHash
-> Property 'PreviousHash' is missing XML documentation
- Line 53, MissingDoc, Error, Constructor, OplogEntry(string collection, string key, OperationType operation, JsonElement? payload, HlcTimestamp timestamp, string previousHash, string? hash)
-> Constructor 'OplogEntry(string collection, string key, OperationType operation, JsonElement? payload, HlcTimestamp timestamp, string previousHash, string? hash)' is missing XML documentation.
FILE: /Users/dohertj2/Desktop/CBDDC/src/ZB.MOM.WW.CBDDC.Persistence/SnapshotMetadataStore.cs
Issues:
- Line 11, MissingDoc, Error, Method, DropAsync(CancellationToken cancellationToken)
-> Method 'DropAsync(CancellationToken cancellationToken)' is missing XML documentation.
- Line 13, MissingDoc, Error, Method, ExportAsync(CancellationToken cancellationToken)
-> Method 'ExportAsync(CancellationToken cancellationToken)' is missing XML documentation.
- Line 15, MissingDoc, Error, Method, GetSnapshotMetadataAsync(string nodeId, CancellationToken cancellationToken)
-> Method 'GetSnapshotMetadataAsync(string nodeId, CancellationToken cancellationToken)' is missing XML documentation.
- Line 17, MissingDoc, Error, Method, GetSnapshotHashAsync(string nodeId, CancellationToken cancellationToken)
-> Method 'GetSnapshotHashAsync(string nodeId, CancellationToken cancellationToken)' is missing XML documentation.
- Line 19, MissingDoc, Error, Method, ImportAsync(IEnumerable<SnapshotMetadata> items, CancellationToken cancellationToken)
-> Method 'ImportAsync(IEnumerable<SnapshotMetadata> items, CancellationToken cancellationToken)' is missing XML documentation.
- Line 21, MissingDoc, Error, Method, InsertSnapshotMetadataAsync(SnapshotMetadata metadata, CancellationToken cancellationToken)
-> Method 'InsertSnapshotMetadataAsync(SnapshotMetadata metadata, CancellationToken cancellationToken)' is missing XML documentation.
- Line 23, MissingDoc, Error, Method, MergeAsync(IEnumerable<SnapshotMetadata> items, CancellationToken cancellationToken)
-> Method 'MergeAsync(IEnumerable<SnapshotMetadata> items, CancellationToken cancellationToken)' is missing XML documentation.
- Line 25, MissingDoc, Error, Method, UpdateSnapshotMetadataAsync(SnapshotMetadata existingMeta, CancellationToken cancellationToken)
-> Method 'UpdateSnapshotMetadataAsync(SnapshotMetadata existingMeta, CancellationToken cancellationToken)' is missing XML documentation.
- Line 27, MissingDoc, Error, Method, GetAllSnapshotMetadataAsync(CancellationToken cancellationToken)
-> Method 'GetAllSnapshotMetadataAsync(CancellationToken cancellationToken)' is missing XML documentation.
FILE: /Users/dohertj2/Desktop/CBDDC/src/ZB.MOM.WW.CBDDC.Network/UdpDiscoveryService.cs
Issues:
- Line 32, MissingDoc, Error, Constructor, UdpDiscoveryService(IPeerNodeConfigurationProvider peerNodeConfigurationProvider, IDocumentStore documentStore, ILogger<UdpDiscoveryService> logger)
-> Constructor 'UdpDiscoveryService(IPeerNodeConfigurationProvider peerNodeConfigurationProvider, IDocumentStore documentStore, ILogger<UdpDiscoveryService> logger)' is missing XML documentation.
- Line 147, MissingDoc, Error, Method, Stop()
-> Method 'Stop()' is missing XML documentation.
- Line 179, MissingDoc, Error, Method, GetActivePeers()
-> Method 'GetActivePeers()' is missing XML documentation.
- Line 279, MissingDoc, Error, Property, NodeId
-> Property 'NodeId' is missing XML documentation
- Line 282, MissingDoc, Error, Property, TcpPort
-> Property 'TcpPort' is missing XML documentation
- Line 285, MissingDoc, Error, Property, ClusterHash
-> Property 'ClusterHash' is missing XML documentation
- Line 288, MissingDoc, Error, Property, InterestingCollections
-> Property 'InterestingCollections' is missing XML documentation
FILE: /Users/dohertj2/Desktop/CBDDC/src/ZB.MOM.WW.CBDDC.Network/ICBDDCNode.cs
Issues:
- Line 7, MissingDoc, Error, Property, Address
-> Property 'Address' is missing XML documentation
- Line 8, MissingDoc, Error, Property, Discovery
-> Property 'Discovery' is missing XML documentation
- Line 9, MissingDoc, Error, Property, Orchestrator
-> Property 'Orchestrator' is missing XML documentation
- Line 10, MissingDoc, Error, Property, Server
-> Property 'Server' is missing XML documentation
- Line 12, MissingDoc, Error, Method, Start()
-> Method 'Start()' is missing XML documentation.
- Line 13, MissingDoc, Error, Method, Stop()
-> Method 'Stop()' is missing XML documentation.
FILE: /Users/dohertj2/Desktop/CBDDC/src/ZB.MOM.WW.CBDDC.Network/Security/OAuth2ClientCredentialsTokenProvider.cs
Issues:
- Line 47, MissingDoc, Error, Method, GetAccessTokenAsync(CancellationToken cancellationToken)
-> Method 'GetAccessTokenAsync(CancellationToken cancellationToken)' is missing XML documentation.
- Line 137, MissingDoc, Error, Method, Dispose()
-> Method 'Dispose()' is missing XML documentation.
- Line 144, MissingDoc, Error, Property, AccessToken
-> Property 'AccessToken' is missing XML documentation
- Line 147, MissingDoc, Error, Property, ExpiresIn
-> Property 'ExpiresIn' is missing XML documentation
- Line 150, MissingDoc, Error, Property, TokenType
-> Property 'TokenType' is missing XML documentation
FILE: /Users/dohertj2/Desktop/CBDDC/src/ZB.MOM.WW.CBDDC.Network/CBDDCNode.cs
Issues:
- Line 138, MissingDoc, Error, Property, Host
-> Property 'Host' is missing XML documentation
- Line 139, MissingDoc, Error, Property, Port
-> Property 'Port' is missing XML documentation
- Line 141, MissingDoc, Error, Constructor, NodeAddress(string host, int port)
-> Constructor 'NodeAddress(string host, int port)' is missing XML documentation.
- Line 147, MissingDoc, Error, Method, ToString()
-> Method 'ToString()' is missing XML documentation.
FILE: /Users/dohertj2/Desktop/CBDDC/src/ZB.MOM.WW.CBDDC.Network/CompressionHelper.cs
Issues:
- Line 11, MissingDoc, Error, Property, IsBrotliSupported
-> Property 'IsBrotliSupported' is missing XML documentation
- Line 23, MissingDoc, Error, Method, Compress(byte[] data)
-> Method 'Compress(byte[] data)' is missing XML documentation.
- Line 39, MissingDoc, Error, Method, Decompress(byte[] compressedData)
-> Method 'Decompress(byte[] compressedData)' is missing XML documentation.
FILE: /Users/dohertj2/Desktop/CBDDC/src/ZB.MOM.WW.CBDDC.Core/Resilience/IRetryPolicy.cs
Issues:
- Line 9, MissingDoc, Error, Method, ExecuteAsync(Func<Task> operation, string operationName, CancellationToken cancellationToken)
-> Method 'ExecuteAsync(Func<Task> operation, string operationName, CancellationToken cancellationToken)' is missing XML documentation.
- Line 10, MissingDoc, Error, Method, ExecuteAsync(Func<Task<T>> operation, string operationName, CancellationToken cancellationToken)
-> Method 'ExecuteAsync(Func<Task<T>> operation, string operationName, CancellationToken cancellationToken)' is missing XML documentation.
FILE: /Users/dohertj2/Desktop/CBDDC/src/ZB.MOM.WW.CBDDC.Network/Security/CryptoHelper.cs
Issues:
- Line 13, MissingDoc, Error, Method, Encrypt(byte[] plaintext, byte[] key)
-> Method 'Encrypt(byte[] plaintext, byte[] key)' is missing XML documentation.
- Line 34, MissingDoc, Error, Method, Decrypt(byte[] ciphertext, byte[] iv, byte[] tag, byte[] key)
-> Method 'Decrypt(byte[] ciphertext, byte[] iv, byte[] tag, byte[] key)' is missing XML documentation.
FILE: /Users/dohertj2/Desktop/CBDDC/src/ZB.MOM.WW.CBDDC.Core/Diagnostics/CBDDCHealthCheck.cs
Issues:
- Line 21, MissingDoc, Error, Constructor, CBDDCHealthCheck(IOplogStore store, ISyncStatusTracker syncTracker, ILogger<CBDDCHealthCheck>? logger)
-> Constructor 'CBDDCHealthCheck(IOplogStore store, ISyncStatusTracker syncTracker, ILogger<CBDDCHealthCheck>? logger)' is missing XML documentation.
- Line 34, MissingParam, Warning, Method, CheckAsync(CancellationToken cancellationToken)
-> Method 'CheckAsync(CancellationToken cancellationToken)' is missing <param name="cancellationToken"> documentation.
FILE: /Users/dohertj2/Desktop/CBDDC/src/ZB.MOM.WW.CBDDC.Core/Sync/LastWriteWinsConflictResolver.cs
Issues:
- Line 8, MissingDoc, Error, Method, Resolve(Document? local, OplogEntry remote)
-> Method 'Resolve(Document? local, OplogEntry remote)' is missing XML documentation.
FILE: /Users/dohertj2/Desktop/CBDDC/src/ZB.MOM.WW.CBDDC.Core/Storage/IVectorClockService.cs
Issues:
- Line 22, MissingParam, Warning, Method, Update(OplogEntry entry)
-> Method 'Update(OplogEntry entry)' is missing <param name="entry"> documentation.
- Line 27, MissingParam, Warning, Method, GetVectorClockAsync(CancellationToken cancellationToken)
-> Method 'GetVectorClockAsync(CancellationToken cancellationToken)' is missing <param name="cancellationToken"> documentation.
- Line 32, MissingParam, Warning, Method, GetLatestTimestampAsync(CancellationToken cancellationToken)
-> Method 'GetLatestTimestampAsync(CancellationToken cancellationToken)' is missing <param name="cancellationToken"> documentation.
- Line 38, MissingParam, Warning, Method, GetLastHash(string nodeId)
-> Method 'GetLastHash(string nodeId)' is missing <param name="nodeId"> documentation.
- Line 44, MissingParam, Warning, Method, UpdateNode(string nodeId, HlcTimestamp timestamp, string hash)
-> Method 'UpdateNode(string nodeId, HlcTimestamp timestamp, string hash)' is missing <param name="hash"> documentation.
- Line 44, MissingParam, Warning, Method, UpdateNode(string nodeId, HlcTimestamp timestamp, string hash)
-> Method 'UpdateNode(string nodeId, HlcTimestamp timestamp, string hash)' is missing <param name="timestamp"> documentation.
- Line 44, MissingParam, Warning, Method, UpdateNode(string nodeId, HlcTimestamp timestamp, string hash)
-> Method 'UpdateNode(string nodeId, HlcTimestamp timestamp, string hash)' is missing <param name="nodeId"> documentation.
Execution rules:
1. Read each file and apply all listed documentation fixes.
2. Keep changes minimal and doc-focused.
3. Avoid duplicate XML tags or malformed XML.
4. Return a short summary: files modified, issue count fixed per file, and any unresolved items.

73
docs/README.md Executable file
View File

@@ -0,0 +1,73 @@
# CBDDC Documentation
This folder contains the official documentation for CBDDC, published as GitHub Pages.
## Documentation Structure (v0.9.0)
### Getting Started
- **[Getting Started](getting-started.md)** - Installation, setup, and first steps with CBDDC
### Core Documentation
- **[Architecture](architecture.md)** - Hybrid Logical Clocks, Gossip Protocol, mesh networking
- **[API Reference](api-reference.md)** - Complete API documentation with examples
- **[Querying](querying.md)** - Data querying patterns and LINQ support
### Persistence & Storage
- **[Persistence Providers](persistence-providers.md)** - SQLite, EF Core, PostgreSQL comparison
- **[Deployment Modes](deployment-modes.md)** - Single-cluster deployment strategy
### Networking & Security
- **[Security](security.md)** - Encryption, authentication, secure networking
- **[Conflict Resolution](conflict-resolution.md)** - LWW and Recursive Merge strategies
- **[Network Telemetry](network-telemetry.md)** - Monitoring and diagnostics
- **[Dynamic Reconfiguration](dynamic-reconfiguration.md)** - Runtime configuration changes
- **[Remote Peer Configuration](remote-peer-configuration.md)** - Managing remote peers and tracking lifecycle
- **[Upgrade: Peer-Confirmed Pruning](upgrade-peer-confirmed-pruning.md)** - Rollout notes and adoption checklist
### Deployment & Operations
- **[Deployment (LAN)](deployment-lan.md)** - Platform-specific deployment guide
- **[Production Hardening](production-hardening.md)** - Configuration, monitoring, best practices
- **[Peer Deprecation & Removal Runbook](peer-deprecation-removal-runbook.md)** - Operational workflow for de-tracking and removal
## Building the Documentation
This documentation uses Jekyll with the Cayman theme and is automatically published via GitHub Pages.
### Local Development
```bash
# Install Jekyll (requires Ruby)
gem install bundler jekyll
# Serve documentation locally
cd docs
jekyll serve
# Open http://localhost:4000
```
### Site Configuration
- **_config.yml** - Jekyll configuration and site metadata
- **_layouts/default.html** - Main page layout with navigation and styling
- **_includes/nav.html** - Top navigation bar
- **_data/navigation.yml** - Sidebar navigation structure per version
## Version History
The documentation supports multiple versions:
- **v0.9** (current) - Latest stable release
- **v0.8** - ASP.NET Core hosting, EF Core, PostgreSQL support
- **v0.7** - Brotli compression, Protocol v4
- **v0.6** - Secure networking, conflict resolution strategies
## Contributing to Documentation
1. **Fix typos or improve clarity** - Submit PRs directly
2. **Add new guides** - Create a new .md file and update navigation.yml
3. **Test locally** - Run Jekyll locally to preview changes before submitting
## Links
- [Main Repository](https://github.com/CBDDC/ZB.MOM.WW.CBDDC.Net)
- [NuGet Packages](https://www.nuget.org/packages?q=CBDDC)

5
docs/_config.yml Executable file
View File

@@ -0,0 +1,5 @@
title: "CBDDC Documentation"
description: A decentralized, offline-first peer-to-peer database.
theme: jekyll-theme-cayman
plugins:
- jekyll-seo-tag

123
docs/_data/navigation.yml Executable file
View File

@@ -0,0 +1,123 @@
v0.9:
- title: Getting Started
url: /getting-started.html
- title: Architecture
url: /architecture.html
- title: Persistence Providers
url: /persistence-providers.html
- title: Deployment Modes
url: /deployment-modes.html
- title: Deployment (LAN)
url: /deployment-lan.html
- title: Security
url: /security.html
- title: Conflict Resolution
url: /conflict-resolution.html
- title: Querying
url: /querying.html
- title: Network Telemetry
url: /network-telemetry.html
- title: Dynamic Reconfiguration
url: /dynamic-reconfiguration.html
- title: Remote Peer Configuration
url: /remote-peer-configuration.html
- title: Upgrade: Peer-Confirmed Pruning
url: /upgrade-peer-confirmed-pruning.html
- title: Peer Deprecation & Removal Runbook
url: /peer-deprecation-removal-runbook.html
- title: Production Hardening
url: /production-hardening.html
- title: API Reference
url: /api-reference.html
v0.8:
- title: Getting Started
url: /v0.8/getting-started.html
- title: Architecture
url: /v0.8/architecture.html
- title: Persistence Providers
url: /v0.8/persistence-providers.html
- title: Deployment Modes
url: /v0.8/deployment-modes.html
- title: Security
url: /v0.8/security.html
- title: Conflict Resolution
url: /v0.8/conflict-resolution.html
- title: Querying
url: /v0.8/querying.html
- title: Production Hardening
url: /v0.8/production-hardening.html
- title: Deployment (LAN)
url: /v0.8/deployment-lan.html
- title: API Reference
url: /v0.8/api-reference.html
v0.7:
- title: Getting Started
url: /v0.7/getting-started.html
- title: Architecture
url: /v0.7/architecture.html
- title: Security
url: /v0.7/security.html
- title: Conflict Resolution
url: /v0.7/conflict-resolution.html
- title: Querying
url: /v0.7/querying.html
- title: Production Hardening
url: /v0.7/production-hardening.html
- title: Deployment (LAN)
url: /v0.7/deployment-lan.html
- title: API Reference
url: /v0.7/api-reference.html
v0.6:
- title: Getting Started
url: /v0.6/getting-started.html
- title: Architecture
url: /v0.6/architecture.html
- title: Security
url: /v0.6/security.html
- title: Conflict Resolution
url: /v0.6/conflict-resolution.html
- title: Deployment (LAN)
url: /v0.6/deployment-lan.html
- title: API Reference
url: /v0.6/api-reference.html
- title: Production Hardening
url: /v0.6/production-hardening.html
v0.5:
- title: Getting Started
url: /v0.5/getting-started.html
- title: Architecture
url: /v0.5/architecture.html
- title: Deployment (LAN)
url: /v0.5/deployment-lan.html
- title: API Reference
url: /v0.5/api-reference.html
- title: Production Hardening
url: /v0.5/production-hardening.html
v0.4:
- title: Getting Started
url: /v0.4/getting-started.html
- title: Architecture
url: /v0.4/architecture.html
- title: Deployment (LAN)
url: /v0.4/deployment-lan.html
- title: API Reference
url: /v0.4/api-reference.html
- title: Production Hardening
url: /v0.4/production-hardening.html
v0.2:
- title: Getting Started
url: /v0.2/getting-started.html
- title: Architecture
url: /v0.2/architecture.html
- title: Deployment (LAN)
url: /v0.2/deployment-lan.html
- title: API Reference
url: /v0.2/api-reference.html
- title: Production Hardening
url: /v0.2/production-hardening.html

5
docs/_includes/nav.html Executable file
View File

@@ -0,0 +1,5 @@
<nav class="site-nav">
<a href="{{ '/' | relative_url }}">Home</a>
<a href="{{ '/getting-started.html' | relative_url }}">Docs (v0.9)</a>
<a href="https://github.com/CBDDC/ZB.MOM.WW.CBDDC.Net" target="_blank">GitHub</a>
</nav>

390
docs/_layouts/default.html Executable file
View File

@@ -0,0 +1,390 @@
<!DOCTYPE html>
<html lang="{{ site.lang | default: " en-US" }}">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<meta name="description" content="{{ site.description }}">
<link rel="stylesheet" href="{{ '/assets/css/style.css?v=' | append: site.github.build_revision | relative_url }}">
<title>{{ page.title | default: site.title }}</title>
<style>
/* Modern Color Palette */
:root {
--primary-color: #159957;
--primary-dark: #0e7042;
--primary-light: #1eb96a;
--secondary-color: #155799;
--background: #ffffff;
--background-alt: #f8f9fa;
--text-primary: #24292e;
--text-secondary: #586069;
--border-color: #e1e4e8;
--shadow: 0 1px 3px rgba(0,0,0,0.12), 0 1px 2px rgba(0,0,0,0.24);
--shadow-hover: 0 3px 6px rgba(0,0,0,0.15), 0 2px 4px rgba(0,0,0,0.12);
}
/* Base Styles */
body {
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif;
color: var(--text-primary);
line-height: 1.6;
margin: 0;
padding: 0;
}
/* Top Navigation */
.site-nav {
background: linear-gradient(135deg, var(--primary-color), var(--primary-light));
padding: 1rem 0;
text-align: center;
box-shadow: var(--shadow);
position: sticky;
top: 0;
z-index: 1000;
}
.site-nav a {
color: rgba(255, 255, 255, 0.9);
margin: 0 1.5rem;
text-decoration: none;
font-weight: 600;
font-size: 0.95rem;
letter-spacing: 0.5px;
transition: all 0.3s ease;
padding: 0.5rem 1rem;
border-radius: 4px;
}
.site-nav a:hover,
.site-nav a.active {
color: white;
background: rgba(255, 255, 255, 0.15);
transform: translateY(-1px);
}
/* Header Section */
.page-header {
background: linear-gradient(135deg, var(--primary-color), var(--secondary-color));
color: white;
text-align: center;
padding: 3rem 1rem;
}
.project-name {
margin: 0 0 0.5rem 0;
font-size: 2.5rem;
font-weight: 700;
letter-spacing: -0.5px;
}
.project-tagline {
margin: 0 0 1.5rem 0;
font-size: 1.2rem;
font-weight: 400;
opacity: 0.9;
}
.btn {
display: inline-block;
margin: 0.5rem;
padding: 0.75rem 2rem;
background: rgba(255, 255, 255, 0.2);
color: white;
border: 2px solid rgba(255, 255, 255, 0.8);
border-radius: 6px;
text-decoration: none;
font-weight: 600;
transition: all 0.3s ease;
font-size: 0.95rem;
}
.btn:hover {
background: white;
color: var(--primary-color);
transform: translateY(-2px);
box-shadow: var(--shadow-hover);
}
/* Layout Grid */
.page-content {
display: flex;
flex-direction: column;
max-width: 1200px;
margin: 2rem auto;
padding: 0 1rem;
gap: 2rem;
}
@media (min-width: 64em) {
.page-content {
flex-direction: row;
align-items: flex-start;
padding: 0 2rem;
}
}
/* Sidebar */
.sidebar {
width: 100%;
background: var(--background-alt);
padding: 1.5rem;
border-radius: 8px;
border: 1px solid var(--border-color);
box-shadow: var(--shadow);
}
@media (min-width: 64em) {
.sidebar {
width: 280px;
flex-shrink: 0;
position: sticky;
top: calc(4rem + 1rem);
max-height: calc(100vh - 6rem);
overflow-y: auto;
}
}
.sidebar h3 {
margin: 0 0 1rem 0;
font-size: 1.1rem;
color: var(--text-primary);
font-weight: 700;
padding-bottom: 0.75rem;
border-bottom: 2px solid var(--primary-color);
}
.sidebar ul {
list-style: none;
padding: 0;
margin: 0;
}
.sidebar li {
margin: 0;
}
.sidebar a {
color: var(--text-secondary);
text-decoration: none;
display: block;
padding: 0.5rem 0.75rem;
border-radius: 4px;
transition: all 0.2s ease;
font-size: 0.95rem;
}
.sidebar a:hover {
color: var(--primary-color);
background: rgba(21, 153, 87, 0.08);
padding-left: 1rem;
}
/* Main Content */
.main-content-wrapper {
flex: 1;
min-width: 0;
}
.main-content {
background: white;
padding: 2rem;
border-radius: 8px;
box-shadow: var(--shadow);
}
.main-content h1 {
color: var(--text-primary);
font-size: 2rem;
margin-top: 0;
margin-bottom: 1.5rem;
padding-bottom: 0.75rem;
border-bottom: 3px solid var(--primary-color);
}
.main-content h2 {
color: var(--text-primary);
font-size: 1.5rem;
margin-top: 2rem;
margin-bottom: 1rem;
padding-bottom: 0.5rem;
border-bottom: 2px solid var(--border-color);
}
.main-content h3 {
color: var(--text-primary);
font-size: 1.25rem;
margin-top: 1.5rem;
margin-bottom: 0.75rem;
}
.main-content code {
background: var(--background-alt);
padding: 0.2rem 0.4rem;
border-radius: 3px;
font-size: 0.9em;
color: var(--secondary-color);
}
.main-content pre {
background: var(--background-alt);
padding: 1rem;
border-radius: 6px;
overflow-x: auto;
border: 1px solid var(--border-color);
}
.main-content pre code {
background: none;
padding: 0;
color: var(--text-primary);
}
.main-content a {
color: var(--primary-color);
text-decoration: underline;
text-decoration-color: rgba(21, 153, 87, 0.3);
text-decoration-thickness: 1px;
text-underline-offset: 2px;
transition: all 0.2s ease;
}
.main-content a:hover {
color: var(--primary-dark);
text-decoration-color: var(--primary-dark);
text-decoration-thickness: 2px;
}
.main-content table {
width: 100%;
border-collapse: collapse;
margin: 1rem 0;
}
.main-content th,
.main-content td {
padding: 0.75rem;
border: 1px solid var(--border-color);
text-align: left;
}
.main-content th {
background: var(--background-alt);
font-weight: 600;
}
.main-content blockquote {
margin: 1rem 0;
padding: 1rem;
border-left: 4px solid var(--primary-color);
background: var(--background-alt);
border-radius: 0 4px 4px 0;
}
/* Footer */
.site-footer {
text-align: center;
padding: 2rem;
border-top: 1px solid var(--border-color);
margin-top: 3rem;
background: var(--background-alt);
color: var(--text-secondary);
font-size: 0.9rem;
}
.site-footer a {
color: var(--primary-color);
text-decoration: none;
}
.site-footer a:hover {
text-decoration: underline;
}
/* Mobile Responsiveness */
@media (max-width: 64em) {
.project-name {
font-size: 2rem;
}
.page-header {
padding: 2rem 1rem;
}
.main-content {
padding: 1.5rem;
}
.sidebar {
margin-bottom: 1.5rem;
}
}
/* Scrollbar Styling */
.sidebar::-webkit-scrollbar {
width: 8px;
}
.sidebar::-webkit-scrollbar-track {
background: var(--background-alt);
}
.sidebar::-webkit-scrollbar-thumb {
background: var(--border-color);
border-radius: 4px;
}
.sidebar::-webkit-scrollbar-thumb:hover {
background: var(--text-secondary);
}
</style>
</head>
<body>
{% include nav.html %}
<section class="page-header">
<h1 class="project-name">{{ site.title }}</h1>
<h2 class="project-tagline">{{ site.description }}</h2>
<a href="https://github.com/CBDDC/ZB.MOM.WW.CBDDC.Net" class="btn">View on GitHub</a>
<a href="https://github.com/CBDDC/ZB.MOM.WW.CBDDC.Net/releases" class="btn">Download</a>
</section>
<div class="page-content">
<!-- Sidebar Logic -->
{% assign version = page.url | split: '/' %}
{% assign current_version = version[1] %}
<!-- If current_version is empty or doesn't start with 'v', treat as v0.9 (root level) -->
{% if current_version == '' or current_version contains '.html' or current_version contains '.md' %}
{% assign current_version = 'v0.9' %}
{% endif %}
{% if current_version == 'v0.9' or current_version == 'v0.8' or current_version == 'v0.7' or current_version == 'v0.6' or current_version == 'v0.5' or current_version ==
'v0.4' or current_version ==
'v0.2' %}
<nav class="sidebar">
<h3>Docs {{ current_version }}</h3>
<ul>
{% for item in site.data.navigation[current_version] %}
<li><a href="{{ item.url | relative_url }}">{{ item.title }}</a></li>
{% endfor %}
</ul>
</nav>
{% endif %}
<div class="main-content-wrapper">
<section class="main-content">
{{ content }}
</section>
</div>
</div>
<footer class="site-footer">
<span class="site-footer-owner"><a href="https://cbddc.com">CBDDC</a> is maintained by <a
href="https://github.com/mrdevrobot">MrDevRobot</a>.</span>
<span class="site-footer-credits">This page was generated by <a href="https://pages.github.com">GitHub
Pages</a>.</span>
</footer>
</body>
</html>

142
docs/api-reference.md Executable file
View File

@@ -0,0 +1,142 @@
# API Reference
## PeerDatabase
The entry point for interacting with the database.
```csharp
public class PeerDatabase : IPeerDatabase
{
public PeerDatabase(IPeerStore store, string nodeId = "local");
// Non-generic collection access
public IPeerCollection Collection(string name);
// Generic collection access (type-safe)
public IPeerCollection<T> Collection<T>(string? customName = null);
}
```
### Collection Naming Convention
When using `Collection<T>()`, the collection name defaults to `typeof(T).Name.ToLowerInvariant()`. You can change this globally using `CBDDCMapper`.
```csharp
// Uses default collection name "user"
var users = db.Collection<User>();
// Custom name override
var users = db.Collection<User>("custom_users");
```
## IPeerCollection<T>
Represents a collection of documents (like a Table or Container).
```csharp
public interface IPeerCollection<T> : IPeerCollection
{
// Single Operations
Task Put(string key, T document, CancellationToken cancellationToken = default);
Task<T> Get(string key, CancellationToken cancellationToken = default);
Task Delete(string key, CancellationToken cancellationToken = default);
// Batch Operations
Task PutMany(IEnumerable<T> documents, CancellationToken cancellationToken = default);
Task DeleteMany(IEnumerable<string> keys, CancellationToken cancellationToken = default);
// Queries
Task<IEnumerable<T>> Find(Expression<Func<T, bool>> predicate, CancellationToken cancellationToken = default);
Task<int> Count(Expression<Func<T, bool>>? predicate = null, CancellationToken cancellationToken = default);
}
```
### Batch Operations
For bulk inserts or deletes, use `PutMany` and `DeleteMany` for better performance and atomicity.
```csharp
var users = new List<User> { ... };
await db.Collection<User>().PutMany(users);
```
### Count
Get the total number of documents matching a filter efficiently (database-side).
```csharp
int total = await db.Collection<User>().Count();
int active = await db.Collection<User>().Count(u => u.IsActive);
```
## Global Configuration (CBDDCMapper)
You can configure entity mappings globally instead of using attributes.
```csharp
CBDDCMapper.Global.Entity<Product>()
.Collection("products_v2")
.Index(p => p.Price)
.Index(p => p.Category);
```
## Primary Keys and Indexes
### Attributes
```csharp
using ZB.MOM.WW.CBDDC.Core.Metadata;
public class User
{
[PrimaryKey(AutoGenerate = true)]
public string Id { get; set; } = "";
[Indexed]
public int Age { get; set; }
}
```
### Indexed Fields
Indexes improve query performance (e.g., `Find(u => u.Age > 18)`).
You can define them via `[Indexed]` attribute or `CBDDCMapper`.
> **Note**: CBDDC automatically creates SQLite indexes for these properties.
## Exceptions
CBDDC throws specific exceptions for better error handling:
- `CBDDCException` (Base class)
- `DocumentNotFoundException`
- `CBDDCConcurrencyException`
- `TimeoutException`
- `NetworkException`
- `PersistenceException`
```csharp
try
{
await db.Collection<User>().Get("invalid-id");
}
catch (DocumentNotFoundException ex)
{
Console.WriteLine($"Document {ex.Key} not found in {ex.Collection}");
}
```
## Querying
Supported LINQ operators:
- Equality: `==`, `!=`
- Comparison: `>`, `<`, `>=`, `<=`
- Boolean: `&&`, `||`
- String: `Contains`, `StartsWith`, `EndsWith` (Mapped to SQL `LIKE`)
- Collections: `Contains` (IN clause)
```csharp
// Find users in list of IDs
var ids = new[] { "1", "2" };
await col.Find(u => ids.Contains(u.Id));
```

70
docs/architecture.md Executable file
View File

@@ -0,0 +1,70 @@
# Architecture & Concepts
## Design Philosophy
CBDDC is designed for **Local Area Networks (LAN)** and **Local-First** scenarios.
It does not rely on a central master server. Every node is equal (Peer-to-Peer).
**Target Deployment**: Trusted LAN environments (offices, homes, private networks)
**Cross-Platform**: Windows, Linux, macOS (.NET 8.0+, with .NET 6.0 and .NET Standard 2.0 support)
### HLC (Hybrid Logical Clock)
To resolve conflicts without a central authority, we use **Hybrid Logical Clocks**.
This allows us to determine the "happened-before" relationship between events even if system clocks are slightly skewed.
In case of concurrent edits, the "Last Write Wins" (LWW) policy based on HLC is applied.
## Synchronization
### Anti-Entropy
When two nodes connect, they exchange their latest HLC timestamps.
- If Node A is ahead of Node B, Node B "pulls" the missing operations from Node A.
- If Node B is ahead, Node A "pushes" its operations.
### Gossip Protocol
Nodes discover each other via UDP Broadcast (LAN) and then form random TCP connections to gossip updates.
This ensures that updates propagate exponentially through the network (Epidemic Algorithm).
### Snapshots & Fast Recovery
To optimize reconnection, each node maintains a **Snapshot** of the last known state (Hash & Timestamp) for every peer.
- When re-connecting, nodes compare their snapshot state.
- If the chain hash matches, they only exchange the delta.
- This avoids re-processing the entire operation history and ensures efficient gap recovery.
### Peer-Confirmed Oplog Pruning
CBDDC maintenance pruning now uses a two-cutoff model:
- **Retention cutoff**: derived from `OplogRetentionHours` (time-based policy).
- **Confirmation cutoff**: the oldest confirmation watermark across all active tracked peers and relevant source nodes.
The effective prune cutoff is:
- `min(retention cutoff, confirmation cutoff)` when confirmation data is complete.
- retention cutoff only when confirmation tracking is not configured or when there are no active tracked peers.
- **no prune** for that maintenance cycle when any active tracked peer is missing confirmation for any relevant source stream.
Key semantics:
- Relevant source streams are taken from the local non-default vector clock entries.
- A peer only gates pruning while it is active in peer tracking.
- Removing peer tracking excludes that peer from prune gating.
- Maintenance logs include explicit skip reasons when pruning is blocked (for example,
missing confirmation for a tracked peer/source pair).
## Security Disclaimer
::: warning FOR LAN USE ONLY
**CBDDC is designed for trusted Local Area Networks.**
:::
- **P2P Mesh Mode**: Designed for **LAN/VPN** use. Uses raw TCP and UDP broadcast. Not safe for public internet by default.
- **ASP.NET Core Server**: Designed for controlled server deployments. Supports standard HTTPS and hosted sync endpoints.
- **Transport**: Data is transmitted via raw TCP. There is **NO Encryption (TLS/SSL)** by default.
- **Authentication**: A basic "Shared Key" mechanism is implemented. Nodes must share the same `AuthToken` to sync.
- **Authorization**: Once authenticated, a node has full read/write access to all collections.
**Recommendation**:
- Use only within trusted private networks (LAN, VPN, or localhost)
- For internet deployment, implement TLS, proper authentication, and firewall rules
- Consider the production hardening features for resilience on LAN
**Cross-Platform Support**: Runs on Windows, Linux, and macOS with .NET 8.0+ (also supports .NET 6.0 and .NET Standard 2.0).

251
docs/conflict-resolution.md Executable file
View File

@@ -0,0 +1,251 @@
# Conflict Resolution
**CBDDC v0.6.0** introduces **pluggable conflict resolution strategies** to handle concurrent updates to the same document across different nodes.
## Overview
When two nodes modify the same document offline and later sync, a conflict occurs. CBDDC provides two built-in strategies:
1. **Last Write Wins (LWW)** - Simple, timestamp-based resolution
2. **Recursive Merge** - Intelligent JSON merging with array handling
## Conflict Resolution Strategies
### Last Write Wins (LWW)
**How it works:**
- Each document has a Hybrid Logical Clock (HLC) timestamp
- During sync, the document with the **highest timestamp wins**
- Conflicts are resolved automatically with no merge attempt
**Pros:**
- ✅ Simple and predictable
- ✅ Fast (no merge computation)
- ✅ No data corruption or invalid states
**Cons:**
- ❌ Data loss - one change is discarded entirely
- ❌ Not suitable for collaborative editing
**Use Cases:**
- Configuration data with infrequent updates
- Reference data (product catalogs, price lists)
- Single-user scenarios with backup sync
#### Example
```csharp
// Both nodes start with same document
{ "id": "doc-1", "name": "Alice", "age": 25 }
// Node A updates (timestamp: 100)
{ "id": "doc-1", "name": "Alice", "age": 26 }
// Node B updates (timestamp: 105)
{ "id": "doc-1", "name": "Alicia", "age": 25 }
// After sync: Node B wins (higher timestamp)
{ "id": "doc-1", "name": "Alicia", "age": 25 }
// Node A's age change is LOST
```
---
### Recursive Merge
**How it works:**
- Performs deep JSON merge of conflicting documents
- Uses the **highest timestamp** for each individual field
- Arrays with `id` or `_id` fields are merged by identity
- Arrays without IDs are concatenated and deduplicated
**Pros:**
- ✅ Preserves changes from both nodes
- ✅ Suitable for collaborative scenarios
- ✅ Intelligent array handling
**Cons:**
- ❌ More complex logic
- ❌ Slightly slower (~5-10ms overhead)
- ❌ May produce unexpected results with complex nested structures
**Use Cases:**
- TodoLists, shopping carts (demonstrated in samples)
- Collaborative documents
- Complex objects with independent fields
- Data where every change matters
#### Example: Field-Level Merge
```csharp
// Both nodes start with same document
{ "id": "doc-1", "name": "Alice", "age": 25 }
// Node A updates (timestamp: 100)
{ "id": "doc-1", "name": "Alice", "age": 26 }
// Node B updates (timestamp: 105)
{ "id": "doc-1", "name": "Alicia", "age": 25 }
// After Recursive Merge:
{ "id": "doc-1", "name": "Alicia", "age": 26 }
// Uses latest timestamp for each field independently
```
#### Example: Array Merging with IDs
```csharp
// Initial TodoList
{
"id": "list-1",
"name": "Shopping",
"items": [
{ "id": "1", "task": "Buy milk", "completed": false },
{ "id": "2", "task": "Buy bread", "completed": false }
]
}
// Node A: Completes milk, adds eggs
{
"items": [
{ "id": "1", "task": "Buy milk", "completed": true },
{ "id": "2", "task": "Buy bread", "completed": false },
{ "id": "3", "task": "Buy eggs", "completed": false }
]
}
// Node B: Completes bread, adds cheese
{
"items": [
{ "id": "1", "task": "Buy milk", "completed": false },
{ "id": "2", "task": "Buy bread", "completed": true },
{ "id": "4", "task": "Buy cheese", "completed": false }
]
}
// After Recursive Merge: ALL changes preserved!
{
"items": [
{ "id": "1", "task": "Buy milk", "completed": true }, // A's completion
{ "id": "2", "task": "Buy bread", "completed": true }, // B's completion
{ "id": "3", "task": "Buy eggs", "completed": false }, // A's addition
{ "id": "4", "task": "Buy cheese", "completed": false } // B's addition
]
}
```
## Configuration
### Select Strategy at Startup
```csharp
using ZB.MOM.WW.CBDDC.Core.Sync;
// Last Write Wins (default)
services.AddSingleton<IConflictResolver, LastWriteWinsConflictResolver>();
// OR Recursive Merge
services.AddSingleton<IConflictResolver, RecursiveNodeMergeConflictResolver>();
```
### Console Sample
```bash
# Use Recursive Merge
dotnet run --merge
# Use Last Write Wins (default)
dotnet run
```
### UI Clients
Resolver selection can be exposed in your UI:
1. Choose "Recursive Merge" or "Last Write Wins"
2. Save configuration
3. Restart the application if required by your host
## Interactive Demo
A UI demo can expose a **"Run Conflict Demo"** action that:
1. Creates a TodoList with 2 items
2. Simulates concurrent edits from two "nodes"
3. Shows the merged result
4. Compares LWW vs Recursive Merge behavior
**Try it yourself** to see the difference!
## Best Practices
### Use LWW When:
- Only one user/node typically writes
- Data is reference/configuration
- Simplicity is more important than preserving every change
- Performance is critical
### Use Recursive Merge When:
- Multiple users collaborate
- Every change is valuable (e.g., TodoItems, cart items)
- Data has independent fields that can conflict
- Arrays have `id` fields for identity
### Avoid Conflicts Entirely:
- Use **different collections** for different data types
- Implement **optimistic locking** with version fields
- Design data models to minimize overlapping writes
## Custom Resolvers
You can implement `IConflictResolver` for custom logic:
```csharp
public class CustomResolver : IConflictResolver
{
public ValueTask<string> ResolveConflict(
string localJson,
string remoteJson,
long localTimestamp,
long remoteTimestamp)
{
// Your custom merge logic here
return new ValueTask<string>(result);
}
}
```
Register your resolver:
```csharp
services.AddSingleton<IConflictResolver, CustomResolver>();
```
## Performance
Benchmark results (1000 conflict resolutions):
| Strategy | Avg Time | Throughput |
|----------|----------|------------|
| Last Write Wins | 0.05ms | 20,000 ops/sec |
| Recursive Merge | 0.15ms | 6,600 ops/sec |
**Recommendation**: Performance difference is negligible for most applications. Choose based on data preservation needs.
## FAQ
**Q: Can I switch resolvers at runtime?**
A: No. The resolver is injected at startup. Changing requires application restart.
**Q: What happens if I change resolvers after data exists?**
A: Existing data is unaffected. Only future conflicts use the new strategy.
**Q: Can different nodes use different resolvers?**
A: Technically yes, but **not recommended**. All nodes should use the same strategy for consistency.
**Q: Does this handle schema changes?**
A: No. Conflict resolution assumes both documents have compatible schemas.
---
**See Also:**
- [Getting Started](getting-started.html)
- [Architecture](architecture.html)
- [Security](security.html)

File diff suppressed because it is too large Load Diff

146
docs/deployment-lan.md Executable file
View File

@@ -0,0 +1,146 @@
# CBDDC - Deployment Guide for LAN
## Target Environment
CBDDC is specifically designed for **Local Area Networks (LAN)** in trusted environments:
**Ideal Use Cases**:
- Office networks (employee workstations, kiosks)
- Home automation systems
- Retail point-of-sale systems (POS)
- Edge computing deployments
- Private industrial networks
- Development/testing environments
**NOT Recommended**:
- Public internet deployment (without significant security enhancements)
- Multi-tenant SaaS applications
- Untrusted network environments
## Cross-Platform Support
CBDDC runs on all major operating systems:
| Platform | Support | Notes |
|----------|---------|-------|
| **Windows** | ✅ Full | Windows 10+, Server 2019+ |
| **Linux** | ✅ Full | Ubuntu, Debian, RHEL, Alpine |
| **macOS** | ✅ Full | macOS 11+ (Big Sur and later) |
**Requirements**: .NET 8.0+ Runtime (also compatible with .NET 6.0 and .NET Standard 2.0)
## LAN Deployment Checklist
### Network Configuration
- [ ] **Firewall Rules**: Open TCP port (default: 5000) and UDP port (default: 6000)
- [ ] **Broadcast Domain**: Ensure nodes are in the same subnet for UDP discovery
- [ ] **Network Stability**: LAN should have reasonable stability (WiFi or wired)
- [ ] **Bandwidth**: Adequate for sync operations (typically low, < 1 Mbps)
### Security Configuration
- [ ] **Cluster Key**: Configure unique cluster authentication key
- [ ] **Network Isolation**: Use VLANs or network segmentation
- [ ] **Access Control**: Limit network access to authorized devices
- [ ] **Monitoring**: Set up logging and health checks
### Application Configuration
```json
{
"CBDDC": {
"Network": {
"TcpPort": 5000,
"UdpPort": 6000,
"LocalhostOnly": false
},
"Persistence": {
"DatabasePath": "/var/lib/cbddc/data.db",
"EnableWalMode": true,
"EnableAutoBackup": true,
"BackupPath": "/var/lib/cbddc/backups"
}
}
}
```
### Platform-Specific Considerations
#### Windows
- Use Windows Services for background operation
- Configure Windows Firewall rules
- Consider SQLite file locking on network shares
#### Linux
- Use systemd for service management
- Set appropriate file permissions
- Consider SELinux/AppArmor policies
#### macOS
- Use launchd for background services
- Configure macOS firewall
- Handle macOS sleep/wake for laptops
## Example: Office Network Deployment
### Scenario
10 workstations in an office need to sync product catalog data.
### Setup
1. **Network**: All on 192.168.1.0/24 subnet
2. **Nodes**: Each workstation runs CBDDC
3. **Discovery**: UDP broadcast for automatic peer discovery
4. **Sync**: TCP for data synchronization
5. **Storage**: Local SQLite database per workstation
### Benefits
- **No Internet Required**: Works during internet outages
- **Low Latency**: Local network = fast reads/writes
- **Resilient**: No single point of failure
- **Offline Capable**: Each workstation works independently
## Troubleshooting
### Nodes Not Discovering Each Other
- Check firewall rules for UDP port
- Verify nodes are on same broadcast domain
- Check cluster key matches on all nodes
### Slow Synchronization
- Check network bandwidth
- Verify no packet loss
- Review batch size configuration
### Database Corruption
- Verify WAL mode is enabled
- Check disk space
- Review backup/restore procedures
## Security Best Practices for LAN
1. **Network Segmentation**: Isolate CBDDC network from public networks
2. **Cluster Authentication**: Use strong cluster keys
3. **Access Control**: Limit which devices can join the network
4. **Monitoring**: Log all sync operations
5. **Regular Backups**: Automated backup to separate storage
6. **Update Policy**: Keep .NET runtime updated
## NOT Recommended for Internet
CBDDC **should NOT** be deployed on public internet without:
- TLS/SSL encryption for TCP connections
- Proper authentication beyond cluster key
- Network firewalls and security groups
- DDoS protection
- Rate limiting
- Intrusion detection
For internet deployment, consider traditional client-server databases instead.
## Support
For LAN deployment questions, see:
- [Production Hardening Guide](production-hardening.md)
- [API Reference](api-reference.md)
- [Architecture Documentation](architecture.md)

82
docs/deployment-modes.md Executable file
View File

@@ -0,0 +1,82 @@
# CBDDC Deployment Mode
ZB.MOM.WW.CBDDC.Hosting supports a single deployment mode.
## Single-Cluster Mode
**Best for:**
- Dedicated database servers
- Simple deployments
- Development/testing environments
- Small-to-medium applications
### Architecture
```
┌─────────────────────┐
│ ASP.NET Server │
│ │
│ ┌───────────────┐ │
│ │ CBDDC │ │
│ │ (1 cluster) │ │
│ └───────────────┘ │
│ │ │
│ [TCP:5001] │
└─────────────────────┘
```
### Configuration
```csharp
builder.Services.AddCBDDCHostingSingleCluster(options =>
{
options.NodeId = "server-01";
options.TcpPort = 5001;
});
```
### Characteristics
- One database per server instance
- Simple configuration
- Easy to scale horizontally (multiple servers = multiple databases)
- No port collision issues
- Recommended for most use cases
## Server Behavior
Servers use respond-only operation:
- Accept incoming TCP sync connections
- Respond to sync requests
- Serve data to clients
- Maintain persistence
- Do not initiate outbound sync
- Do not perform UDP discovery
- Do not connect to other servers automatically
## Persistence Layer Compatibility
Single-cluster mode works with all persistence providers:
| Provider | Support | Notes |
|----------|---------|-------|
| SQLite (Direct) | Yes | One DB file per cluster |
| EF Core | Yes | Flexible DB options |
| PostgreSQL | Yes | Best for production |
## Scaling Strategy
### Horizontal Scaling
```
Load Balancer
├─> Server 1 (Cluster A) [PostgreSQL A]
├─> Server 2 (Cluster B) [PostgreSQL B]
└─> Server 3 (Cluster C) [PostgreSQL C]
```
## Recommendation
Use single-cluster mode for ASP.NET hosting. It is simpler, robust, and straightforward to scale.

66
docs/dynamic-reconfiguration.md Executable file
View File

@@ -0,0 +1,66 @@
---
layout: default
title: Dynamic Reconfiguration
nav_order: 8
---
# Dynamic Reconfiguration & Leader Election
CBDDC v0.8.0 introduces support for dynamic reconfiguration, allowing nodes to change their role, listening ports, and database identity without a full process restart. This is essential for containerized environments and long-running services.
## Configuration Provider
The core of this feature is the `IPeerNodeConfigurationProvider` interface (available in .Net, Kotlin, and NodeJs).
```typescript
// NodeJs Example
interface IPeerNodeConfigurationProvider {
getConfiguration(): Promise<PeerNodeConfiguration>;
onConfigurationChanged(callback: (config: PeerNodeConfiguration) => void): void;
}
```
Implementations can watch a file (e.g., `config.json`), a remote configuration server, or environment variables. When the configuration changes, the provider fires an event.
### Usage
Services like `TcpSyncServer` subscribe to these events.
- **Port Change**: The server stops the listener and restarts on the new port.
- **NodeId Change**: If the NodeID changes, it typically implies a change of identity. The persistence layer (SQLite) is bound to a specific NodeID (internal HLC history). Thus, a NodeID change requires closing the existing DB and opening/creating a new one matching the new ID.
## Known Peers (Static Remote)
You can now persist a list of "Known Peers" (Static Remote nodes) that survive restarts. This is useful for:
- Configuring a fixed set of Cloud Gateways.
- Connecting to specific peers outside the local broadcast domain.
### API
```typescript
// Add a static peer
await peerStore.saveRemotePeer({
nodeId: "gateway-1",
address: "192.168.1.50:25000",
type: PeerType.StaticRemote,
...
});
// Retrieve
const peers = await peerStore.getRemotePeers();
```
## Leader Election (Bully Algorithm)
For Cloud Synchronization, usually one node in a LAN cluster acts as the "Gateway" to the cloud to avoid redundant traffic. CBDDC implements the **Bully Algorithm** for automatic leader election.
- **Rule**: Among all discovered peers (LAN or Static) that are eligible, the node with the **lexicographically smallest NodeID** declares itself the Leader.
- **Mechanism**: Each node periodically checks the list of active peers. If it finds no other peer with a smaller ID than itself, it assumes leadership.
- **Event**: The `LeaderElectionService` emits a `LeadershipChanged` event (boolean `isLeader`).
- **Action**: The `SyncOrchestrator` uses this flag to decide whether to push/pull from Cloud Remotes.
### NodeJs Logic
```typescript
const amILeader = !activePeers.some(p => p.nodeId < myNodeId);
```
This simple consensus mechanism works well for small-to-medium clusters on a low-latency LAN.

188
docs/getting-started.md Executable file
View File

@@ -0,0 +1,188 @@
# Getting Started (v0.9.0)
## Installation
CBDDC is available as a set of NuGet packages for .NET 8.0, .NET 6.0, and .NET Standard 2.0.
```bash
dotnet add package ZB.MOM.WW.CBDDC.Core
dotnet add package CBDDC.Network
dotnet add package ZB.MOM.WW.CBDDC.Persistence.Sqlite
```
### Cloud & Enterprise Packages
For ASP.NET Core hosting and enterprise database support:
```bash
# ASP.NET Core hosting
dotnet add package ZB.MOM.WW.CBDDC.Hosting
# Entity Framework Core (SQL Server, MySQL, SQLite)
dotnet add package ZB.MOM.WW.CBDDC.Persistence.EntityFramework
# PostgreSQL with JSONB optimization
dotnet add package ZB.MOM.WW.CBDDC.Persistence.PostgreSQL
```
## Requirements
- **.NET 8.0+ Runtime** (recommended) or .NET 6.0+
- **SQLite** (included via Microsoft.Data.Sqlite)
- **PostgreSQL 12+** (optional, for PostgreSQL persistence)
- **SQL Server 2016+** (optional, for SQL Server persistence)
## Basic Usage
### 1. Initialize the Store
Use `SqlitePeerStore` for persistence. Supported on Windows, Linux, and macOS.
```csharp
using ZB.MOM.WW.CBDDC.Core;
using ZB.MOM.WW.CBDDC.Core.Sync;
using ZB.MOM.WW.CBDDC.Persistence.Sqlite;
using CBDDC.Network.Security;
// Choose conflict resolver (v0.6.0+)
var resolver = new RecursiveNodeMergeConflictResolver(); // OR LastWriteWinsConflictResolver()
var store = new SqlitePeerStore("Data Source=my-node.db", logger, resolver);
// Automatically creates tables on first run
```
### 2. Configure Networking (with Optional Security)
Use `AddCBDDCNetwork` extension method to register services.
```csharp
var services = new ServiceCollection();
string myNodeId = "node-1";
int port = 5001;
string authToken = "my-secret-cluster-key";
services.AddSingleton<IPeerStore>(store);
// Optional: Enable encryption (v0.6.0+)
services.AddSingleton<IPeerHandshakeService, SecureHandshakeService>();
services.AddCBDDCNetwork(myNodeId, port, authToken);
```
### 3. Start the Node
```csharp
var provider = services.BuildServiceProvider();
var node = provider.GetRequiredService<CBDDCNode>();
node.Start();
```
### 4. CRUD Operations
Interact with data using `PeerDatabase`.
```csharp
var db = new PeerDatabase(store, "my-node-id"); // Node ID used for HLC clock
await db.InitializeAsync();
var users = db.Collection("users");
// Put
await users.Put("user-1", new { Name = "Alice", Age = 30 });
// Get
var user = await users.Get<User>("user-1");
// Query
var results = await users.Find<User>(u => u.Age > 20);
```
## ASP.NET Core Deployment (v0.8.0+)
### Single Cluster Mode (Recommended)
Perfect for production deployments with dedicated database servers:
```csharp
// Program.cs
var builder = WebApplication.CreateBuilder(args);
// Use PostgreSQL for production
builder.Services.AddCBDDCPostgreSql(
builder.Configuration.GetConnectionString("CBDDC"));
// Configure single cluster
builder.Services.AddCBDDCHostingSingleCluster(options =>
{
options.NodeId = "server-01";
options.TcpPort = 5001;
});
var app = builder.Build();
app.MapHealthChecks("/health");
app.Run();
```
See [Deployment Modes](deployment-modes.md) for deployment details.
## What's New in v0.9.0
### 🚀 Production Enhancements
- **Improved ASP.NET Core Sample**: Enhanced error handling and better examples
- **EF Core Stability**: Fixed runtime issues for all persistence providers
- **Sync Refinements**: More reliable synchronization across hosted deployments
### 📸 Snapshots (v0.8.6)
- **Fast Reconnection**: Peers resume sync from the last known state
- **Optimized Recovery**: Prevents re-processing of already applied operations
- **Automatic Management**: Snapshot metadata tracked per peer
See [CHANGELOG](https://github.com/CBDDC/ZB.MOM.WW.CBDDC.Net/blob/main/CHANGELOG.md) for complete version history.
## What's New in v0.8.0
### ☁️ Cloud Infrastructure
- **ASP.NET Core Hosting**: Single-cluster deployment mode
- **Multi-Database Support**: SQL Server, PostgreSQL, MySQL, SQLite via EF Core
- **PostgreSQL Optimization**: JSONB storage with GIN indexes
- **Shared-token Authentication**: Secure controlled deployments
- **Health Checks**: Production monitoring and observability
[Learn more about Cloud Deployment →](deployment-modes.md)
## What's New in v0.7.0
### 📦 Efficient Networking
- **Brotli Compression**: Data is automatically compressed, significantly reducing bandwidth usage
- **Protocol v4**: Enhanced framing and security negotiation
## What's New in v0.6.0
### 🔐 Secure Networking
Protect your data in transit with:
- **ECDH** key exchange
- **AES-256-CBC** encryption
- **HMAC-SHA256** authentication
[Learn more about Security →](security.md)
### 🔀 Advanced Conflict Resolution
Choose your strategy:
- **Last Write Wins** - Simple, fast, timestamp-based
- **Recursive Merge** - Intelligent JSON merging with array ID detection
[Learn more about Conflict Resolution →](conflict-resolution.md)
### 🎯 Multi-Target Framework Support
- `netstandard2.0` - Maximum compatibility
- `net6.0` - Modern features
- `net8.0` - Latest performance optimizations
## Next Steps
- [Architecture Overview](architecture.html) - Understand HLC, Gossip Protocol, and mesh networking
- [Persistence Providers](persistence-providers.html) - Choose the right database for your deployment
- [Deployment Modes](deployment-modes.html) - Single-cluster deployment strategy
- [Security Configuration](security.html) - Encryption and authentication
- [Conflict Resolution Strategies](conflict-resolution.html) - LWW vs Recursive Merge
- [Production Hardening](production-hardening.html) - Best practices and monitoring
- [API Reference](api-reference.html) - Complete API documentation

57
docs/index.md Executable file
View File

@@ -0,0 +1,57 @@
---
layout: default
---
# CBDDC Documentation
Welcome to the CBDDC documentation for **version 0.9.0**.
## What's New in v0.9.0
Version 0.9.0 brings enhanced ASP.NET Core support, improved EF Core runtime stability, and refined synchronization and persistence layers.
- **ASP.NET Core Enhancements**: Improved sample application with better error handling
- **EF Core Stability**: Fixed runtime issues and improved persistence layer reliability
- **Sync & Persistence**: Enhanced stability across all persistence providers
- **Production Ready**: All features tested and stable for production use
See the [CHANGELOG](https://github.com/CBDDC/ZB.MOM.WW.CBDDC.Net/blob/main/CHANGELOG.md) for complete details.
## Documentation
### Getting Started
* [**Getting Started**](getting-started.html) - Installation, basic setup, and your first CBDDC application
### Core Concepts
* [Architecture](architecture.html) - Understanding HLC, Gossip Protocol, and P2P mesh networking
* [API Reference](api-reference.html) - Complete API documentation and examples
* [Querying](querying.html) - Data querying patterns and LINQ support
### Persistence & Storage
* [Persistence Providers](persistence-providers.html) - SQLite, EF Core, PostgreSQL comparison and configuration
* [Deployment Modes](deployment-modes.html) - Single-cluster deployment strategy
### Networking & Security
* [Security](security.html) - Encryption, authentication, and secure networking
* [Conflict Resolution](conflict-resolution.html) - LWW and Recursive Merge strategies
* [Network Telemetry](network-telemetry.html) - Monitoring and diagnostics
* [Dynamic Reconfiguration](dynamic-reconfiguration.html) - Runtime configuration and leader election
* [Remote Peer Configuration](remote-peer-configuration.html) - Managing remote peers and tracking lifecycle
* [Upgrade: Peer-Confirmed Pruning](upgrade-peer-confirmed-pruning.html) - Adoption and rollout notes
### Deployment & Operations
* [Deployment (LAN)](deployment-lan.html) - Platform-specific deployment instructions
* [Production Hardening](production-hardening.html) - Configuration, monitoring, and best practices
* [Peer Deprecation & Removal Runbook](peer-deprecation-removal-runbook.html) - Operational deprecation/removal workflow
## Previous Versions
- [v0.8.x Documentation](v0.8/getting-started.html)
- [v0.7.x Documentation](v0.7/getting-started.html)
- [v0.6.x Documentation](v0.6/getting-started.html)
## Links
* [GitHub Repository](https://github.com/CBDDC/ZB.MOM.WW.CBDDC.Net)
* [NuGet Packages](https://www.nuget.org/packages?q=CBDDC)
* [Changelog](https://github.com/CBDDC/ZB.MOM.WW.CBDDC.Net/blob/main/CHANGELOG.md)

71
docs/network-telemetry.md Executable file
View File

@@ -0,0 +1,71 @@
---
layout: default
title: Network Telemetry
nav_order: 9
---
# Network Telemetry
Starting from v0.7.2, CBDDC includes a built-in telemetry system to monitor network performance, compression efficiency, and encryption overhead. This system is designed to have near-zero impact on runtime performance while providing valuable insights into peer-to-peer communication.
## Collected Metrics
The system collects the following metrics:
| Metric Type | Description | Unit |
| :--- | :--- | :--- |
| **CompressionRatio** | Ratio of compressed size to original size. Lower is better (e.g., 0.4 means 60% reduction). | Ratio (0.0 - 1.0) |
| **EncryptionTime** | Time taken to encrypt a message payload. | Milliseconds (ms) |
| **DecryptionTime** | Time taken to decrypt a received message. | Milliseconds (ms) |
| **RoundTripTime** | Time taken for a `GetClock` request-response cycle (latency). | Milliseconds (ms) |
## Architecture
The telemetry service uses a high-performance, non-blocking architecture:
1. **Capture**: Metrics are pushed to a `System.Threading.Channels` queue, ensuring the critical network path is never blocked.
2. **Aggregation**: A background worker aggregates samples into **1-second buckets**.
3. **Rolling Windows**: Averages are calculated on-the-fly for **1m, 5m, 10m, and 30m** windows.
4. **Persistence**: Aggregated data is automatically persisted to a local binary file (`cbddc_metrics.bin`) every minute.
## Configuration
Telemetry is enabled by default when using the standard DI extensions.
### Dependency Injection
When using `AddCBDDCNetwork`, the `INetworkTelemetryService` is automatically registered as a singleton.
```csharp
services.AddCBDDCCore()
.AddCBDDCNetwork<MyNodeConfiguration>();
```
Use `INetworkTelemetryService` to access metric data programmatically:
```csharp
public class MyService
{
private readonly INetworkTelemetryService _telemetry;
public MyService(INetworkTelemetryService telemetry)
{
_telemetry = telemetry;
}
public void PrintMetrics()
{
var snapshot = _telemetry.GetSnapshot();
// Access snapshot[MetricType.CompressionRatio][60] for 1-minute average
}
}
```
## Security & Privacy
- Telemetry data is **stored locally** (on the device/server).
- No data is sent to external servers or other peers.
- Metric data does not contain any PII or payload content.
## Viewing Metrics
You can visualize these metrics in a custom dashboard (desktop or web) for real-time monitoring.

View File

@@ -0,0 +1,66 @@
# Peer Deprecation & Removal Runbook
Operational workflow for safely deprecating or removing peers in clusters using
peer-confirmed pruning.
## When to use this runbook
- A site is permanently decommissioned.
- A peer has been unreachable long enough to block prune progress.
- A peer is being replaced and should stop gating prune decisions.
## Decision matrix
| Scenario | Action |
|------|-----------|
| Peer is temporarily offline and expected to return soon | Keep tracking; monitor lag and confirmations. |
| Peer should stay configured but must stop gating pruning | `RemovePeerTrackingAsync(nodeId, removeRemoteConfig: false)` |
| Peer is permanently removed from topology | `RemoveRemotePeerAsync(nodeId)` |
## Procedure
1. Confirm peer intent (temporary outage vs. decommission).
2. Inspect health payload:
- `peersWithNoConfirmation`
- `laggingPeers`
- `lastSuccessfulConfirmationUpdateByPeer`
3. If deprecating from prune gating only, run:
```csharp
await peerManagement.RemovePeerTrackingAsync(
nodeId: "peer-to-deprecate",
removeRemoteConfig: false,
cancellationToken);
```
4. If permanently removing, run:
```csharp
await peerManagement.RemoveRemotePeerAsync("peer-to-remove", cancellationToken);
```
5. Re-check `/health` and verify status transition:
- `Degraded`/`Unhealthy` should clear if the removed peer was the cause.
6. Confirm maintenance logs no longer report prune blocking for that peer.
## Post-change verification
- Peer no longer appears in active tracked peers.
- `maxLagMs` trends with only current active peers.
- Pruning resumes with a valid effective cutoff (or a known non-peer reason).
## Emergency path
If pruning is blocked and storage pressure is high:
1. De-track the clearly retired peer first (`removeRemoteConfig: false`).
2. Validate pruning resumes.
3. Perform full peer removal after change-control approval.
## Re-activation path
If a deprecated peer returns and should gate pruning again:
1. Ensure peer config is enabled/available.
2. Allow sync to re-register and refresh confirmations.
3. Watch health payload until peer exits `peersWithNoConfirmation`.

280
docs/persistence-providers.md Executable file
View File

@@ -0,0 +1,280 @@
# CBDDC Persistence Providers
CBDDC supports multiple persistence backends to suit different deployment scenarios.
## Overview
| Provider | Best For | Performance | Setup | Production Ready |
|----------|----------|-------------|-------|------------------|
| **SQLite (Direct)** | Embedded apps, single-node | ⭐⭐⭐⭐⭐ | ⭐⭐⭐⭐⭐ | ✅ Yes |
| **EF Core (Generic)** | Multi-DB support, migrations | ⭐⭐⭐ | ⭐⭐⭐ | ✅ Yes |
| **PostgreSQL** | Production, high load, JSON queries | ⭐⭐⭐⭐⭐ | ⭐⭐⭐ | ✅ Yes |
## SQLite (Direct)
**Package:** `ZB.MOM.WW.CBDDC.Persistence.Sqlite`
### Characteristics
-**Zero configuration**: Works out of the box
-**Excellent performance**: Native SQL, no ORM overhead
-**WAL mode**: Concurrent readers + writers
-**Per-collection tables**: Optional for better isolation
-**Snapshots**: Fast reconnection with `SnapshotMetadata`
-**Portable**: Single file database
-**Limited JSON queries**: Uses `json_extract()`
### When to Use
- Building single-node applications
- Embedded scenarios (desktop, mobile)
- Development/testing
- Maximum simplicity required
- File-based portability important
### Configuration
```csharp
// Legacy mode (simple)
services.AddCBDDCSqlite("Data Source=cbddc.db");
// New mode (per-collection tables)
services.AddCBDDCSqlite(options =>
{
options.BasePath = "/var/lib/cbddc";
options.DatabaseFilenameTemplate = "cbddc-{NodeId}.db";
options.UsePerCollectionTables = true;
});
```
### Performance Tips
1. Enable WAL mode (done automatically)
2. Use per-collection tables for large datasets
3. Create indexes on frequently queried fields
4. Keep database on fast storage (SSD)
## EF Core (Generic)
**Package:** `ZB.MOM.WW.CBDDC.Persistence.EntityFramework`
### Characteristics
-**Multi-database support**: SQL Server, MySQL, SQLite, PostgreSQL
-**EF Core benefits**: Migrations, LINQ, change tracking
-**Type-safe**: Strongly-typed entities
- ⚠️ **Query limitation**: JSON queries evaluated in-memory
- ⚠️ **ORM overhead**: Slightly slower than direct SQL
### When to Use
- Need to support multiple database backends
- Team familiar with EF Core patterns
- Want automated migrations
- Building enterprise applications
- Database portability is important
### Configuration
#### SQLite
```csharp
services.AddCBDDCEntityFrameworkSqlite("Data Source=cbddc.db");
```
#### SQL Server
```csharp
services.AddCBDDCEntityFrameworkSqlServer(
"Server=localhost;Database=CBDDC;Trusted_Connection=True;");
```
#### PostgreSQL
```csharp
services.AddDbContext<CBDDCContext>(options =>
options.UseNpgsql(connectionString));
services.AddCBDDCEntityFramework();
```
#### MySQL
```csharp
var serverVersion = ServerVersion.AutoDetect(connectionString);
services.AddCBDDCEntityFrameworkMySql(connectionString, serverVersion);
```
### Migrations
```bash
# Add migration
dotnet ef migrations add InitialCreate --context CBDDCContext
# Apply migration
dotnet ef database update --context CBDDCContext
```
## PostgreSQL
**Package:** `ZB.MOM.WW.CBDDC.Persistence.PostgreSQL`
### Characteristics
-**JSONB native storage**: Optimal JSON handling
-**GIN indexes**: Fast JSON path queries
-**High performance**: Production-grade
-**Connection resilience**: Built-in retry logic
-**Full ACID**: Strong consistency guarantees
- ⚠️ **Future feature**: JSONB query translation (roadmap)
### When to Use
- Production deployments with high traffic
- Need advanced JSON querying (future)
- Require horizontal scalability
- Want best-in-class reliability
- Cloud deployments (AWS RDS, Azure Database, etc.)
### Configuration
```csharp
services.AddCBDDCPostgreSql(
"Host=localhost;Database=CBDDC;Username=user;Password=pass");
// With custom options
services.AddCBDDCPostgreSql(connectionString, options =>
{
options.EnableSensitiveDataLogging(); // Dev only
options.CommandTimeout(30);
});
```
### JSONB Indexes
For optimal performance, create GIN indexes via migrations:
```csharp
protected override void Up(MigrationBuilder migrationBuilder)
{
migrationBuilder.Sql(@"
CREATE INDEX IF NOT EXISTS IX_Documents_ContentJson_gin
ON ""Documents"" USING GIN (""ContentJson"" jsonb_path_ops);
CREATE INDEX IF NOT EXISTS IX_Oplog_PayloadJson_gin
ON ""Oplog"" USING GIN (""PayloadJson"" jsonb_path_ops);
");
}
```
### Connection String Examples
#### Local Development
```
Host=localhost;Port=5432;Database=CBDDC;Username=admin;Password=secret
```
#### Production with SSL
```
Host=prod-db.example.com;Database=CBDDC;Username=admin;Password=secret;SSL Mode=Require
```
#### Connection Pooling
```
Host=localhost;Database=CBDDC;Username=admin;Password=secret;Pooling=true;Minimum Pool Size=5;Maximum Pool Size=100
```
## Feature Comparison
| Feature | SQLite (Direct) | EF Core | PostgreSQL |
|---------|----------------|---------|------------|
| **Storage Format** | File-based | Varies | Server-based |
| **JSON Storage** | TEXT | NVARCHAR/TEXT | JSONB |
| **JSON Indexing** | Standard | Standard | GIN/GIST |
| **JSON Queries** | `json_extract()` | In-Memory | Native (future) |
| **Concurrent Writes** | Good (WAL) | Varies | Excellent |
| **Horizontal Scaling** | No | Limited | Yes (replication) |
| **Migrations** | Manual SQL | EF Migrations | EF Migrations |
| **Connection Pooling** | N/A | Built-in | Built-in |
| **Cloud Support** | N/A | Varies | Excellent |
## Performance Benchmarks
_These are approximate figures for comparison:_
### Write Performance (docs/sec)
| Provider | Single Write | Bulk Insert (1000) |
|----------|--------------|-------------------|
| SQLite | 5,000 | 50,000 |
| EF Core (SQL Server) | 3,000 | 30,000 |
| PostgreSQL | 8,000 | 80,000 |
### Read Performance (docs/sec)
| Provider | Single Read | Query (100 results) |
|----------|-------------|---------------------|
| SQLite | 10,000 | 5,000 |
| EF Core (SQL Server) | 8,000 | 4,000 |
| PostgreSQL | 12,000 | 8,000 |
_*Benchmarks vary based on hardware, network, and configuration_
## Migration Guide
### From SQLite to PostgreSQL
1. Export data from SQLite
2. Set up PostgreSQL database
3. Update connection configuration
4. Import data to PostgreSQL
5. Verify functionality
### From EF Core to PostgreSQL
1. Change NuGet package reference
2. Update service registration
3. Generate new migrations for PostgreSQL
4. Apply migrations
5. Test thoroughly
## Recommendations
### Development
- **Use**: SQLite (Direct)
- **Why**: Fast, simple, portable
### Testing
- **Use**: SQLite (Direct) or EF Core with SQLite
- **Why**: Disposable, fast test execution
### Production (Low-Medium Scale)
- **Use**: SQLite (Direct) with per-collection tables
- **Why**: Excellent performance, simple ops
### Production (High Scale)
- **Use**: PostgreSQL
- **Why**: Best performance, scalability, reliability
### Enterprise
- **Use**: EF Core with SQL Server or PostgreSQL
- **Why**: Enterprise support, compliance, familiarity
## Troubleshooting
### SQLite: "Database is locked"
- Ensure WAL mode is enabled (automatic)
- Increase busy timeout
- Check for long-running transactions
### EF Core: "Query evaluated in-memory"
- Expected for complex JSON queries
- Consider PostgreSQL for better JSON support
- Use indexes on frequently queried properties
### PostgreSQL: "Connection pool exhausted"
- Increase `Maximum Pool Size`
- Check for connection leaks
- Consider connection pooler (PgBouncer)
## Future Enhancements
- **JSONB Query Translation**: Native PostgreSQL JSON queries from QueryNode
- **MongoDB Provider**: NoSQL option for document-heavy workloads
- **Redis Cache Layer**: Hybrid persistence for high-read scenarios
- **Multi-Master PostgreSQL**: Active-active replication support

267
docs/production-hardening.md Executable file
View File

@@ -0,0 +1,267 @@
# Production Hardening - Implementation Guide
## Quick Reference
### Configuration (appsettings.json)
```json
{
"CBDDC": {
"Network": {
"TcpPort": 5000,
"UdpPort": 6000,
"RetryAttempts": 3
},
"Persistence": {
"DatabasePath": "data/cbddc.db",
"EnableWalMode": true,
"CacheSizeMb": 50,
"EnableAutoBackup": true,
"BackupPath": "backups/"
},
"Sync": {
"EnableOfflineQueue": true,
"MaxQueueSize": 1000
}
}
}
```
### DI Setup
```csharp
services.Configure<CBDDCOptions>(configuration.GetSection("CBDDC"));
services.AddSingleton<RetryPolicy>();
services.AddSingleton<DocumentCache>();
services.AddSingleton<OfflineQueue>();
services.AddSingleton<SyncStatusTracker>();
services.AddCBDDCHosting(options =>
{
options.Cluster.NodeId = "server-01";
options.Cluster.TcpPort = 5001;
options.Cluster.PeerConfirmationLagThresholdMs = 30_000;
options.Cluster.PeerConfirmationCriticalLagThresholdMs = 120_000;
});
```
### Health Check
```csharp
app.MapHealthChecks("/health");
```
### Peer Confirmation Lag Thresholds
`CBDDCHealthCheck` evaluates tracked peers using confirmation lag thresholds:
- `PeerConfirmationLagThresholdMs` (default `30000`) marks peers as lagging and
returns `Degraded`.
- `PeerConfirmationCriticalLagThresholdMs` (default `120000`) marks critical lag and
returns `Unhealthy`.
Thresholds are clamped so critical is never lower than lag.
### Health Status Interpretation
For the `cbddc` health check:
| Status | Meaning | Typical operator action |
|------|-----------|-------------|
| Healthy | All active tracked peers have confirmations and lag is within threshold. | No action required. |
| Degraded | At least one tracked peer is lagging, or at least one tracked peer has no confirmation rows yet. | Investigate slow/unconfirmed peers, confirm whether any should be untracked/deprecated. |
| Unhealthy | At least one tracked peer exceeds critical lag threshold, or persistence check throws. | Page on-call, verify storage/network path, and evaluate emergency peer de-tracking for permanently retired peers. |
Health payload fields:
- `trackedPeerCount`
- `peersWithNoConfirmation`
- `maxLagMs`
- `laggingPeers`
- `lastSuccessfulConfirmationUpdateByPeer`
Use these fields to distinguish temporary lag from stale peer registrations.
### Offline Queue
```csharp
// Enqueue during offline
if (!isOnline)
{
offlineQueue.Enqueue(new PendingOperation
{
Type = "put",
Collection = "users",
Key = "user1",
Data = user
});
}
// Flush when back online
var (successful, failed) = await offlineQueue.FlushAsync(async op =>
{
var collection = database.Collection(op.Collection);
if (op.Type == "put" && op.Data != null)
await collection.Put(op.Key, op.Data);
else if (op.Type == "delete")
await collection.Delete(op.Key);
});
```
### Document Cache
```csharp
var cache = new DocumentCache(maxSizeMb: 50);
// Check cache first
var cached = cache.Get("users", "user1");
if (cached != null) return cached;
// Load from database
var doc = await store.GetDocumentAsync("users", "user1");
if (doc != null) cache.Set("users", "user1", doc);
```
### SQLite Backup
```csharp
await store.BackupAsync("backups/backup-20260115.db");
```
### Retry Policy
```csharp
var retry = new RetryPolicy(logger, maxAttempts: 3, delayMs: 1000);
await retry.ExecuteAsync(
() => tcpClient.ConnectAsync(endpoint),
"TCP Connect"
);
```
### Error Handling
Use specific exceptions for robust control flow:
```csharp
try
{
await operation();
}
catch (DocumentNotFoundException ex)
{
// Handle specific document missing case
logger.LogWarning("Document {Key} missing", ex.Key);
}
catch (CBDDCConcurrencyException ex)
{
// Handle conflict (though LWW usually resolves it automatically)
logger.LogWarning("Concurrency conflict: {Message}", ex.Message);
}
catch (NetworkException ex)
{
logger.LogError(ex, "Network operation failed");
syncTracker.RecordError(ex.Message, peerNodeId, ex.ErrorCode);
}
catch (PersistenceException ex) when (ex is DatabaseCorruptionException)
{
logger.LogCritical(ex, "Database corruption detected!");
// Attempt recovery or alert admin
}
```
## Error Codes
| Code | Exception | Description |
|------|-----------|-------------|
| NETWORK_ERROR | NetworkException | Network operation failed |
| PERSISTENCE_ERROR | PersistenceException | Database operation failed |
| SYNC_ERROR | SyncException | Synchronization failed |
| CONFIG_ERROR | ConfigurationException | Invalid configuration |
| TIMEOUT_ERROR | TimeoutException | Operation timed out |
## Logging Levels
- **Trace**: Internal details (cache hits/misses)
- **Debug**: Debugging info (sync operations)
- **Information**: Normal events (peer discovered, backup created)
- **Warning**: Recoverable errors (queue full, retry attempt, documents not found)
- **Error**: Failures requiring attention (sync failed, corruption detected)
- **Critical**: System failures (database initialization failed)
## Best Practices
1. **Always use structured logging**
```csharp
_logger.LogInformation("User {UserId} synced {Count} documents", userId, count);
```
2. **Wrap network operations with retry policy**
```csharp
await _retryPolicy.ExecuteAsync(() => client.ConnectAsync(), "Connect");
```
3. **Check cache before database**
```csharp
var doc = _cache.Get(collection, key) ?? await _store.GetDocumentAsync(collection, key);
```
4. **Enable offline queue for LAN instability**
```csharp
if (options.Sync.EnableOfflineQueue && !isOnline)
_offlineQueue.Enqueue(operation);
```
5. **Periodic health checks**
```csharp
var timer = new Timer(async _ =>
{
var report = await healthCheckService.CheckHealthAsync(r => r.Name == "cbddc");
var entry = report.Entries["cbddc"];
if (entry.Status != HealthStatus.Healthy)
{
_logger.LogWarning(
"CBDDC health is {Status}. LaggingPeers={LaggingPeers} UnconfirmedPeers={UnconfirmedPeers}",
entry.Status,
entry.Data["laggingPeers"],
entry.Data["peersWithNoConfirmation"]);
}
}, null, TimeSpan.Zero, TimeSpan.FromMinutes(5));
```
## Deployment Checklist
- [ ] Configuration file created (appsettings.json)
- [ ] Log directory permissions set
- [ ] Backup directory configured
- [ ] Database file location specified
- [ ] Network ports configured (firewall)
- [ ] Health check endpoint tested
- [ ] Offline queue tested
- [ ] Backup/restore tested
- [ ] Graceful shutdown tested
## Troubleshooting
### Database corruption
```csharp
try
{
await store.CheckIntegrityAsync();
}
catch (DatabaseCorruptionException)
{
// Restore from backup
File.Copy("backups/latest.db", options.Persistence.DatabasePath, overwrite: true);
}
```
### Network issues
```
Check sync tracker:
- Last sync time
- Active peers
- Recent errors
```
### Performance degradation
```csharp
var stats = cache.GetStatistics();
if (stats.HitRate < 0.5)
{
// Consider increasing cache size
options.Persistence.CacheSizeMb = 100;
}
```

53
docs/querying.md Executable file
View File

@@ -0,0 +1,53 @@
---
layout: default
title: Querying
---
# Querying
CBDDC allows querying local collections using a rich set of operators and idiomatic C# syntax, including LINQ support.
## Basic Querying
You can query documents using the `Find` method on a `PeerCollection`.
```csharp
var users = await peerStore.GetCollection<User>("users");
// Precise match
var fabio = await users.Find(u => u.FirstName == "Fabio");
// Comparisons
var adults = await users.Find(u => u.Age >= 18);
// Logical Operators
var activeAdmins = await users.Find(u => u.IsActive && u.Role == "Admin");
```
## Serialization Consistency
CBDDC respects your configured serialization settings. If you use `snake_case` in your JSON serialization but standard C# PascalCase properties, the query translator automatically handles the mapping.
```csharp
// Definition
public class User
{
[JsonPropertyName("first_name")]
public string FirstName { get; set; }
}
// Query
// This translates to a SQL query checking json_extract(data, '$.first_name')
var result = await users.Find(u => u.FirstName == "Fabio");
```
## Supported Operators
- `==` (Equal)
- `!=` (Not Equal)
- `>` (Greater Than)
- `<` (Less Than)
- `>=` (Greater Than or Equal)
- `<=` (Less Than or Equal)
- `&&` (AND)
- `||` (OR)

108
docs/remote-peer-configuration.md Executable file
View File

@@ -0,0 +1,108 @@
# Remote Peer Configuration
Use `IPeerManagementService` to manage persistent remote peers and their pruning
tracking lifecycle at runtime.
## Supported peer operations
- Add a static remote peer
- Remove a configured peer and tracking
- Remove tracking only (keep static peer config)
- Enable/disable a peer
- List all configured peers
## Add a static peer
```csharp
await peerManagement.AddStaticPeerAsync(
"branch-2",
"branch2.example.com:9000",
cancellationToken);
```
## Peer lifecycle model
Peer management now has two independent dimensions:
- **Remote peer configuration**: whether a static peer exists in remote peer config.
- **Peer confirmation tracking**: whether the peer participates in
peer-confirmed prune gating.
Common states:
- **Enabled + tracked**: normal operation, peer is eligible for sync and prune gating.
- **Disabled + tracked**: peer config retained, outbound static sync disabled, still
counted as tracked until tracking is removed/deactivated.
- **Enabled/disabled + untracked**: peer config may remain, but peer is excluded from
prune confirmation gating.
- **Removed**: peer config removed and tracking removed.
## Remove tracking only (deprecate peer from pruning)
Use this when a peer should no longer gate pruning but you are not removing static
configuration yet.
```csharp
await peerManagement.RemovePeerTrackingAsync(
nodeId: "branch-2",
removeRemoteConfig: false,
cancellationToken);
```
## Remove a peer and tracking (full removal)
`RemoveRemotePeerAsync` performs full cleanup by removing both peer tracking and
static peer configuration.
```csharp
await peerManagement.RemoveRemotePeerAsync("branch-2", cancellationToken);
```
Equivalent explicit call:
```csharp
await peerManagement.RemovePeerTrackingAsync(
nodeId: "branch-2",
removeRemoteConfig: true,
cancellationToken);
```
## Enable or disable a peer
```csharp
await peerManagement.DisablePeerAsync("branch-2", cancellationToken);
await peerManagement.EnablePeerAsync("branch-2", cancellationToken);
```
## List configured peers
```csharp
var peers = await peerManagement.GetAllRemotePeersAsync(cancellationToken);
foreach (var peer in peers)
{
Console.WriteLine($"{peer.NodeId} @ {peer.Address} ({peer.Type}) Enabled={peer.IsEnabled}");
}
```
## Storage model
Remote peer configuration is persisted in the peer configuration store and synced across nodes as part of CBDDC metadata replication.
Fields stored per peer:
- `NodeId`
- `Address`
- `Type`
- `IsEnabled`
- `InterestingCollections`
## Notes
- Authentication for sync handshakes is based on the cluster shared token (`AuthToken`) from node configuration.
- Disabled peers remain persisted but are excluded from active sync.
- Peer tracking removal is implemented as deactivation in the confirmation store,
which removes the peer from active prune-gating.
- Re-observed peers can be re-registered and become active for tracking again.
- For rollout steps and production operations, see:
- [Upgrade: Peer-Confirmed Pruning](upgrade-peer-confirmed-pruning.html)
- [Peer Deprecation & Removal Runbook](peer-deprecation-removal-runbook.html)

145
docs/security.md Executable file
View File

@@ -0,0 +1,145 @@
# Network Security
**CBDDC v0.6.0** introduces optional **secure networking** to protect data in transit between peers using industry-standard cryptography.
## Overview
The security layer provides:
- **ECDH (Elliptic Curve Diffie-Hellman)** key exchange for establishing shared secrets
- **AES-256-CBC** encryption for all synchronized data
- **HMAC-SHA256** authentication to prevent tampering
- **Perfect Forward Secrecy** - each session uses unique ephemeral keys
## When to Use Secure Networking
### ✅ Recommended For:
- **Sensitive data** (customer information, financial records, health data)
- **Compliance requirements** (GDPR, HIPAA, PCI-DSS)
- **Untrusted network segments** within your LAN
- **Production deployments** where data confidentiality is important
### ⚠️ Consider Performance Impact:
- Encryption adds ~5-10ms latency per message
- CPU overhead for encryption/decryption
- Not necessary for public/non-sensitive data
## How It Works
### Handshake Process
```
Peer A Peer B
| |
| 1. Generate ECDH keypair |
|------ Public Key A ---------------->| 2. Generate ECDH keypair
| |
|<----- Public Key B -----------------|
| |
3. Compute shared secret 3. Compute shared secret
4. Derive encryption keys 4. Derive encryption keys
| |
|==== Encrypted Communication ====|
```
### Encryption Details
1. **Key Exchange**: NIST P-256 elliptic curve (secp256r1)
2. **Encryption**: AES-256 in CBC mode with random IV per message
3. **Authentication**: HMAC-SHA256 over ciphertext
4. **Message Format**: `IV (16 bytes) + Ciphertext + HMAC (32 bytes)`
## Usage
### Enable Security
```csharp
using CBDDC.Network.Security;
// Register secure handshake service
services.AddSingleton<IPeerHandshakeService, SecureHandshakeService>();
// Network will automatically use encryption if service is registered
services.AddCBDDCNetwork(nodeId, tcpPort, authToken);
```
### Console Sample (--secure flag)
```bash
# Start with encryption enabled
dotnet run --secure
# Or without encryption (faster, less secure)
dotnet run
```
### UI Clients
If you build a UI client, enable secure mode by default to avoid mixed-security clusters.
### Verify Security Status
UI clients should display security status:
- **🔒 Encrypted** - Secure communication active
- **🔓 Plaintext** - No encryption (Console sample default)
## Configuration
### Optional: Custom Security Settings
Currently, security uses default secure parameters. Future versions may support:
- Custom curve selection
- Cipher suite configuration
- Certificate pinning
## Security Considerations
### ✅ What This Protects Against:
- **Eavesdropping**: Data is encrypted in transit
- **Tampering**: HMAC prevents message modification
- **Man-in-the-Middle**: ECDH provides authenticity
### ⚠️ Out of Scope:
- **Data at Rest**: SQLite databases are NOT encrypted. Use OS-level encryption if needed.
- **Authentication**: No peer identity verification beyond shared auth token.
- **Public Internet**: Still designed for trusted LANs. Use VPN/firewall for internet exposure.
## Performance Impact
Benchmarks on typical workloads:
| Operation | Plaintext | Encrypted | Overhead |
|-----------|-----------|-----------|----------|
| Small sync (10 docs) | 15ms | 22ms | +47% |
| Large sync (1000 docs) | 450ms | 520ms | +16% |
| Handshake | N/A | 8ms | Initial |
**Recommendation**: Enable for production. Disable for development/testing if needed.
## Compatibility
- **Secure ↔ Secure**: ✅ Works
- **Plaintext ↔ Plaintext**: ✅ Works
- **Secure ↔ Plaintext**: ❌ Connection fails (by design)
All nodes in a network must use the **same security mode**.
## FAQ
**Q: Can I use this over the public internet?**
A: While encryption helps, CBDDC is still designed for LANs. Use VPN, firewall rules, and consider TLS/SSL wrapping for internet exposure.
**Q: How do I encrypt the SQLite database?**
A: Use SQLCipher or OS-level disk encryption (BitLocker, LUKS, FileVault).
**Q: Can different nodes use different keys?**
A: No. All nodes in a mesh share the same `authToken`. Key rotation is not yet supported.
**Q: What about .NET Standard 2.0 support?**
A: Security works on all target frameworks (netstandard2.0, net6.0, net8.0) with appropriate polyfills.
---
**See Also:**
- [Getting Started](getting-started.html)
- [Architecture](architecture.html)
- [Production Hardening](production-hardening.html)

View File

@@ -0,0 +1,69 @@
# Upgrade Notes: Peer-Confirmed Pruning
This guide covers adopting peer-confirmed pruning semantics introduced across
Phases 1-4.
## What changed
1. Oplog pruning now uses an **effective cutoff**:
- `min(retention cutoff, confirmation cutoff)` when peer confirmations are complete.
- prune is skipped for that cycle when an active tracked peer is missing required confirmation.
2. Peer tracking is now a managed lifecycle:
- `RemovePeerTrackingAsync(nodeId, removeRemoteConfig: false)` deprecates a peer from prune gating.
- `RemoveRemotePeerAsync(nodeId)` removes both static peer config and tracking.
3. Hosting health now includes confirmation lag semantics:
- `Degraded` for lagging or unconfirmed tracked peers.
- `Unhealthy` for critical lag or storage failures.
## Upgrade impact to expect
- During initial rollout, a peer may appear in `peersWithNoConfirmation` until the
first successful confirmation update.
- Any stale active tracked peer can block prune progress and/or keep health degraded.
## Recommended rollout sequence
1. Upgrade one node and validate health payload and pruning logs.
2. Upgrade remaining nodes in the cluster.
3. Audit peer inventory and remove/deprecate stale peers.
4. Tune lag thresholds after observing normal confirmation latency.
## Peer inventory and cleanup
List configured peers:
```csharp
var peers = await peerManagement.GetAllRemotePeersAsync(cancellationToken);
```
Deprecate from pruning only:
```csharp
await peerManagement.RemovePeerTrackingAsync(
nodeId: "retired-peer",
removeRemoteConfig: false,
cancellationToken);
```
Fully remove peer + tracking:
```csharp
await peerManagement.RemoveRemotePeerAsync("retired-peer", cancellationToken);
```
## Validation checklist
- `/health` returns `Healthy` or expected transient `Degraded` during warm-up.
- `laggingPeers` and `peersWithNoConfirmation` converge toward zero for active peers.
- Maintenance logs no longer report prune skip reasons for retired peers.
## Rollback/mitigation
If rollout exposes unexpected persistent degradation:
1. Remove tracking for permanently retired peers.
2. Temporarily raise lag thresholds to reduce alert noise while investigating.
3. Keep full peer removal for nodes that are confirmed decommissioned.
For a detailed operator procedure, see
[Peer Deprecation & Removal Runbook](peer-deprecation-removal-runbook.html).

7
fixdocs-batch1-verify.md Normal file
View File

@@ -0,0 +1,7 @@
# Documentation Analysis Report
Files Scanned: 138
Files With Issues: 0
Total Issues: 0
Status: No documentation issues found

115
fixplan.md Normal file
View File

@@ -0,0 +1,115 @@
# CBDDC Fix Plan
## Goal
Address all remaining issues identified in the architecture/quality audit after EntityFramework removal and persistence merge.
## Current State
- All GitHub workflows/pipelines have been removed from this repository.
- Build and tests pass locally on `CBDDC.slnx`.
- Remaining issues are architecture, warning debt, style drift, dependency deprecations, security hardening, and missing automated fitness gates.
## Issue Inventory
1. **Core framework coupling (CA-02)**
- `Core` references `Microsoft.Extensions.*` and contains DI composition extension classes.
2. **Static analysis and warning debt (BP-05)**
- Nullable warnings in persistence (`CS8618`, `CS8604`).
- `NU1510` warning in network (`System.Threading.Channels`).
3. **Formatting drift (BP-06)**
- `dotnet format --verify-no-changes` fails with widespread whitespace issues (mostly test files).
4. **Dependency deprecations (BP-07)**
- Deprecated `Microsoft.AspNetCore.Http.Abstractions` `2.2.0`.
- Legacy `xunit` `2.9.3` in test projects.
5. **Security baseline gaps (BP-08)**
- Insecure default auth token in default node config.
- `JwtOAuth2Validator` is explicitly demo/basic and does not perform signature/JWKS verification.
6. **No architecture fitness enforcement (BP-09)**
- No architecture rule tests.
- No active pipeline to enforce build/test/format/package/security checks.
## Execution Plan
### Phase 1: Stabilize and Clean Baseline
1. Fix nullable/event warnings in persistence:
- Make `ChangesApplied` nullable or initialize safely.
- Guard null snapshot values before constructing `OplogEntry`.
2. Resolve `NU1510`:
- Remove `System.Threading.Channels` if unused; otherwise document explicit need.
3. Apply formatting once repo-wide:
- Run `dotnet format CBDDC.slnx`.
- Commit mechanical formatting separately.
**Exit criteria**
- `dotnet build CBDDC.slnx` has zero warnings (or only documented, explicitly accepted warnings).
- `dotnet format CBDDC.slnx --verify-no-changes` passes.
### Phase 2: Dependency and Package Modernization
1. Replace deprecated AspNet package:
- Remove `Microsoft.AspNetCore.Http.Abstractions 2.2.0`.
- Use current ASP.NET shared framework-compatible references.
2. Upgrade tests from xUnit 2 to xUnit 3 (or pin with explicit temporary rationale).
3. Re-run package audits:
- `dotnet package list --project CBDDC.slnx --deprecated`
- `dotnet package list --project CBDDC.slnx --include-transitive --vulnerable --format json`
**Exit criteria**
- No unapproved deprecated packages.
- No known vulnerabilities.
### Phase 3: Security Hardening
1. Remove insecure token default behavior:
- Require explicit token provisioning for production paths.
- Keep development fallback only behind clear dev-only opt-in.
2. Replace `JwtOAuth2Validator` with production-grade validator:
- Signature validation (JWKS retrieval/rotation).
- Issuer/audience/lifetime validation.
- Clock skew handling and structured failure reasons.
3. Add unit and integration tests for auth failure/success paths.
**Exit criteria**
- Auth path rejects unsigned/invalid JWTs.
- No default cluster token in production defaults.
### Phase 4: Architecture Boundary Cleanup
1. Move DI composition out of `Core` into outer adapters/host packages.
2. Keep `Core` limited to domain/application contracts and logic.
3. Validate no framework namespace leakage into `Core`.
**Exit criteria**
- `Core` no longer references DI/logging framework packages except where explicitly approved.
- Architecture tests enforce the boundary.
### Phase 5: Fitness Functions and Automation Reintroduction
1. Add architecture tests:
- Rule: `Core` must not depend on `Network`, `Persistence`, `AspNet`, or host frameworks.
- Rule: layer graph must remain acyclic.
2. Reintroduce a **new** minimal CI workflow (after review/approval):
- `dotnet restore`, `dotnet build`, `dotnet test`
- `dotnet format --verify-no-changes`
- package vulnerable/deprecated checks
3. Add fail-fast quality gates on pull requests.
**Exit criteria**
- Every architecture/quality rule is automatically enforced on PRs.
## Verification Commands
Run from `/Users/dohertj2/Desktop/CBDDC`:
```bash
dotnet restore CBDDC.slnx
dotnet build CBDDC.slnx
dotnet test CBDDC.slnx
dotnet format CBDDC.slnx --verify-no-changes
dotnet package list --project CBDDC.slnx --include-transitive --vulnerable --format json
dotnet package list --project CBDDC.slnx --deprecated
```
## Suggested Order of Delivery
1. Phase 1 (stability/style baseline)
2. Phase 2 (dependencies)
3. Phase 3 (security)
4. Phase 4 (architecture cleanup)
5. Phase 5 (fitness automation)
## Notes for Review
- I split mechanical formatting from behavioral/security changes to keep diffs reviewable.
- Reintroducing CI is intentionally deferred to Phase 5 so it enforces the cleaned baseline, not current debt.

12
pack.bat Executable file
View File

@@ -0,0 +1,12 @@
@echo off
echo Packing CBDDC NuGet Packages...
mkdir nupkgs 2>nul
dotnet pack src/ZB.MOM.WW.CBDDC.Core/ZB.MOM.WW.CBDDC.Core.csproj -c Release -o nupkgs
dotnet pack src/ZB.MOM.WW.CBDDC.Network/ZB.MOM.WW.CBDDC.Network.csproj -c Release -o nupkgs
dotnet pack src/ZB.MOM.WW.CBDDC.Persistence/ZB.MOM.WW.CBDDC.Persistence.csproj -c Release -o nupkgs
echo.
echo Packages created in 'nupkgs' directory.
dir nupkgs
pause

76
samples/README.md Executable file
View File

@@ -0,0 +1,76 @@
# CBDDC Samples
This directory contains sample applications demonstrating **CBDDC v0.6** integration across different platforms using the **Lifter** hosting model.
## 🔗 Cross-Platform Cluster Compatibility
All samples are configured to run on the **same cluster** securely. You can run them simultaneously to test real-time P2P synchronization.
| Parameter | Value | Description |
| :--- | :--- | :--- |
| **AuthToken** | `demo-secret-key` | Shared secret for secure discovery |
| **Discovery Port** | `6000` (UDP) | Port for local peer discovery |
| **DB Mode** | SQLite (WAL) | Persistent storage |
### 🖥️ ZB.MOM.WW.CBDDC.Sample.Console
Interactive CLI node. Good for monitoring the mesh.
**Run:**
```bash
cd ZB.MOM.WW.CBDDC.Sample.Console
dotnet run
# OR simulate multiple nodes:
dotnet run node-2 5001
dotnet run node-3 5002
# Enable recursive merge conflict resolution:
dotnet run --merge
```
## 🧪 Quick Test Scenario
1. Start **Console** app (creates `node-1`).
**All Nodes Must Use Same Security Mode:**
- Secure ↔ Secure: ✅ Works
- Plaintext ↔ Plaintext: ✅ Works
- Secure ↔ Plaintext: ❌ Connection fails
2. In Console, type `l` to list peers.
3. In Console, type `p` to put some data (`Alice`, `Bob`).
## 📚 Documentation
For complete v0.6 documentation, see:
- [Getting Started](../docs/v0.6/getting-started.html)
- [Security](../docs/v0.6/security.html)
- [Conflict Resolution](../docs/v0.6/conflict-resolution.html)
- [Architecture](../docs/v0.6/architecture.html)
## 🆕 Conflict Resolution Demo
**New Commands** (Console sample):
- `demo` - Run automated conflict scenario
- `todos` - View all TodoLists
- `resolver [lww|merge]` - Show current strategy
**Try Recursive Merge:**
```bash
cd ZB.MOM.WW.CBDDC.Sample.Console
dotnet run --merge
# Type: demo
```
The demo simulates concurrent edits to a TodoList. With `--merge`, both changes are preserved by merging array items by `id`. Without it (LWW), last write wins.
## 🔒 Network Security
**Enable encrypted communication:**
```bash
dotnet run --secure
```
Features:
- ECDH key exchange for session establishment
- AES-256-CBC encryption with HMAC-SHA256 authentication
- Visual indicators: 🔒 (encrypted) vs 🔓 (plaintext)
- Status displayed in `l` (list peers) and `h` (health) commands
**Note**: All nodes in a cluster must use the same security mode (all `--secure` or all plaintext).

View File

@@ -0,0 +1,348 @@
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Logging;
using ZB.MOM.WW.CBDDC.Core;
using ZB.MOM.WW.CBDDC.Core.Cache;
using ZB.MOM.WW.CBDDC.Core.Diagnostics;
using ZB.MOM.WW.CBDDC.Core.Sync;
using ZB.MOM.WW.CBDDC.Core.Storage;
using ZB.MOM.WW.CBDDC.Network;
using ZB.MOM.WW.CBDDC.Persistence.BLite;
using Microsoft.Extensions.DependencyInjection; // For IServiceProvider if needed
using Serilog.Context;
using ZB.MOM.WW.CBDDC.Sample.Console;
using ZB.MOM.WW.CBDDC.Core.Network;
namespace ZB.MOM.WW.CBDDC.Sample.Console;
public class ConsoleInteractiveService : BackgroundService
{
private readonly ILogger<ConsoleInteractiveService> _logger;
private readonly SampleDbContext _db;
private readonly ICBDDCNode _node;
private readonly IHostApplicationLifetime _lifetime;
// Auxiliary services for status/commands
private readonly IDocumentCache _cache;
private readonly IOfflineQueue _queue;
private readonly ICBDDCHealthCheck _healthCheck;
private readonly ISyncStatusTracker _syncTracker;
private readonly IServiceProvider _serviceProvider;
private readonly IPeerNodeConfigurationProvider _configProvider;
/// <summary>
/// Initializes a new instance of the <see cref="ConsoleInteractiveService"/> class.
/// </summary>
/// <param name="logger">The logger used by the interactive service.</param>
/// <param name="db">The sample database context.</param>
/// <param name="node">The active CBDDC node instance.</param>
/// <param name="lifetime">The application lifetime controller.</param>
/// <param name="cache">The document cache service.</param>
/// <param name="queue">The offline queue service.</param>
/// <param name="healthCheck">The health check service.</param>
/// <param name="syncTracker">The sync status tracker.</param>
/// <param name="serviceProvider">The service provider for resolving optional services.</param>
/// <param name="peerNodeConfigurationProvider">The provider for peer node configuration.</param>
public ConsoleInteractiveService(
ILogger<ConsoleInteractiveService> logger,
SampleDbContext db,
ICBDDCNode node,
IHostApplicationLifetime lifetime,
IDocumentCache cache,
IOfflineQueue queue,
ICBDDCHealthCheck healthCheck,
ISyncStatusTracker syncTracker,
IServiceProvider serviceProvider,
IPeerNodeConfigurationProvider peerNodeConfigurationProvider)
{
_logger = logger;
_db = db;
_node = node;
_lifetime = lifetime;
_cache = cache;
_queue = queue;
_healthCheck = healthCheck;
_syncTracker = syncTracker;
_serviceProvider = serviceProvider;
_configProvider = peerNodeConfigurationProvider;
}
/// <inheritdoc />
protected override async Task ExecuteAsync(CancellationToken stoppingToken)
{
var config = await _configProvider.GetConfiguration();
System.Console.WriteLine($"--- Interactive Console ---");
System.Console.WriteLine($"Node ID: {config.NodeId}");
PrintHelp();
while (!stoppingToken.IsCancellationRequested)
{
// Non-blocking read to allow cancellation check
if (!System.Console.KeyAvailable)
{
await Task.Delay(100, stoppingToken);
continue;
}
var input = System.Console.ReadLine();
if (string.IsNullOrEmpty(input)) continue;
try
{
using (LogContext.PushProperty("NodeId", config.NodeId))
using (LogContext.PushProperty("Command", input))
using (LogContext.PushProperty("OperationId", Guid.NewGuid().ToString("N")))
{
_logger.LogInformation("Handling interactive command {Command}", input);
await HandleInput(input);
}
}
catch (Exception ex)
{
System.Console.WriteLine($"Error: {ex.Message}");
}
if (input == "q" || input == "quit")
{
_lifetime.StopApplication();
break;
}
}
}
private void PrintHelp()
{
System.Console.WriteLine("Commands:");
System.Console.WriteLine(" [p]ut, [g]et, [d]elete, [f]ind, [l]ist peers, [q]uit");
System.Console.WriteLine(" [n]ew (auto), [s]pam (5x), [c]ount, [t]odos");
System.Console.WriteLine(" [h]ealth, cac[h]e");
System.Console.WriteLine(" [r]esolver [lww|merge], [demo] conflict");
}
private async Task HandleInput(string input)
{
var config = await _configProvider.GetConfiguration();
if (input.StartsWith("n"))
{
var ts = DateTime.Now.ToString("HH:mm:ss.fff");
var user = new User { Id = Guid.NewGuid().ToString(), Name = $"User-{ts}", Age = new Random().Next(18, 90), Address = new Address { City = "AutoCity" } };
await _db.Users.InsertAsync(user);
await _db.SaveChangesAsync();
System.Console.WriteLine($"[+] Created {user.Name} with Id: {user.Id}...");
}
else if (input.StartsWith("s"))
{
for (int i = 0; i < 5; i++)
{
var ts = DateTime.Now.ToString("HH:mm:ss.fff");
var user = new User { Id = Guid.NewGuid().ToString(), Name = $"User-{ts}", Age = new Random().Next(18, 90), Address = new Address { City = "SpamCity" } };
await _db.Users.InsertAsync(user);
System.Console.WriteLine($"[+] Created {user.Name} with Id: {user.Id}...");
await Task.Delay(100);
}
await _db.SaveChangesAsync();
}
else if (input.StartsWith("c"))
{
var userCount = _db.Users.FindAll().Count();
var todoCount = _db.TodoLists.FindAll().Count();
System.Console.WriteLine($"Collection 'Users': {userCount} documents");
System.Console.WriteLine($"Collection 'TodoLists': {todoCount} documents");
}
else if (input.StartsWith("p"))
{
var alice = new User { Id = Guid.NewGuid().ToString(), Name = "Alice", Age = 30, Address = new Address { City = "Paris" } };
var bob = new User { Id = Guid.NewGuid().ToString(), Name = "Bob", Age = 25, Address = new Address { City = "Rome" } };
await _db.Users.InsertAsync(alice);
await _db.Users.InsertAsync(bob);
await _db.SaveChangesAsync();
System.Console.WriteLine($"Put Alice ({alice.Id}) and Bob ({bob.Id})");
}
else if (input.StartsWith("g"))
{
System.Console.Write("Enter user Id: ");
var id = System.Console.ReadLine();
if (!string.IsNullOrEmpty(id))
{
var u = _db.Users.FindById(id);
System.Console.WriteLine(u != null ? $"Got: {u.Name}, Age {u.Age}, City: {u.Address?.City}" : "Not found");
}
}
else if (input.StartsWith("d"))
{
System.Console.Write("Enter user Id to delete: ");
var id = System.Console.ReadLine();
if (!string.IsNullOrEmpty(id))
{
await _db.Users.DeleteAsync(id);
await _db.SaveChangesAsync();
System.Console.WriteLine($"Deleted user {id}");
}
}
else if (input.StartsWith("l"))
{
var peers = _node.Discovery.GetActivePeers();
var handshakeSvc = _serviceProvider.GetService<ZB.MOM.WW.CBDDC.Network.Security.IPeerHandshakeService>();
var secureIcon = handshakeSvc != null ? "🔒" : "🔓";
System.Console.WriteLine($"Active Peers ({secureIcon}):");
foreach (var p in peers)
System.Console.WriteLine($" - {p.NodeId} at {p.Address}");
if (handshakeSvc != null)
System.Console.WriteLine("\n Secure mode: Connections use ECDH + AES-256");
}
else if (input.StartsWith("f"))
{
System.Console.WriteLine("Query: Age > 28");
var results = _db.Users.Find(u => u.Age > 28);
foreach (var u in results) System.Console.WriteLine($"Found: {u.Name} ({u.Age})");
}
else if (input.StartsWith("h"))
{
var health = await _healthCheck.CheckAsync();
var syncStatus = _syncTracker.GetStatus();
var handshakeSvc = _serviceProvider.GetService<ZB.MOM.WW.CBDDC.Network.Security.IPeerHandshakeService>();
System.Console.WriteLine("=== Health Check ===");
System.Console.WriteLine($"Database: {(health.DatabaseHealthy ? "" : "")}");
System.Console.WriteLine($"Network: {(health.NetworkHealthy ? "" : "")}");
System.Console.WriteLine($"Security: {(handshakeSvc != null ? "🔒 Encrypted" : "🔓 Plaintext")}");
System.Console.WriteLine($"Connected Peers: {health.ConnectedPeers}");
System.Console.WriteLine($"Last Sync: {health.LastSyncTime?.ToString("HH:mm:ss") ?? "Never"}");
System.Console.WriteLine($"Total Synced: {syncStatus.TotalDocumentsSynced} docs");
if (health.Errors.Any())
{
System.Console.WriteLine("Errors:");
foreach (var err in health.Errors.Take(3)) System.Console.WriteLine($" - {err}");
}
}
else if (input.StartsWith("ch") || input == "cache")
{
var stats = _cache.GetStatistics();
System.Console.WriteLine($"=== Cache Stats ===\nSize: {stats.Size}\nHits: {stats.Hits}\nMisses: {stats.Misses}\nRate: {stats.HitRate:P1}");
}
else if (input.StartsWith("r") && input.Contains("resolver"))
{
var parts = input.Split(' ');
if (parts.Length > 1)
{
var newResolver = parts[1].ToLower() switch
{
"lww" => (IConflictResolver)new LastWriteWinsConflictResolver(),
"merge" => new RecursiveNodeMergeConflictResolver(),
_ => null
};
if (newResolver != null)
{
// Note: Requires restart to fully apply. For demo, we inform user.
System.Console.WriteLine($"⚠️ Resolver changed to {parts[1].ToUpper()}. Restart node to apply.");
System.Console.WriteLine($" (Current session continues with previous resolver)");
}
else
{
System.Console.WriteLine("Usage: resolver [lww|merge]");
}
}
}
else if (input == "demo")
{
await RunConflictDemo();
}
else if (input == "todos")
{
var lists = _db.TodoLists.FindAll();
System.Console.WriteLine("=== Todo Lists ===");
foreach (var list in lists)
{
System.Console.WriteLine($"📋 {list.Name} ({list.Items.Count} items)");
foreach (var item in list.Items)
{
var status = item.Completed ? "✓" : " ";
System.Console.WriteLine($" [{status}] {item.Task}");
}
}
}
}
private async Task RunConflictDemo()
{
System.Console.WriteLine("\n=== Conflict Resolution Demo ===");
System.Console.WriteLine("Simulating concurrent edits to a TodoList...\n");
// Create initial list
var list = new TodoList
{
Id = Guid.NewGuid().ToString(),
Name = "Shopping List",
Items = new List<TodoItem>
{
new TodoItem { Task = "Buy milk", Completed = false },
new TodoItem { Task = "Buy bread", Completed = false }
}
};
await _db.TodoLists.InsertAsync(list);
await _db.SaveChangesAsync();
System.Console.WriteLine($"✓ Created list '{list.Name}' with {list.Items.Count} items");
await Task.Delay(100);
// Simulate Node A edit: Mark item as completed, add new item
var listA = _db.TodoLists.FindById(list.Id);
if (listA != null)
{
listA.Items[0].Completed = true; // Mark milk as done
listA.Items.Add(new TodoItem { Task = "Buy eggs", Completed = false });
await _db.TodoLists.UpdateAsync(listA);
await _db.SaveChangesAsync();
System.Console.WriteLine("📝 Node A: Marked 'Buy milk' complete, added 'Buy eggs'");
}
await Task.Delay(100);
// Simulate Node B edit: Mark different item, add different item
var listB = _db.TodoLists.FindById(list.Id);
if (listB != null)
{
listB.Items[1].Completed = true; // Mark bread as done
listB.Items.Add(new TodoItem { Task = "Buy cheese", Completed = false });
await _db.TodoLists.UpdateAsync(listB);
await _db.SaveChangesAsync();
System.Console.WriteLine("📝 Node B: Marked 'Buy bread' complete, added 'Buy cheese'");
}
await Task.Delay(200);
// Show final merged state
var merged = _db.TodoLists.FindById(list.Id);
if (merged != null)
{
System.Console.WriteLine("\n🔀 Merged Result:");
System.Console.WriteLine($" List: {merged.Name}");
foreach (var item in merged.Items)
{
var status = item.Completed ? "✓" : " ";
System.Console.WriteLine($" [{status}] {item.Task}");
}
var resolver = _serviceProvider.GetRequiredService<IConflictResolver>();
var resolverType = resolver.GetType().Name;
System.Console.WriteLine($"\n Resolution Strategy: {resolverType}");
if (resolverType.Contains("Recursive"))
{
System.Console.WriteLine(" → Items merged by 'id', both edits preserved");
}
else
{
System.Console.WriteLine(" → Last write wins, Node B changes override Node A");
}
}
System.Console.WriteLine("\n✓ Demo complete. Run 'todos' to see all lists.\n");
}
}

View File

@@ -0,0 +1,126 @@
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
using ZB.MOM.WW.CBDDC.Core;
using ZB.MOM.WW.CBDDC.Core.Storage;
using ZB.MOM.WW.CBDDC.Core.Cache;
using ZB.MOM.WW.CBDDC.Core.Sync;
using ZB.MOM.WW.CBDDC.Core.Diagnostics;
using ZB.MOM.WW.CBDDC.Core.Resilience;
using ZB.MOM.WW.CBDDC.Network;
using ZB.MOM.WW.CBDDC.Network.Security;
using ZB.MOM.WW.CBDDC.Persistence.BLite;
using ZB.MOM.WW.CBDDC.Sample.Console;
using Microsoft.Extensions.Hosting;
using Serilog;
using ZB.MOM.WW.CBDDC.Core.Network;
namespace ZB.MOM.WW.CBDDC.Sample.Console;
// Local User/Address classes removed in favor of Shared project
class Program
{
static async Task Main(string[] args)
{
var builder = Host.CreateApplicationBuilder(args);
// Configuration
builder.Configuration.SetBasePath(Directory.GetCurrentDirectory())
.AddJsonFile("appsettings.json", optional: true, reloadOnChange: true);
// Logging
builder.Logging.ClearProviders();
builder.Services.AddSerilog((_, loggerConfiguration) =>
loggerConfiguration
.MinimumLevel.Information()
.Enrich.FromLogContext()
.Enrich.WithProperty("Application", "CBDDC.Sample.Console")
.WriteTo.Console());
var randomPort = new Random().Next(1000, 9999);
// Node ID
string nodeId = args.Length > 0 ? args[0] : ("node-" + randomPort);
int tcpPort = args.Length > 1 ? int.Parse(args[1]) : randomPort;
// Conflict Resolution Strategy (can be switched at runtime via service replacement)
var useRecursiveMerge = args.Contains("--merge");
if (useRecursiveMerge)
{
builder.Services.AddSingleton<IConflictResolver, RecursiveNodeMergeConflictResolver>();
}
IPeerNodeConfigurationProvider peerNodeConfigurationProvider = new StaticPeerNodeConfigurationProvider(
new PeerNodeConfiguration
{
NodeId = nodeId,
TcpPort = tcpPort,
AuthToken = "Test-Cluster-Key",
//KnownPeers = builder.Configuration.GetSection("CBDDC:KnownPeers").Get<List<KnownPeerConfiguration>>() ?? new()
});
builder.Services.AddSingleton<IPeerNodeConfigurationProvider>(peerNodeConfigurationProvider);
// Database path
var dataPath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "data");
Directory.CreateDirectory(dataPath);
var databasePath = Path.Combine(dataPath, $"{nodeId}.blite");
// Register CBDDC Services using Fluent Extensions with BLite, SampleDbContext, and SampleDocumentStore
builder.Services.AddCBDDCCore()
.AddCBDDCBLite<SampleDbContext, SampleDocumentStore>(sp => new SampleDbContext(databasePath))
.AddCBDDCNetwork<StaticPeerNodeConfigurationProvider>(); // useHostedService = true by default
builder.Services.AddHostedService<ConsoleInteractiveService>(); // Runs the Input Loop
var host = builder.Build();
System.Console.WriteLine($"? Node {nodeId} initialized on port {tcpPort}");
System.Console.WriteLine($"? Database: {databasePath}");
System.Console.WriteLine();
await host.RunAsync();
}
private class StaticPeerNodeConfigurationProvider : IPeerNodeConfigurationProvider
{
/// <summary>
/// Gets or sets the current peer node configuration.
/// </summary>
public PeerNodeConfiguration Configuration { get; set; }
/// <summary>
/// Initializes a new instance of the <see cref="StaticPeerNodeConfigurationProvider"/> class.
/// </summary>
/// <param name="configuration">The initial peer node configuration.</param>
public StaticPeerNodeConfigurationProvider(PeerNodeConfiguration configuration)
{
Configuration = configuration;
}
/// <summary>
/// Occurs when the peer node configuration changes.
/// </summary>
public event PeerNodeConfigurationChangedEventHandler? ConfigurationChanged;
/// <summary>
/// Gets the current peer node configuration.
/// </summary>
/// <returns>A task that returns the current configuration.</returns>
public Task<PeerNodeConfiguration> GetConfiguration()
{
return Task.FromResult(Configuration);
}
/// <summary>
/// Raises the configuration changed event.
/// </summary>
/// <param name="newConfig">The new configuration value.</param>
protected virtual void OnConfigurationChanged(PeerNodeConfiguration newConfig)
{
ConfigurationChanged?.Invoke(this, newConfig);
}
}
}

View File

@@ -0,0 +1,11 @@
{
"profiles": {
"WSL": {
"commandName": "WSL2",
"distributionName": ""
},
"ZB.MOM.WW.CBDDC.Sample.Console": {
"commandName": "Project"
}
}
}

View File

@@ -0,0 +1,154 @@
# CBDDC Sample Console Application
This sample demonstrates the core features of CBDDC, a distributed peer-to-peer database with automatic synchronization.
## Features Demonstrated
### 🔑 Primary Keys & Auto-Generation
- Automatic GUID generation for entities
- Convention-based key detection (`Id` property)
- `[PrimaryKey]` attribute support
### 🎯 Generic Type-Safe API
- `Collection<T>()` for compile-time type safety
- Keyless `Put(entity)` with auto-key extraction
- IntelliSense-friendly operations
### 🔍 LINQ Query Support
- Expression-based queries
- Paging and sorting
- Complex predicates (>, >=, ==, !=, nested properties)
### 🌐 Network Synchronization
- UDP peer discovery
- TCP synchronization
- Automatic conflict resolution (Last-Write-Wins)
## Running the Sample
### Single Node
```bash
dotnet run
```
### Multi-Node (Peer-to-Peer)
Terminal 1:
```bash
dotnet run -- --node-id node1 --tcp-port 5001 --udp-port 6001
```
Terminal 2:
```bash
dotnet run -- --node-id node2 --tcp-port 5002 --udp-port 6002
```
Terminal 3:
```bash
dotnet run -- --node-id node3 --tcp-port 5003 --udp-port 6003
```
Changes made on any node will automatically sync to all peers!
## Available Commands
| Command | Description |
|---------|-------------|
| `p` | Put Alice and Bob (auto-generated IDs) |
| `g` | Get user by ID (prompts for ID) |
| `d` | Delete user by ID (prompts for ID) |
| `n` | Create new user with auto-generated ID |
| `s` | Spam 5 users with auto-generated IDs |
| `c` | Count total documents |
| `f` | Demo various Find queries |
| `f2` | Demo Find with paging (skip/take) |
| `a` | Demo auto-generated primary keys |
| `t` | Demo generic typed API |
| `l` | List active peers |
| `q` | Quit |
## Example Session
```
--- Started node1 on Port 5001 ---
Commands: [p]ut, [g]et, [d]elete, [l]ist peers, [q]uit, [f]ind
[n]ew (auto-generate), [s]pam (5x auto), [c]ount
[a]uto-keys (demo), [t]yped (demo generic API)
> p
Put Alice (Id: 3fa85f64...) and Bob (Id: 7c9e6679...)
> c
Total Documents: 2
> f
Query: Age > 28
Found: Alice (30)
> a
=== Auto-Generated Primary Keys Demo ===
Created: AutoUser1 with auto-generated Id: 9b2c3d4e...
Created: AutoUser2 with auto-generated Id: 1a2b3c4d...
Retrieved: AutoUser1 (Age: 25)
> l
Active Peers:
- node2 at 127.0.0.1:5002
- node3 at 127.0.0.1:5003
```
## Code Highlights
### Entity Definition
```csharp
using ZB.MOM.WW.CBDDC.Core.Metadata;
public class User
{
[PrimaryKey(AutoGenerate = true)]
public string Id { get; set; } = "";
public string? Name { get; set; }
[Indexed]
public int Age { get; set; }
public Address? Address { get; set; }
}
```
### Using the API
```csharp
// Get typed collection
var users = db.Collection<User>();
// Auto-generates Id
var user = new User { Name = "Alice", Age = 30 };
await users.Put(user);
Console.WriteLine(user.Id); // "3fa85f64-5717-4562-b3fc-2c963f66afa6"
// Retrieve by ID
var retrieved = await users.Get(user.Id);
// Query with LINQ
var results = await users.Find(u => u.Age > 30);
// Paging
var page = await users.Find(u => true, skip: 10, take: 5);
```
## Architecture
- **Storage**: SQLite with HLC timestamps
- **Sync**: TCP for data transfer, UDP for discovery
- **Conflict Resolution**: Last-Write-Wins based on Hybrid Logical Clocks
- **Serialization**: System.Text.Json
## Learn More
- [API Reference](../../docs/api-reference.md)
- [Architecture](../../docs/architecture.md)
- [Getting Started](../../docs/getting-started.md)

View File

@@ -0,0 +1,55 @@
using BLite.Core.Collections;
using BLite.Core.Metadata;
using BLite.Core.Storage;
using ZB.MOM.WW.CBDDC.Persistence.BLite;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace ZB.MOM.WW.CBDDC.Sample.Console;
public partial class SampleDbContext : CBDDCDocumentDbContext
{
/// <summary>
/// Gets the users collection.
/// </summary>
public DocumentCollection<string, User> Users { get; private set; } = null!;
/// <summary>
/// Gets the todo lists collection.
/// </summary>
public DocumentCollection<string, TodoList> TodoLists { get; private set; } = null!;
/// <summary>
/// Initializes a new instance of the SampleDbContext class using the specified database file path.
/// </summary>
/// <param name="databasePath">The file system path to the database file. Cannot be null or empty.</param>
public SampleDbContext(string databasePath) : base(databasePath)
{
}
/// <summary>
/// Initializes a new instance of the SampleDbContext class using the specified database file path and page file
/// configuration.
/// </summary>
/// <param name="databasePath">The file system path to the database file. Cannot be null or empty.</param>
/// <param name="config">The configuration settings for the page file. Cannot be null.</param>
public SampleDbContext(string databasePath, PageFileConfig config) : base(databasePath, config)
{
}
/// <inheritdoc />
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
base.OnModelCreating(modelBuilder);
modelBuilder.Entity<User>()
.ToCollection("Users")
.HasKey(u => u.Id);
modelBuilder.Entity<TodoList>()
.ToCollection("TodoLists")
.HasKey(t => t.Id);
}
}

View File

@@ -0,0 +1,164 @@
using ZB.MOM.WW.CBDDC.Core;
using ZB.MOM.WW.CBDDC.Core.Network;
using ZB.MOM.WW.CBDDC.Core.Storage;
using ZB.MOM.WW.CBDDC.Core.Sync;
using ZB.MOM.WW.CBDDC.Persistence.BLite;
using Microsoft.Extensions.Logging;
using System.Text.Json;
namespace ZB.MOM.WW.CBDDC.Sample.Console;
/// <summary>
/// Document store implementation for CBDDC Sample using BLite persistence.
/// Extends BLiteDocumentStore to automatically handle Oplog creation via CDC.
/// </summary>
public class SampleDocumentStore : BLiteDocumentStore<SampleDbContext>
{
private const string UsersCollection = "Users";
private const string TodoListsCollection = "TodoLists";
/// <summary>
/// Initializes a new instance of the <see cref="SampleDocumentStore"/> class.
/// </summary>
/// <param name="context">The sample database context.</param>
/// <param name="configProvider">The peer node configuration provider.</param>
/// <param name="vectorClockService">The vector clock service.</param>
/// <param name="logger">The optional logger instance.</param>
public SampleDocumentStore(
SampleDbContext context,
IPeerNodeConfigurationProvider configProvider,
IVectorClockService vectorClockService,
ILogger<SampleDocumentStore>? logger = null)
: base(context, configProvider, vectorClockService, new LastWriteWinsConflictResolver(), logger)
{
// Register CDC watchers for local change detection
// InterestedCollection is automatically populated
WatchCollection(UsersCollection, context.Users, u => u.Id);
WatchCollection(TodoListsCollection, context.TodoLists, t => t.Id);
}
#region Abstract Method Implementations
/// <inheritdoc />
protected override async Task ApplyContentToEntityAsync(
string collection, string key, JsonElement content, CancellationToken cancellationToken)
{
UpsertEntity(collection, key, content);
await _context.SaveChangesAsync(cancellationToken);
}
/// <inheritdoc />
protected override async Task ApplyContentToEntitiesBatchAsync(
IEnumerable<(string Collection, string Key, JsonElement Content)> documents, CancellationToken cancellationToken)
{
foreach (var (collection, key, content) in documents)
{
UpsertEntity(collection, key, content);
}
await _context.SaveChangesAsync(cancellationToken);
}
private void UpsertEntity(string collection, string key, JsonElement content)
{
switch (collection)
{
case UsersCollection:
var user = content.Deserialize<User>()!;
user.Id = key;
var existingUser = _context.Users.Find(u => u.Id == key).FirstOrDefault();
if (existingUser != null)
_context.Users.Update(user);
else
_context.Users.Insert(user);
break;
case TodoListsCollection:
var todoList = content.Deserialize<TodoList>()!;
todoList.Id = key;
var existingTodoList = _context.TodoLists.Find(t => t.Id == key).FirstOrDefault();
if (existingTodoList != null)
_context.TodoLists.Update(todoList);
else
_context.TodoLists.Insert(todoList);
break;
default:
throw new NotSupportedException($"Collection '{collection}' is not supported for sync.");
}
}
/// <inheritdoc />
protected override Task<JsonElement?> GetEntityAsJsonAsync(
string collection, string key, CancellationToken cancellationToken)
{
return Task.FromResult<JsonElement?>(collection switch
{
UsersCollection => SerializeEntity(_context.Users.Find(u => u.Id == key).FirstOrDefault()),
TodoListsCollection => SerializeEntity(_context.TodoLists.Find(t => t.Id == key).FirstOrDefault()),
_ => null
});
}
/// <inheritdoc />
protected override async Task RemoveEntityAsync(
string collection, string key, CancellationToken cancellationToken)
{
DeleteEntity(collection, key);
await _context.SaveChangesAsync(cancellationToken);
}
/// <inheritdoc />
protected override async Task RemoveEntitiesBatchAsync(
IEnumerable<(string Collection, string Key)> documents, CancellationToken cancellationToken)
{
foreach (var (collection, key) in documents)
{
DeleteEntity(collection, key);
}
await _context.SaveChangesAsync(cancellationToken);
}
private void DeleteEntity(string collection, string key)
{
switch (collection)
{
case UsersCollection:
_context.Users.Delete(key);
break;
case TodoListsCollection:
_context.TodoLists.Delete(key);
break;
default:
_logger.LogWarning("Attempted to remove entity from unsupported collection: {Collection}", collection);
break;
}
}
/// <inheritdoc />
protected override async Task<IEnumerable<(string Key, JsonElement Content)>> GetAllEntitiesAsJsonAsync(
string collection, CancellationToken cancellationToken)
{
return await Task.Run(() => collection switch
{
UsersCollection => _context.Users.FindAll()
.Select(u => (u.Id, SerializeEntity(u)!.Value)),
TodoListsCollection => _context.TodoLists.FindAll()
.Select(t => (t.Id, SerializeEntity(t)!.Value)),
_ => Enumerable.Empty<(string, JsonElement)>()
}, cancellationToken);
}
#endregion
#region Helper Methods
private static JsonElement? SerializeEntity<T>(T? entity) where T : class
{
if (entity == null) return null;
return JsonSerializer.SerializeToElement(entity);
}
#endregion
}

View File

@@ -0,0 +1,41 @@
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
namespace ZB.MOM.WW.CBDDC.Sample.Console;
public class TodoList
{
/// <summary>
/// Gets or sets the document identifier.
/// </summary>
[Key]
public string Id { get; set; } = Guid.NewGuid().ToString();
/// <summary>
/// Gets or sets the list name.
/// </summary>
public string Name { get; set; } = string.Empty;
/// <summary>
/// Gets or sets the todo items in the list.
/// </summary>
public List<TodoItem> Items { get; set; } = new();
}
public class TodoItem
{
/// <summary>
/// Gets or sets the task description.
/// </summary>
public string Task { get; set; } = string.Empty;
/// <summary>
/// Gets or sets a value indicating whether the task is completed.
/// </summary>
public bool Completed { get; set; }
/// <summary>
/// Gets or sets the UTC creation timestamp.
/// </summary>
public DateTime CreatedAt { get; set; } = DateTime.UtcNow;
}

View File

@@ -0,0 +1,35 @@
using System.ComponentModel.DataAnnotations;
namespace ZB.MOM.WW.CBDDC.Sample.Console;
public class User
{
/// <summary>
/// Gets or sets the unique user identifier.
/// </summary>
[Key]
public string Id { get; set; } = "";
/// <summary>
/// Gets or sets the user name.
/// </summary>
public string? Name { get; set; }
/// <summary>
/// Gets or sets the user age.
/// </summary>
public int Age { get; set; }
/// <summary>
/// Gets or sets the user address.
/// </summary>
public Address? Address { get; set; }
}
public class Address
{
/// <summary>
/// Gets or sets the city value.
/// </summary>
public string? City { get; set; }
}

View File

@@ -0,0 +1,41 @@
<Project Sdk="Microsoft.NET.Sdk">
<ItemGroup>
<PackageReference Include="Lifter.Core" Version="1.1.0" />
<PackageReference Include="BLite.SourceGenerators" Version="1.3.1">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
<ProjectReference Include="..\..\src\ZB.MOM.WW.CBDDC.Core\ZB.MOM.WW.CBDDC.Core.csproj" />
<ProjectReference Include="..\..\src\ZB.MOM.WW.CBDDC.Network\ZB.MOM.WW.CBDDC.Network.csproj" />
<ProjectReference Include="..\..\src\ZB.MOM.WW.CBDDC.Persistence\ZB.MOM.WW.CBDDC.Persistence.csproj" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="Microsoft.Extensions.Configuration.Binder" Version="9.0.0" />
<PackageReference Include="Microsoft.Extensions.Configuration.Json" Version="9.0.0" />
<PackageReference Include="Microsoft.Extensions.DependencyInjection" Version="9.0.0" />
<PackageReference Include="Microsoft.Extensions.Hosting" Version="9.0.0" />
<PackageReference Include="Serilog" Version="4.2.0" />
<PackageReference Include="Serilog.Extensions.Hosting" Version="9.0.0" />
<PackageReference Include="Serilog.Sinks.Console" Version="6.0.0" />
</ItemGroup>
<ItemGroup>
<None Update="appsettings.json">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
</ItemGroup>
<PropertyGroup>
<AssemblyName>ZB.MOM.WW.CBDDC.Sample.Console</AssemblyName>
<RootNamespace>ZB.MOM.WW.CBDDC.Sample.Console</RootNamespace>
<PackageId>ZB.MOM.WW.CBDDC.Sample.Console</PackageId>
<OutputType>Exe</OutputType>
<TargetFramework>net10.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
<IsPackable>false</IsPackable>
</PropertyGroup>
</Project>

View File

@@ -0,0 +1,51 @@
{
"Logging": {
"LogLevel": {
"Default": "Information",
"Microsoft": "Warning",
"System": "Warning",
"CBDDC": "Information",
"ZB.MOM.WW.CBDDC.Network.SyncOrchestrator": "Information",
"ZB.MOM.WW.CBDDC.Core.Storage.OplogCoordinator": "Warning",
"ZB.MOM.WW.CBDDC.Persistence": "Warning"
}
},
"CBDDC": {
"Network": {
"TcpPort": 5001,
"UdpPort": 6000,
"AuthToken": "demo-secret-key",
"ConnectionTimeoutMs": 5000,
"RetryAttempts": 3,
"RetryDelayMs": 1000,
"LocalhostOnly": false
},
"Persistence": {
"DatabasePath": "data/cbddc.db",
"EnableWalMode": true,
"CacheSizeMb": 50,
"EnableAutoBackup": true,
"BackupPath": "backups/",
"BusyTimeoutMs": 5000
},
"Sync": {
"SyncIntervalMs": 5000,
"BatchSize": 100,
"EnableOfflineQueue": true,
"MaxQueueSize": 1000
},
"Logging": {
"LogLevel": "Information",
"LogFilePath": "logs/cbddc.log",
"MaxLogFileSizeMb": 10,
"MaxLogFiles": 5
},
"KnownPeers": [
{
"NodeId": "AspNetSampleNode",
"Host": "localhost",
"Port": 6001
}
]
}
}

29
samples/run-cluster.bat Executable file
View File

@@ -0,0 +1,29 @@
@echo off
setlocal
echo ==========================================
echo CBDDC Cross-Platform Cluster
echo ==========================================
echo.
echo [1/2] Building projects...
dotnet build CBDDC.Sample.Console/ZB.MOM.WW.CBDDC.Sample.Console.csproj --nologo -v q
if %ERRORLEVEL% neq 0 (
echo Build failed!
pause
exit /b 1
)
echo.
echo [2/2] Starting Console Node (node-console-1)...
start "CBDDC Console Node 1" dotnet run --project CBDDC.Sample.Console/ZB.MOM.WW.CBDDC.Sample.Console.csproj --no-build -- node-console-1 5001
echo.
echo ==========================================
echo Cluster launched!
echo - Console Node 1 (Port 5001)
echo.
echo Use the Console window to 'l'ist peers.
echo ==========================================
pause

View File

@@ -0,0 +1,179 @@
using System;
using System.Collections.Generic;
using ZB.MOM.WW.CBDDC.Core;
using ZB.MOM.WW.CBDDC.Core.Network;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Logging.Abstractions;
using System.Threading.Tasks;
namespace ZB.MOM.WW.CBDDC.Core.Cache;
/// <summary>
/// LRU cache entry with linked list node.
/// </summary>
internal class CacheEntry
{
/// <summary>
/// Gets the cached document.
/// </summary>
public Document Document { get; }
/// <summary>
/// Gets the linked-list node used for LRU tracking.
/// </summary>
public LinkedListNode<string> Node { get; }
/// <summary>
/// Initializes a new instance of the <see cref="CacheEntry"/> class.
/// </summary>
/// <param name="document">The cached document.</param>
/// <param name="node">The linked-list node used for LRU tracking.</param>
public CacheEntry(Document document, LinkedListNode<string> node)
{
Document = document;
Node = node;
}
}
/// <summary>
/// In-memory LRU cache for documents.
/// </summary>
public class DocumentCache : IDocumentCache
{
private readonly IPeerNodeConfigurationProvider _peerNodeConfigurationProvider;
private readonly Dictionary<string, CacheEntry> _cache = new();
private readonly LinkedList<string> _lru = new();
private readonly ILogger<DocumentCache> _logger;
private readonly object _lock = new();
// Statistics
private long _hits = 0;
private long _misses = 0;
/// <summary>
/// Initializes a new instance of the <see cref="DocumentCache"/> class.
/// </summary>
/// <param name="peerNodeConfigurationProvider">The configuration provider used for cache size limits.</param>
/// <param name="logger">The logger instance.</param>
public DocumentCache(IPeerNodeConfigurationProvider peerNodeConfigurationProvider, ILogger<DocumentCache>? logger = null)
{
_peerNodeConfigurationProvider = peerNodeConfigurationProvider;
_logger = logger ?? NullLogger<DocumentCache>.Instance;
}
/// <summary>
/// Gets a document from cache.
/// </summary>
/// <param name="collection">The document collection name.</param>
/// <param name="key">The document key.</param>
/// <returns>A task whose result is the cached document, or <see langword="null"/> if not found.</returns>
public async Task<Document?> Get(string collection, string key)
{
lock (_lock)
{
var cacheKey = $"{collection}:{key}";
if (_cache.TryGetValue(cacheKey, out var entry))
{
// Move to front (most recently used)
_lru.Remove(entry.Node);
_lru.AddFirst(entry.Node);
_hits++;
_logger.LogTrace("Cache hit for {Key}", cacheKey);
return entry.Document;
}
_misses++;
_logger.LogTrace("Cache miss for {Key}", cacheKey);
return null;
}
}
/// <summary>
/// Sets a document in cache.
/// </summary>
/// <param name="collection">The document collection name.</param>
/// <param name="key">The document key.</param>
/// <param name="document">The document to cache.</param>
/// <returns>A task that represents the asynchronous operation.</returns>
public async Task Set(string collection, string key, Document document)
{
var peerConfig = await _peerNodeConfigurationProvider.GetConfiguration();
lock (_lock)
{
var cacheKey = $"{collection}:{key}";
// If already exists, update and move to front
if (_cache.TryGetValue(cacheKey, out var existingEntry))
{
_lru.Remove(existingEntry.Node);
var newNode = _lru.AddFirst(cacheKey);
_cache[cacheKey] = new CacheEntry(document, newNode);
_logger.LogTrace("Updated cache for {Key}", cacheKey);
return;
}
// Evict if full
if (_cache.Count >= peerConfig.MaxDocumentCacheSize)
{
var oldest = _lru.Last!.Value;
_lru.RemoveLast();
_cache.Remove(oldest);
_logger.LogTrace("Evicted oldest cache entry {Key}", oldest);
}
var node = _lru.AddFirst(cacheKey);
_cache[cacheKey] = new CacheEntry(document, node);
_logger.LogTrace("Added to cache: {Key}", cacheKey);
}
}
/// <summary>
/// Removes a document from cache.
/// </summary>
/// <param name="collection">The document collection name.</param>
/// <param name="key">The document key.</param>
public void Remove(string collection, string key)
{
lock (_lock)
{
var cacheKey = $"{collection}:{key}";
if (_cache.TryGetValue(cacheKey, out var entry))
{
_lru.Remove(entry.Node);
_cache.Remove(cacheKey);
_logger.LogTrace("Removed from cache: {Key}", cacheKey);
}
}
}
/// <summary>
/// Clears all cached documents.
/// </summary>
public void Clear()
{
lock (_lock)
{
var count = _cache.Count;
_cache.Clear();
_lru.Clear();
_logger.LogInformation("Cleared cache ({Count} entries)", count);
}
}
/// <summary>
/// Gets cache statistics.
/// </summary>
public (long Hits, long Misses, int Size, double HitRate) GetStatistics()
{
lock (_lock)
{
var total = _hits + _misses;
var hitRate = total > 0 ? (double)_hits / total : 0;
return (_hits, _misses, _cache.Count, hitRate);
}
}
}

View File

@@ -0,0 +1,45 @@
using System.Threading.Tasks;
namespace ZB.MOM.WW.CBDDC.Core.Cache
{
/// <summary>
/// Defines operations for caching documents by collection and key.
/// </summary>
public interface IDocumentCache
{
/// <summary>
/// Clears all cached documents.
/// </summary>
void Clear();
/// <summary>
/// Gets a cached document by collection and key.
/// </summary>
/// <param name="collection">The collection name.</param>
/// <param name="key">The document key.</param>
/// <returns>The cached document, or <see langword="null"/> if not found.</returns>
Task<Document?> Get(string collection, string key);
/// <summary>
/// Gets cache hit/miss statistics.
/// </summary>
/// <returns>A tuple containing hits, misses, current size, and hit rate.</returns>
(long Hits, long Misses, int Size, double HitRate) GetStatistics();
/// <summary>
/// Removes a cached document by collection and key.
/// </summary>
/// <param name="collection">The collection name.</param>
/// <param name="key">The document key.</param>
void Remove(string collection, string key);
/// <summary>
/// Adds or updates a cached document.
/// </summary>
/// <param name="collection">The collection name.</param>
/// <param name="key">The document key.</param>
/// <param name="document">The document to cache.</param>
/// <returns>A task that represents the asynchronous operation.</returns>
Task Set(string collection, string key, Document document);
}
}

View File

@@ -0,0 +1,24 @@
using System;
using System.Collections.Generic;
namespace ZB.MOM.WW.CBDDC.Core;
/// <summary>
/// Event arguments for when changes are applied to the peer store.
/// </summary>
public class ChangesAppliedEventArgs : EventArgs
{
/// <summary>
/// Gets the changes that were applied.
/// </summary>
public IEnumerable<OplogEntry> Changes { get; }
/// <summary>
/// Initializes a new instance of the <see cref="ChangesAppliedEventArgs"/> class.
/// </summary>
/// <param name="changes">The changes that were applied.</param>
public ChangesAppliedEventArgs(IEnumerable<OplogEntry> changes)
{
Changes = changes;
}
}

View File

@@ -0,0 +1,82 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Logging.Abstractions;
using ZB.MOM.WW.CBDDC.Core.Storage;
namespace ZB.MOM.WW.CBDDC.Core.Diagnostics;
/// <summary>
/// Provides health check functionality.
/// </summary>
public class CBDDCHealthCheck : ICBDDCHealthCheck
{
private readonly IOplogStore _store;
private readonly ISyncStatusTracker _syncTracker;
private readonly ILogger<CBDDCHealthCheck> _logger;
/// <summary>
/// Initializes a new instance of the <see cref="CBDDCHealthCheck"/> class.
/// </summary>
/// <param name="store">The oplog store used for database health checks.</param>
/// <param name="syncTracker">The tracker that provides synchronization status.</param>
/// <param name="logger">The logger instance.</param>
public CBDDCHealthCheck(
IOplogStore store,
ISyncStatusTracker syncTracker,
ILogger<CBDDCHealthCheck>? logger = null)
{
_store = store ?? throw new ArgumentNullException(nameof(store));
_syncTracker = syncTracker ?? throw new ArgumentNullException(nameof(syncTracker));
_logger = logger ?? NullLogger<CBDDCHealthCheck>.Instance;
}
/// <summary>
/// Performs a comprehensive health check.
/// </summary>
/// <param name="cancellationToken">A token used to cancel the health check.</param>
public async Task<HealthStatus> CheckAsync(CancellationToken cancellationToken = default)
{
var status = new HealthStatus();
// Check database health
try
{
// Try to get latest timestamp (simple database operation)
var timestamp = await _store.GetLatestTimestampAsync(cancellationToken);
status.DatabaseHealthy = true;
_logger.LogDebug("Database health check passed (latest timestamp: {Timestamp})", timestamp);
}
catch (Exception ex)
{
status.DatabaseHealthy = false;
status.Errors.Add($"Database check failed: {ex.Message}");
_logger.LogError(ex, "Database health check failed");
}
// Get sync status
var syncStatus = _syncTracker.GetStatus();
status.NetworkHealthy = syncStatus.IsOnline;
status.ConnectedPeers = syncStatus.ActivePeers.Count(p => p.IsConnected);
status.LastSyncTime = syncStatus.LastSyncTime;
// Add error messages from sync tracker
foreach (var error in syncStatus.SyncErrors.Take(5)) // Last 5 errors
{
status.Errors.Add($"{error.Timestamp:yyyy-MM-dd HH:mm:ss} - {error.Message}");
}
// Add metadata
status.Metadata["TotalDocumentsSynced"] = syncStatus.TotalDocumentsSynced;
status.Metadata["TotalBytesTransferred"] = syncStatus.TotalBytesTransferred;
status.Metadata["ActivePeers"] = syncStatus.ActivePeers.Count;
_logger.LogInformation("Health check completed: Database={DbHealth}, Network={NetHealth}, Peers={Peers}",
status.DatabaseHealthy, status.NetworkHealthy, status.ConnectedPeers);
return status;
}
}

View File

@@ -0,0 +1,148 @@
using System;
using System.Collections.Generic;
namespace ZB.MOM.WW.CBDDC.Core.Diagnostics;
/// <summary>
/// Represents the health status of an CBDDC instance.
/// </summary>
public class HealthStatus
{
/// <summary>
/// Indicates if the database is healthy.
/// </summary>
public bool DatabaseHealthy { get; set; }
/// <summary>
/// Indicates if network connectivity is available.
/// </summary>
public bool NetworkHealthy { get; set; }
/// <summary>
/// Number of currently connected peers.
/// </summary>
public int ConnectedPeers { get; set; }
/// <summary>
/// Timestamp of the last successful sync operation.
/// </summary>
public DateTime? LastSyncTime { get; set; }
/// <summary>
/// List of recent errors.
/// </summary>
public List<string> Errors { get; set; } = new();
/// <summary>
/// Overall health status.
/// </summary>
public bool IsHealthy => DatabaseHealthy && NetworkHealthy && Errors.Count == 0;
/// <summary>
/// Additional diagnostic information.
/// </summary>
public Dictionary<string, object> Metadata { get; set; } = new();
}
/// <summary>
/// Represents the synchronization status.
/// </summary>
public class SyncStatus
{
/// <summary>
/// Indicates if the node is currently online.
/// </summary>
public bool IsOnline { get; set; }
/// <summary>
/// Timestamp of the last sync operation.
/// </summary>
public DateTime? LastSyncTime { get; set; }
/// <summary>
/// Number of pending operations in the offline queue.
/// </summary>
public int PendingOperations { get; set; }
/// <summary>
/// List of active peer nodes.
/// </summary>
public List<PeerInfo> ActivePeers { get; set; } = new();
/// <summary>
/// Recent sync errors.
/// </summary>
public List<SyncError> SyncErrors { get; set; } = new();
/// <summary>
/// Total number of documents synced.
/// </summary>
public long TotalDocumentsSynced { get; set; }
/// <summary>
/// Total bytes transferred.
/// </summary>
public long TotalBytesTransferred { get; set; }
}
/// <summary>
/// Information about a peer node.
/// </summary>
public class PeerInfo
{
/// <summary>
/// Unique identifier of the peer.
/// </summary>
public string NodeId { get; set; } = "";
/// <summary>
/// Network address of the peer.
/// </summary>
public string Address { get; set; } = "";
/// <summary>
/// Last time the peer was seen.
/// </summary>
public DateTime LastSeen { get; set; }
/// <summary>
/// Indicates if the peer is currently connected.
/// </summary>
public bool IsConnected { get; set; }
/// <summary>
/// Number of successful syncs with this peer.
/// </summary>
public int SuccessfulSyncs { get; set; }
/// <summary>
/// Number of failed syncs with this peer.
/// </summary>
public int FailedSyncs { get; set; }
}
/// <summary>
/// Represents a synchronization error.
/// </summary>
public class SyncError
{
/// <summary>
/// Timestamp when the error occurred.
/// </summary>
public DateTime Timestamp { get; set; }
/// <summary>
/// Error message.
/// </summary>
public string Message { get; set; } = "";
/// <summary>
/// Peer node ID if applicable.
/// </summary>
public string? PeerNodeId { get; set; }
/// <summary>
/// Error code.
/// </summary>
public string? ErrorCode { get; set; }
}

View File

@@ -0,0 +1,15 @@
using System.Threading;
using System.Threading.Tasks;
namespace ZB.MOM.WW.CBDDC.Core.Diagnostics
{
public interface ICBDDCHealthCheck
{
/// <summary>
/// Performs a health check for the implementing component.
/// </summary>
/// <param name="cancellationToken">Cancellation token.</param>
/// <returns>The resulting health status.</returns>
Task<HealthStatus> CheckAsync(CancellationToken cancellationToken = default);
}
}

View File

@@ -0,0 +1,63 @@
using System;
namespace ZB.MOM.WW.CBDDC.Core.Diagnostics
{
/// <summary>
/// Tracks synchronization status and peer health metrics.
/// </summary>
public interface ISyncStatusTracker
{
/// <summary>
/// Removes peer entries that have been inactive longer than the specified threshold.
/// </summary>
/// <param name="inactiveThreshold">The inactivity threshold used to prune peers.</param>
void CleanupInactivePeers(TimeSpan inactiveThreshold);
/// <summary>
/// Gets the current synchronization status snapshot.
/// </summary>
/// <returns>The current <see cref="SyncStatus"/>.</returns>
SyncStatus GetStatus();
/// <summary>
/// Records an error encountered during synchronization.
/// </summary>
/// <param name="message">The error message.</param>
/// <param name="peerNodeId">The related peer node identifier, if available.</param>
/// <param name="errorCode">An optional error code.</param>
void RecordError(string message, string? peerNodeId = null, string? errorCode = null);
/// <summary>
/// Records a failed operation for the specified peer.
/// </summary>
/// <param name="nodeId">The peer node identifier.</param>
void RecordPeerFailure(string nodeId);
/// <summary>
/// Records a successful operation for the specified peer.
/// </summary>
/// <param name="nodeId">The peer node identifier.</param>
void RecordPeerSuccess(string nodeId);
/// <summary>
/// Records synchronization throughput metrics.
/// </summary>
/// <param name="documentCount">The number of synchronized documents.</param>
/// <param name="bytesTransferred">The number of bytes transferred.</param>
void RecordSync(int documentCount, long bytesTransferred);
/// <summary>
/// Sets whether the local node is currently online.
/// </summary>
/// <param name="isOnline">A value indicating whether the node is online.</param>
void SetOnlineStatus(bool isOnline);
/// <summary>
/// Updates peer connectivity details.
/// </summary>
/// <param name="nodeId">The peer node identifier.</param>
/// <param name="address">The peer network address.</param>
/// <param name="isConnected">A value indicating whether the peer is connected.</param>
void UpdatePeer(string nodeId, string address, bool isConnected);
}
}

View File

@@ -0,0 +1,198 @@
using System;
using System.Collections.Generic;
using System.Linq;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Logging.Abstractions;
namespace ZB.MOM.WW.CBDDC.Core.Diagnostics;
/// <summary>
/// Tracks synchronization status and provides diagnostics.
/// </summary>
public class SyncStatusTracker : ISyncStatusTracker
{
private readonly ILogger<SyncStatusTracker> _logger;
private readonly object _lock = new();
private bool _isOnline = false;
private DateTime? _lastSyncTime;
private readonly List<PeerInfo> _activePeers = new();
private readonly Queue<SyncError> _recentErrors = new();
private long _totalDocumentsSynced = 0;
private long _totalBytesTransferred = 0;
private const int MaxErrorHistory = 50;
/// <summary>
/// Initializes a new instance of the <see cref="SyncStatusTracker"/> class.
/// </summary>
/// <param name="logger">Optional logger instance.</param>
public SyncStatusTracker(ILogger<SyncStatusTracker>? logger = null)
{
_logger = logger ?? NullLogger<SyncStatusTracker>.Instance;
}
/// <summary>
/// Updates online status.
/// </summary>
/// <param name="isOnline">Whether the node is currently online.</param>
public void SetOnlineStatus(bool isOnline)
{
lock (_lock)
{
if (_isOnline != isOnline)
{
_isOnline = isOnline;
_logger.LogInformation("Status changed to {Status}", isOnline ? "Online" : "Offline");
}
}
}
/// <summary>
/// Records a successful sync operation.
/// </summary>
/// <param name="documentCount">The number of documents synchronized.</param>
/// <param name="bytesTransferred">The number of bytes transferred.</param>
public void RecordSync(int documentCount, long bytesTransferred)
{
lock (_lock)
{
_lastSyncTime = DateTime.UtcNow;
_totalDocumentsSynced += documentCount;
_totalBytesTransferred += bytesTransferred;
_logger.LogDebug("Synced {Count} documents ({Bytes} bytes)", documentCount, bytesTransferred);
}
}
/// <summary>
/// Records a sync error.
/// </summary>
/// <param name="message">The error message.</param>
/// <param name="peerNodeId">The related peer node identifier, if available.</param>
/// <param name="errorCode">The error code, if available.</param>
public void RecordError(string message, string? peerNodeId = null, string? errorCode = null)
{
lock (_lock)
{
var error = new SyncError
{
Timestamp = DateTime.UtcNow,
Message = message,
PeerNodeId = peerNodeId,
ErrorCode = errorCode
};
_recentErrors.Enqueue(error);
while (_recentErrors.Count > MaxErrorHistory)
{
_recentErrors.Dequeue();
}
_logger.LogWarning("Sync error recorded: {Message} (Peer: {Peer})", message, peerNodeId ?? "N/A");
}
}
/// <summary>
/// Updates peer information.
/// </summary>
/// <param name="nodeId">The peer node identifier.</param>
/// <param name="address">The peer address.</param>
/// <param name="isConnected">Whether the peer is currently connected.</param>
public void UpdatePeer(string nodeId, string address, bool isConnected)
{
lock (_lock)
{
var peer = _activePeers.FirstOrDefault(p => p.NodeId == nodeId);
if (peer == null)
{
peer = new PeerInfo
{
NodeId = nodeId,
Address = address,
IsConnected = isConnected,
LastSeen = DateTime.UtcNow
};
_activePeers.Add(peer);
_logger.LogInformation("New peer discovered: {NodeId} at {Address}", nodeId, address);
}
else
{
peer.Address = address;
peer.IsConnected = isConnected;
peer.LastSeen = DateTime.UtcNow;
}
}
}
/// <summary>
/// Records successful sync with a peer.
/// </summary>
/// <param name="nodeId">The peer node identifier.</param>
public void RecordPeerSuccess(string nodeId)
{
lock (_lock)
{
var peer = _activePeers.FirstOrDefault(p => p.NodeId == nodeId);
if (peer != null)
{
peer.SuccessfulSyncs++;
}
}
}
/// <summary>
/// Records failed sync with a peer.
/// </summary>
/// <param name="nodeId">The peer node identifier.</param>
public void RecordPeerFailure(string nodeId)
{
lock (_lock)
{
var peer = _activePeers.FirstOrDefault(p => p.NodeId == nodeId);
if (peer != null)
{
peer.FailedSyncs++;
}
}
}
/// <summary>
/// Gets current sync status.
/// </summary>
public SyncStatus GetStatus()
{
lock (_lock)
{
return new SyncStatus
{
IsOnline = _isOnline,
LastSyncTime = _lastSyncTime,
PendingOperations = 0, // Will be set by caller if offline queue is available
ActivePeers = _activePeers.ToList(),
SyncErrors = _recentErrors.ToList(),
TotalDocumentsSynced = _totalDocumentsSynced,
TotalBytesTransferred = _totalBytesTransferred
};
}
}
/// <summary>
/// Cleans up inactive peers.
/// </summary>
/// <param name="inactiveThreshold">The inactivity threshold used to remove peers.</param>
public void CleanupInactivePeers(TimeSpan inactiveThreshold)
{
lock (_lock)
{
var cutoff = DateTime.UtcNow - inactiveThreshold;
var removed = _activePeers.RemoveAll(p => p.LastSeen < cutoff);
if (removed > 0)
{
_logger.LogInformation("Removed {Count} inactive peers", removed);
}
}
}
}

View File

@@ -0,0 +1,83 @@
using ZB.MOM.WW.CBDDC.Core.Sync;
using System;
using System.Text.Json;
namespace ZB.MOM.WW.CBDDC.Core;
/// <summary>
/// Represents a stored document and its synchronization metadata.
/// </summary>
public class Document
{
/// <summary>
/// Gets the collection that contains the document.
/// </summary>
public string Collection { get; private set; }
/// <summary>
/// Gets the document key.
/// </summary>
public string Key { get; private set; }
/// <summary>
/// Gets the document content.
/// </summary>
public JsonElement Content { get; private set; }
/// <summary>
/// Gets the timestamp of the latest applied update.
/// </summary>
public HlcTimestamp UpdatedAt { get; private set; }
/// <summary>
/// Gets a value indicating whether the document is deleted.
/// </summary>
public bool IsDeleted { get; private set; }
/// <summary>
/// Initializes a new instance of the <see cref="Document"/> class.
/// </summary>
/// <param name="collection">The collection that contains the document.</param>
/// <param name="key">The document key.</param>
/// <param name="content">The document content.</param>
/// <param name="updatedAt">The timestamp of the latest applied update.</param>
/// <param name="isDeleted">Whether the document is marked as deleted.</param>
public Document(string collection, string key, JsonElement content, HlcTimestamp updatedAt, bool isDeleted)
{
Collection = collection;
Key = key;
Content = content;
UpdatedAt = updatedAt;
IsDeleted = isDeleted;
}
/// <summary>
/// Merges a remote operation into the current document using last-write-wins or a conflict resolver.
/// </summary>
/// <param name="oplogEntry">The remote operation to merge.</param>
/// <param name="resolver">An optional conflict resolver for custom merge behavior.</param>
public void Merge(OplogEntry oplogEntry, IConflictResolver? resolver = null)
{
if (oplogEntry == null) return;
if (Collection != oplogEntry.Collection) return;
if (Key != oplogEntry.Key) return;
if (resolver == null)
{
//last wins
if (UpdatedAt <= oplogEntry.Timestamp)
{
Content = oplogEntry.Payload ?? default;
UpdatedAt = oplogEntry.Timestamp;
IsDeleted = oplogEntry.Operation == OperationType.Delete;
}
return;
}
var resolutionResult = resolver.Resolve(this, oplogEntry);
if (resolutionResult.ShouldApply && resolutionResult.MergedDocument != null)
{
Content = resolutionResult.MergedDocument.Content;
UpdatedAt = resolutionResult.MergedDocument.UpdatedAt;
IsDeleted = resolutionResult.MergedDocument.IsDeleted;
}
}
}

View File

@@ -0,0 +1,189 @@
using System;
namespace ZB.MOM.WW.CBDDC.Core.Exceptions;
/// <summary>
/// Base exception for all CBDDC-related errors.
/// </summary>
public class CBDDCException : Exception
{
/// <summary>
/// Error code for programmatic error handling.
/// </summary>
public string ErrorCode { get; }
/// <summary>
/// Initializes a new instance of the <see cref="CBDDCException"/> class.
/// </summary>
/// <param name="errorCode">The application-specific error code.</param>
/// <param name="message">The exception message.</param>
public CBDDCException(string errorCode, string message)
: base(message)
{
ErrorCode = errorCode;
}
/// <summary>
/// Initializes a new instance of the <see cref="CBDDCException"/> class.
/// </summary>
/// <param name="errorCode">The application-specific error code.</param>
/// <param name="message">The exception message.</param>
/// <param name="innerException">The exception that caused the current exception.</param>
public CBDDCException(string errorCode, string message, Exception innerException)
: base(message, innerException)
{
ErrorCode = errorCode;
}
}
/// <summary>
/// Exception thrown when network operations fail.
/// </summary>
public class NetworkException : CBDDCException
{
/// <summary>
/// Initializes a new instance of the <see cref="NetworkException"/> class.
/// </summary>
/// <param name="message">The exception message.</param>
public NetworkException(string message)
: base("NETWORK_ERROR", message) { }
/// <summary>
/// Initializes a new instance of the <see cref="NetworkException"/> class.
/// </summary>
/// <param name="message">The exception message.</param>
/// <param name="innerException">The exception that caused the current exception.</param>
public NetworkException(string message, Exception innerException)
: base("NETWORK_ERROR", message, innerException) { }
}
/// <summary>
/// Exception thrown when persistence operations fail.
/// </summary>
public class PersistenceException : CBDDCException
{
/// <summary>
/// Initializes a new instance of the <see cref="PersistenceException"/> class.
/// </summary>
/// <param name="message">The exception message.</param>
public PersistenceException(string message)
: base("PERSISTENCE_ERROR", message) { }
/// <summary>
/// Initializes a new instance of the <see cref="PersistenceException"/> class.
/// </summary>
/// <param name="message">The exception message.</param>
/// <param name="innerException">The exception that caused the current exception.</param>
public PersistenceException(string message, Exception innerException)
: base("PERSISTENCE_ERROR", message, innerException) { }
}
/// <summary>
/// Exception thrown when synchronization operations fail.
/// </summary>
public class SyncException : CBDDCException
{
/// <summary>
/// Initializes a new instance of the <see cref="SyncException"/> class.
/// </summary>
/// <param name="message">The exception message.</param>
public SyncException(string message)
: base("SYNC_ERROR", message) { }
/// <summary>
/// Initializes a new instance of the <see cref="SyncException"/> class.
/// </summary>
/// <param name="message">The exception message.</param>
/// <param name="innerException">The exception that caused the current exception.</param>
public SyncException(string message, Exception innerException)
: base("SYNC_ERROR", message, innerException) { }
}
/// <summary>
/// Exception thrown when configuration is invalid.
/// </summary>
public class ConfigurationException : CBDDCException
{
/// <summary>
/// Initializes a new instance of the <see cref="ConfigurationException"/> class.
/// </summary>
/// <param name="message">The exception message.</param>
public ConfigurationException(string message)
: base("CONFIG_ERROR", message) { }
}
/// <summary>
/// Exception thrown when database corruption is detected.
/// </summary>
public class DatabaseCorruptionException : PersistenceException
{
/// <summary>
/// Initializes a new instance of the <see cref="DatabaseCorruptionException"/> class.
/// </summary>
/// <param name="message">The exception message.</param>
public DatabaseCorruptionException(string message)
: base(message) { }
/// <summary>
/// Initializes a new instance of the <see cref="DatabaseCorruptionException"/> class.
/// </summary>
/// <param name="message">The exception message.</param>
/// <param name="innerException">The exception that caused the current exception.</param>
public DatabaseCorruptionException(string message, Exception innerException)
: base(message, innerException) { }
}
/// <summary>
/// Exception thrown when a timeout occurs.
/// </summary>
public class TimeoutException : CBDDCException
{
/// <summary>
/// Initializes a new instance of the <see cref="TimeoutException"/> class.
/// </summary>
/// <param name="operation">The operation that timed out.</param>
/// <param name="timeoutMs">The timeout in milliseconds.</param>
public TimeoutException(string operation, int timeoutMs)
: base("TIMEOUT_ERROR", $"Operation '{operation}' timed out after {timeoutMs}ms") { }
}
/// <summary>
/// Exception thrown when a document is not found in a collection.
/// </summary>
public class DocumentNotFoundException : PersistenceException
{
/// <summary>
/// Gets the document key that was not found.
/// </summary>
public string Key { get; }
/// <summary>
/// Gets the collection where the document was searched.
/// </summary>
public string Collection { get; }
/// <summary>
/// Initializes a new instance of the <see cref="DocumentNotFoundException"/> class.
/// </summary>
/// <param name="collection">The collection where the document was searched.</param>
/// <param name="key">The document key that was not found.</param>
public DocumentNotFoundException(string collection, string key)
: base($"Document with key '{key}' not found in collection '{collection}'.")
{
Collection = collection;
Key = key;
}
}
/// <summary>
/// Exception thrown when a concurrency conflict occurs during persistence operations.
/// </summary>
public class CBDDCConcurrencyException : PersistenceException
{
/// <summary>
/// Initializes a new instance of the <see cref="CBDDCConcurrencyException"/> class.
/// </summary>
/// <param name="message">The exception message.</param>
public CBDDCConcurrencyException(string message) : base(message) { }
}

View File

@@ -0,0 +1,137 @@
using System;
namespace ZB.MOM.WW.CBDDC.Core;
/// <summary>
/// Represents a Hybrid Logical Clock timestamp.
/// Provides a Total Ordering of events in a distributed system.
/// Implements value semantics and comparable interfaces.
/// </summary>
public readonly struct HlcTimestamp : IComparable<HlcTimestamp>, IComparable, IEquatable<HlcTimestamp>
{
/// <summary>
/// Gets the physical time component of the timestamp.
/// </summary>
public long PhysicalTime { get; }
/// <summary>
/// Gets the logical counter component used to order events with equal physical time.
/// </summary>
public int LogicalCounter { get; }
/// <summary>
/// Gets the node identifier that produced this timestamp.
/// </summary>
public string NodeId { get; }
/// <summary>
/// Initializes a new instance of the <see cref="HlcTimestamp"/> struct.
/// </summary>
/// <param name="physicalTime">The physical time component.</param>
/// <param name="logicalCounter">The logical counter component.</param>
/// <param name="nodeId">The node identifier.</param>
public HlcTimestamp(long physicalTime, int logicalCounter, string nodeId)
{
PhysicalTime = physicalTime;
LogicalCounter = logicalCounter;
NodeId = nodeId ?? throw new ArgumentNullException(nameof(nodeId));
}
/// <summary>
/// Compares two timestamps to establish a total order.
/// Order: PhysicalTime -> LogicalCounter -> NodeId (lexicographical tie-breaker).
/// </summary>
/// <param name="other">The other timestamp to compare with this instance.</param>
/// <returns>
/// A value less than zero if this instance is earlier than <paramref name="other"/>, zero if they are equal,
/// or greater than zero if this instance is later than <paramref name="other"/>.
/// </returns>
public int CompareTo(HlcTimestamp other)
{
int timeComparison = PhysicalTime.CompareTo(other.PhysicalTime);
if (timeComparison != 0) return timeComparison;
int counterComparison = LogicalCounter.CompareTo(other.LogicalCounter);
if (counterComparison != 0) return counterComparison;
// Use Ordinal comparison for consistent tie-breaking across cultures/platforms
return string.Compare(NodeId, other.NodeId, StringComparison.Ordinal);
}
/// <summary>
/// Compares this instance with another object.
/// </summary>
/// <param name="obj">The object to compare with this instance.</param>
/// <returns>
/// A value less than zero if this instance is earlier than <paramref name="obj"/>, zero if equal, or greater
/// than zero if later.
/// </returns>
public int CompareTo(object? obj)
{
if (obj is null) return 1;
if (obj is HlcTimestamp other) return CompareTo(other);
throw new ArgumentException($"Object must be of type {nameof(HlcTimestamp)}");
}
/// <summary>
/// Determines whether this instance and another timestamp are equal.
/// </summary>
/// <param name="other">The other timestamp to compare.</param>
/// <returns><see langword="true"/> if the timestamps are equal; otherwise, <see langword="false"/>.</returns>
public bool Equals(HlcTimestamp other)
{
return PhysicalTime == other.PhysicalTime &&
LogicalCounter == other.LogicalCounter &&
string.Equals(NodeId, other.NodeId, StringComparison.Ordinal);
}
/// <inheritdoc />
public override bool Equals(object? obj)
{
return obj is HlcTimestamp other && Equals(other);
}
/// <inheritdoc />
public override int GetHashCode()
{
unchecked
{
var hashCode = PhysicalTime.GetHashCode();
hashCode = (hashCode * 397) ^ LogicalCounter;
// Ensure HashCode uses the same comparison logic as Equals/CompareTo
// Handle null NodeId gracefully (possible via default(HlcTimestamp))
hashCode = (hashCode * 397) ^ (NodeId != null ? StringComparer.Ordinal.GetHashCode(NodeId) : 0);
return hashCode;
}
}
public static bool operator ==(HlcTimestamp left, HlcTimestamp right) => left.Equals(right);
public static bool operator !=(HlcTimestamp left, HlcTimestamp right) => !left.Equals(right);
// Standard comparison operators making usage in SyncOrchestrator cleaner (e.g., remote > local)
public static bool operator <(HlcTimestamp left, HlcTimestamp right) => left.CompareTo(right) < 0;
public static bool operator <=(HlcTimestamp left, HlcTimestamp right) => left.CompareTo(right) <= 0;
public static bool operator >(HlcTimestamp left, HlcTimestamp right) => left.CompareTo(right) > 0;
public static bool operator >=(HlcTimestamp left, HlcTimestamp right) => left.CompareTo(right) >= 0;
/// <inheritdoc />
public override string ToString() => FormattableString.Invariant($"{PhysicalTime}:{LogicalCounter}:{NodeId}");
/// <summary>
/// Parses a timestamp string.
/// </summary>
/// <param name="s">The string to parse, in the format "PhysicalTime:LogicalCounter:NodeId".</param>
/// <returns>The parsed <see cref="HlcTimestamp"/>.</returns>
public static HlcTimestamp Parse(string s)
{
if (string.IsNullOrEmpty(s)) throw new ArgumentNullException(nameof(s));
var parts = s.Split(':');
if (parts.Length != 3) throw new FormatException("Invalid HlcTimestamp format. Expected 'PhysicalTime:LogicalCounter:NodeId'.");
if (!long.TryParse(parts[0], out var physicalTime))
throw new FormatException("Invalid PhysicalTime component in HlcTimestamp.");
if (!int.TryParse(parts[1], out var logicalCounter))
throw new FormatException("Invalid LogicalCounter component in HlcTimestamp.");
var nodeId = parts[2];
return new HlcTimestamp(physicalTime, logicalCounter, nodeId);
}
}

View File

@@ -0,0 +1,60 @@
using System.Collections.Generic;
using System.Threading;
using System.Threading.Tasks;
using ZB.MOM.WW.CBDDC.Core.Network;
namespace ZB.MOM.WW.CBDDC.Core.Management;
/// <summary>
/// Service for managing remote peer configurations.
/// Provides CRUD operations for adding, removing, enabling/disabling remote cloud nodes.
/// </summary>
public interface IPeerManagementService
{
/// <summary>
/// Adds a static remote peer with simple authentication.
/// </summary>
/// <param name="nodeId">Unique identifier for the remote peer.</param>
/// <param name="address">Network address (hostname:port) of the remote peer.</param>
/// <param name="cancellationToken">Cancellation token.</param>
Task AddStaticPeerAsync(string nodeId, string address, CancellationToken cancellationToken = default);
/// <summary>
/// Removes a remote peer configuration.
/// </summary>
/// <param name="nodeId">Unique identifier of the peer to remove.</param>
/// <param name="cancellationToken">Cancellation token.</param>
Task RemoveRemotePeerAsync(string nodeId, CancellationToken cancellationToken = default);
/// <summary>
/// Removes confirmation tracking for a peer and optionally removes static remote configuration.
/// </summary>
/// <param name="nodeId">Unique identifier of the peer to untrack.</param>
/// <param name="removeRemoteConfig">When true, also removes static remote peer configuration.</param>
/// <param name="cancellationToken">Cancellation token.</param>
Task RemovePeerTrackingAsync(
string nodeId,
bool removeRemoteConfig = true,
CancellationToken cancellationToken = default);
/// <summary>
/// Retrieves all configured remote peers.
/// </summary>
/// <param name="cancellationToken">Cancellation token.</param>
/// <returns>Collection of remote peer configurations.</returns>
Task<IEnumerable<RemotePeerConfiguration>> GetAllRemotePeersAsync(CancellationToken cancellationToken = default);
/// <summary>
/// Enables synchronization with a remote peer.
/// </summary>
/// <param name="nodeId">Unique identifier of the peer to enable.</param>
/// <param name="cancellationToken">Cancellation token.</param>
Task EnablePeerAsync(string nodeId, CancellationToken cancellationToken = default);
/// <summary>
/// Disables synchronization with a remote peer (keeps configuration).
/// </summary>
/// <param name="nodeId">Unique identifier of the peer to disable.</param>
/// <param name="cancellationToken">Cancellation token.</param>
Task DisablePeerAsync(string nodeId, CancellationToken cancellationToken = default);
}

View File

@@ -0,0 +1,185 @@
using System;
using System.Collections.Generic;
using System.Threading;
using System.Threading.Tasks;
using ZB.MOM.WW.CBDDC.Core.Network;
using ZB.MOM.WW.CBDDC.Core.Storage;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Logging.Abstractions;
namespace ZB.MOM.WW.CBDDC.Core.Management;
/// <summary>
/// Implementation of peer management service.
/// Provides CRUD operations for managing remote peer configurations.
///
/// Remote peer configurations are stored in a synchronized collection and automatically
/// replicated across all nodes in the cluster. Any change made on one node will be
/// synchronized to all other nodes through the normal CBDDC sync process.
/// </summary>
public class PeerManagementService : IPeerManagementService
{
private readonly IPeerConfigurationStore _store;
private readonly IPeerOplogConfirmationStore _peerOplogConfirmationStore;
private readonly ILogger<PeerManagementService> _logger;
/// <summary>
/// Initializes a new instance of the PeerManagementService class.
/// </summary>
/// <param name="store">Database instance for accessing the synchronized collection.</param>
/// <param name="peerOplogConfirmationStore">Peer confirmation tracking store.</param>
/// <param name="logger">Logger instance.</param>
public PeerManagementService(
IPeerConfigurationStore store,
IPeerOplogConfirmationStore peerOplogConfirmationStore,
ILogger<PeerManagementService>? logger = null)
{
_store = store ?? throw new ArgumentNullException(nameof(store));
_peerOplogConfirmationStore = peerOplogConfirmationStore ?? throw new ArgumentNullException(nameof(peerOplogConfirmationStore));
_logger = logger ?? NullLogger<PeerManagementService>.Instance;
}
/// <summary>
/// Adds or updates a static remote peer configuration.
/// </summary>
/// <param name="nodeId">The unique node identifier of the peer.</param>
/// <param name="address">The peer network address in host:port format.</param>
/// <param name="cancellationToken">A token used to cancel the operation.</param>
/// <returns>A task that represents the asynchronous operation.</returns>
public async Task AddStaticPeerAsync(string nodeId, string address, CancellationToken cancellationToken = default)
{
ValidateNodeId(nodeId);
ValidateAddress(address);
var config = new RemotePeerConfiguration
{
NodeId = nodeId,
Address = address,
Type = PeerType.StaticRemote,
IsEnabled = true
};
await _store.SaveRemotePeerAsync(config, cancellationToken);
_logger.LogInformation("Added static remote peer: {NodeId} at {Address} (will sync to all cluster nodes)", nodeId, address);
}
/// <summary>
/// Removes a remote peer configuration.
/// </summary>
/// <param name="nodeId">The unique node identifier of the peer to remove.</param>
/// <param name="cancellationToken">A token used to cancel the operation.</param>
/// <returns>A task that represents the asynchronous operation.</returns>
public async Task RemoveRemotePeerAsync(string nodeId, CancellationToken cancellationToken = default)
{
await RemovePeerTrackingAsync(nodeId, removeRemoteConfig: true, cancellationToken);
}
/// <summary>
/// Removes peer tracking and optionally removes remote peer configuration.
/// </summary>
/// <param name="nodeId">The unique node identifier of the peer to untrack.</param>
/// <param name="removeRemoteConfig">When true, also removes static remote peer configuration.</param>
/// <param name="cancellationToken">A token used to cancel the operation.</param>
/// <returns>A task that represents the asynchronous operation.</returns>
public async Task RemovePeerTrackingAsync(
string nodeId,
bool removeRemoteConfig = true,
CancellationToken cancellationToken = default)
{
ValidateNodeId(nodeId);
await _peerOplogConfirmationStore.RemovePeerTrackingAsync(nodeId, cancellationToken);
if (removeRemoteConfig)
{
await _store.RemoveRemotePeerAsync(nodeId, cancellationToken);
_logger.LogInformation("Removed remote peer and tracking: {NodeId} (will sync to all cluster nodes)", nodeId);
return;
}
_logger.LogInformation("Removed peer tracking only: {NodeId}", nodeId);
}
/// <summary>
/// Gets all configured remote peers.
/// </summary>
/// <param name="cancellationToken">A token used to cancel the operation.</param>
/// <returns>A task that represents the asynchronous operation. The task result contains remote peer configurations.</returns>
public async Task<IEnumerable<RemotePeerConfiguration>> GetAllRemotePeersAsync(CancellationToken cancellationToken = default)
{
return await _store.GetRemotePeersAsync(cancellationToken);
}
/// <summary>
/// Enables a configured remote peer.
/// </summary>
/// <param name="nodeId">The unique node identifier of the peer to enable.</param>
/// <param name="cancellationToken">A token used to cancel the operation.</param>
/// <returns>A task that represents the asynchronous operation.</returns>
public async Task EnablePeerAsync(string nodeId, CancellationToken cancellationToken = default)
{
ValidateNodeId(nodeId);
var peer = await _store.GetRemotePeerAsync(nodeId, cancellationToken);
if (peer == null)
{
return; // Peer not found, nothing to enable
}
if (!peer.IsEnabled)
{
peer.IsEnabled = true;
await _store.SaveRemotePeerAsync(peer, cancellationToken);
_logger.LogInformation("Enabled remote peer: {NodeId} (will sync to all cluster nodes)", nodeId);
}
}
/// <summary>
/// Disables a configured remote peer.
/// </summary>
/// <param name="nodeId">The unique node identifier of the peer to disable.</param>
/// <param name="cancellationToken">A token used to cancel the operation.</param>
/// <returns>A task that represents the asynchronous operation.</returns>
public async Task DisablePeerAsync(string nodeId, CancellationToken cancellationToken = default)
{
ValidateNodeId(nodeId);
var peer = await _store.GetRemotePeerAsync(nodeId, cancellationToken);
if (peer == null)
{
return; // Peer not found, nothing to disable
}
if (peer.IsEnabled)
{
peer.IsEnabled = false;
await _store.SaveRemotePeerAsync(peer, cancellationToken);
_logger.LogInformation("Disabled remote peer: {NodeId} (will sync to all cluster nodes)", nodeId);
}
}
private static void ValidateNodeId(string nodeId)
{
if (string.IsNullOrWhiteSpace(nodeId))
{
throw new ArgumentException("NodeId cannot be null or empty", nameof(nodeId));
}
}
private static void ValidateAddress(string address)
{
if (string.IsNullOrWhiteSpace(address))
{
throw new ArgumentException("Address cannot be null or empty", nameof(address));
}
// Basic format validation (should contain host:port)
if (!address.Contains(':'))
{
throw new ArgumentException("Address must be in format 'host:port'", nameof(address));
}
}
}

View File

@@ -0,0 +1,38 @@
using System;
using System.Threading.Tasks;
namespace ZB.MOM.WW.CBDDC.Core.Network;
/// <summary>
/// Represents a method that handles peer node configuration change notifications.
/// </summary>
/// <param name="sender">The source of the event.</param>
/// <param name="newConfig">The updated peer node configuration.</param>
public delegate void PeerNodeConfigurationChangedEventHandler(object? sender, PeerNodeConfiguration newConfig);
/// <summary>
/// Defines a contract for retrieving and monitoring configuration settings for a peer node.
/// </summary>
/// <remarks>Implementations of this interface provide access to the current configuration and notify subscribers
/// when configuration changes occur. This interface is typically used by components that require up-to-date
/// configuration information for peer-to-peer networking scenarios.</remarks>
public interface IPeerNodeConfigurationProvider
{
/// <summary>
/// Asynchronously retrieves the current configuration settings for the peer node.
/// </summary>
/// <returns>
/// A task that represents the asynchronous operation. The task result contains the current
/// <see cref="PeerNodeConfiguration"/>.
/// </returns>
public Task<PeerNodeConfiguration> GetConfiguration();
/// <summary>
/// Occurs when the configuration of the peer node changes.
/// </summary>
/// <remarks>Subscribe to this event to be notified when any configuration settings for the peer node are
/// modified. Event handlers can use this notification to update dependent components or respond to configuration
/// changes as needed.</remarks>
public event PeerNodeConfigurationChangedEventHandler? ConfigurationChanged;
}

View File

@@ -0,0 +1,20 @@
namespace ZB.MOM.WW.CBDDC.Core.Network;
/// <summary>
/// Defines the role of a node in the distributed network cluster.
/// </summary>
public enum NodeRole
{
/// <summary>
/// Standard member node that synchronizes only within the local area network.
/// Does not connect to cloud remote nodes.
/// </summary>
Member = 0,
/// <summary>
/// Leader node that acts as a gateway to cloud remote nodes.
/// Elected via the Bully algorithm (lexicographically smallest NodeId).
/// Responsible for synchronizing local cluster changes with cloud nodes.
/// </summary>
CloudGateway = 1
}

View File

@@ -0,0 +1,76 @@
using System;
using System.Collections.Generic;
using System.Linq;
namespace ZB.MOM.WW.CBDDC.Core.Network;
/// <summary>
/// Represents a peer node in a distributed network, including its unique identifier, network address, and last seen
/// timestamp.
/// </summary>
public class PeerNode
{
/// <summary>
/// Gets the unique identifier for the node.
/// </summary>
public string NodeId { get; }
/// <summary>
/// Gets the address associated with the current instance.
/// </summary>
public string Address { get; }
/// <summary>
/// Gets the date and time when the entity was last observed or updated.
/// </summary>
public DateTimeOffset LastSeen { get; }
/// <summary>
/// Gets the configuration settings for the peer node.
/// </summary>
public PeerNodeConfiguration? Configuration { get; }
/// <summary>
/// Gets the type of the peer node (LanDiscovered, StaticRemote, or CloudRemote).
/// </summary>
public PeerType Type { get; }
/// <summary>
/// Gets the role assigned to this node within the cluster.
/// </summary>
public NodeRole Role { get; }
/// <summary>
/// Gets the list of collections this peer is interested in.
/// </summary>
public System.Collections.Generic.IReadOnlyList<string> InterestingCollections { get; }
/// <summary>
/// Initializes a new instance of the PeerNode class with the specified node identifier, network address, and last
/// seen timestamp.
/// </summary>
/// <param name="nodeId">The unique identifier for the peer node. Cannot be null or empty.</param>
/// <param name="address">The network address of the peer node. Cannot be null or empty.</param>
/// <param name="lastSeen">The date and time when the peer node was last seen, expressed as a DateTimeOffset.</param>
/// <param name="type">The type of the peer node. Defaults to LanDiscovered.</param>
/// <param name="role">The role of the peer node. Defaults to Member.</param>
/// <param name="configuration">The peer node configuration</param>
/// <param name="interestingCollections">The list of collections this peer is interested in.</param>
public PeerNode(
string nodeId,
string address,
DateTimeOffset lastSeen,
PeerType type = PeerType.LanDiscovered,
NodeRole role = NodeRole.Member,
PeerNodeConfiguration? configuration = null,
IEnumerable<string>? interestingCollections = null)
{
NodeId = nodeId;
Address = address;
LastSeen = lastSeen;
Type = type;
Role = role;
Configuration = configuration;
InterestingCollections = new List<string>(interestingCollections ?? []).AsReadOnly();
}
}

View File

@@ -0,0 +1,96 @@
using System;
namespace ZB.MOM.WW.CBDDC.Core.Network;
/// <summary>
/// Represents the configuration settings for a peer node in a distributed network.
/// </summary>
/// <remarks>Use this class to specify identification, network port, and authentication details required for a
/// peer node to participate in a cluster or peer-to-peer environment. The <see cref="Default"/> property provides a
/// basic configuration suitable for development or testing scenarios.</remarks>
public class PeerNodeConfiguration
{
/// <summary>
/// Gets or sets the unique identifier for the node.
/// </summary>
public string NodeId { get; set; } = string.Empty;
/// <summary>
/// Gets or sets the TCP port number used for network communication.
/// </summary>
public int TcpPort { get; set; }
/// <summary>
/// Gets or sets the authentication token used to authorize API requests.
/// </summary>
public string AuthToken { get; set; } = string.Empty;
/// <summary>
/// Maximum size of the document cache items. Default: 10.
/// </summary>
public int MaxDocumentCacheSize { get; set; } = 100;
/// <summary>
/// Maximum size of offline queue. Default: 1000.
/// </summary>
public int MaxQueueSize { get; set; } = 1000;
/// <summary>
/// Number of retry attempts for failed network operations. Default: 3.
/// </summary>
public int RetryAttempts { get; set; } = 3;
/// <summary>
/// Delay between retry attempts in milliseconds. Default: 1000ms.
/// </summary>
public int RetryDelayMs { get; set; } = 1000;
/// <summary>
/// Interval between periodic maintenance operations (Oplog pruning) in minutes. Default: 60 minutes.
/// </summary>
public int MaintenanceIntervalMinutes { get; set; } = 60;
/// <summary>
/// Oplog retention period in hours. Entries older than this will be pruned. Default: 24 hours.
/// </summary>
public int OplogRetentionHours { get; set; } = 24;
/// <summary>
/// Gets or sets a list of known peers to connect to directly, bypassing discovery.
/// </summary>
public System.Collections.Generic.List<KnownPeerConfiguration> KnownPeers { get; set; } = new();
/// <summary>
/// Gets the default configuration settings for a peer node.
/// </summary>
/// <remarks>Each access returns a new instance of the configuration with a unique node identifier. The
/// default settings use TCP port 9000 and a generated authentication token. Modify the returned instance as needed
/// before use.</remarks>
public static PeerNodeConfiguration Default => new PeerNodeConfiguration
{
NodeId = Guid.NewGuid().ToString(),
TcpPort = 9000,
AuthToken = Guid.NewGuid().ToString("N")
};
}
/// <summary>
/// Configuration for a known peer node.
/// </summary>
public class KnownPeerConfiguration
{
/// <summary>
/// The unique identifier of the peer node.
/// </summary>
public string NodeId { get; set; } = string.Empty;
/// <summary>
/// The hostname or IP address of the peer.
/// </summary>
public string Host { get; set; } = string.Empty;
/// <summary>
/// The TCP port of the peer.
/// </summary>
public int Port { get; set; }
}

View File

@@ -0,0 +1,26 @@
namespace ZB.MOM.WW.CBDDC.Core.Network;
/// <summary>
/// Defines the type of peer node in the distributed network.
/// </summary>
public enum PeerType
{
/// <summary>
/// Peer discovered via UDP broadcast on the local area network.
/// These peers are ephemeral and removed after timeout when no longer broadcasting.
/// </summary>
LanDiscovered = 0,
/// <summary>
/// Peer manually configured with a static address.
/// These peers are persistent across restarts and stored in the database.
/// </summary>
StaticRemote = 1,
/// <summary>
/// Cloud remote node.
/// Always active if internet connectivity is available.
/// Synchronized only by the elected leader node to reduce overhead.
/// </summary>
CloudRemote = 2
}

View File

@@ -0,0 +1,38 @@
using System.ComponentModel.DataAnnotations;
namespace ZB.MOM.WW.CBDDC.Core.Network;
/// <summary>
/// Configuration for a remote peer node that is persistent across restarts.
/// This collection is automatically synchronized across all nodes in the cluster.
/// </summary>
public class RemotePeerConfiguration
{
/// <summary>
/// Gets or sets the unique identifier for the remote peer node.
/// </summary>
[Key]
public string NodeId { get; set; } = "";
/// <summary>
/// Gets or sets the network address of the remote peer (hostname:port).
/// </summary>
public string Address { get; set; } = "";
/// <summary>
/// Gets or sets the type of the peer (StaticRemote or CloudRemote).
/// </summary>
public PeerType Type { get; set; }
/// <summary>
/// Gets or sets whether this peer is enabled for synchronization.
/// Disabled peers are stored but not used for sync.
/// </summary>
public bool IsEnabled { get; set; } = true;
/// <summary>
/// Gets or sets the list of collections this peer is interested in.
/// If empty, the peer is interested in all collections.
/// </summary>
public System.Collections.Generic.List<string> InterestingCollections { get; set; } = new();
}

View File

@@ -0,0 +1,59 @@
using System.Threading.Tasks;
namespace ZB.MOM.WW.CBDDC.Core.Network;
/// <summary>
/// Provides peer node configuration from an in-memory static source.
/// </summary>
public class StaticPeerNodeConfigurationProvider : IPeerNodeConfigurationProvider
{
private PeerNodeConfiguration _configuration = new();
/// <summary>
/// Gets or sets the current peer node configuration.
/// </summary>
public PeerNodeConfiguration Configuration
{
get => _configuration;
set
{
if (_configuration != value)
{
_configuration = value;
OnConfigurationChanged(_configuration);
}
}
}
/// <summary>
/// Initializes a new instance of the <see cref="StaticPeerNodeConfigurationProvider"/> class.
/// </summary>
/// <param name="configuration">The initial peer node configuration.</param>
public StaticPeerNodeConfigurationProvider(PeerNodeConfiguration configuration)
{
Configuration = configuration;
}
/// <summary>
/// Occurs when the peer node configuration changes.
/// </summary>
public event PeerNodeConfigurationChangedEventHandler? ConfigurationChanged;
/// <summary>
/// Gets the current peer node configuration.
/// </summary>
/// <returns>A task whose result is the current configuration.</returns>
public Task<PeerNodeConfiguration> GetConfiguration()
{
return Task.FromResult(Configuration);
}
/// <summary>
/// Raises the <see cref="ConfigurationChanged"/> event.
/// </summary>
/// <param name="newConfig">The new peer node configuration.</param>
protected virtual void OnConfigurationChanged(PeerNodeConfiguration newConfig)
{
ConfigurationChanged?.Invoke(this, newConfig);
}
}

View File

@@ -0,0 +1,107 @@
using System;
using System.ComponentModel.DataAnnotations;
using System.Text.Json;
namespace ZB.MOM.WW.CBDDC.Core;
public enum OperationType
{
Put,
Delete
}
public static class OplogEntryExtensions
{
/// <summary>
/// Computes a deterministic hash for the specified oplog entry.
/// </summary>
/// <param name="entry">The oplog entry to hash.</param>
/// <returns>The lowercase hexadecimal SHA-256 hash of the entry.</returns>
public static string ComputeHash(this OplogEntry entry)
{
using var sha256 = System.Security.Cryptography.SHA256.Create();
var sb = new System.Text.StringBuilder();
sb.Append(entry.Collection);
sb.Append('|');
sb.Append(entry.Key);
sb.Append('|');
// Ensure stable string representation for Enum (integer value)
sb.Append(((int)entry.Operation).ToString(System.Globalization.CultureInfo.InvariantCulture));
sb.Append('|');
// Payload excluded from hash to avoid serialization non-determinism
// sb.Append(entry.Payload...);
sb.Append('|');
// Timestamp.ToString() is now Invariant
sb.Append(entry.Timestamp.ToString());
sb.Append('|');
sb.Append(entry.PreviousHash);
var bytes = System.Text.Encoding.UTF8.GetBytes(sb.ToString());
var hashBytes = sha256.ComputeHash(bytes);
// Convert to hex string
return BitConverter.ToString(hashBytes).Replace("-", "").ToLowerInvariant();
}
}
public class OplogEntry
{
/// <summary>
/// Gets the collection name associated with this entry.
/// </summary>
public string Collection { get; }
/// <summary>
/// Gets the document key associated with this entry.
/// </summary>
public string Key { get; }
/// <summary>
/// Gets the operation represented by this entry.
/// </summary>
public OperationType Operation { get; }
/// <summary>
/// Gets the serialized payload for the operation.
/// </summary>
public JsonElement? Payload { get; }
/// <summary>
/// Gets the logical timestamp for this entry.
/// </summary>
public HlcTimestamp Timestamp { get; }
/// <summary>
/// Gets the hash of this entry.
/// </summary>
public string Hash { get; }
/// <summary>
/// Gets the hash of the previous entry in the chain.
/// </summary>
public string PreviousHash { get; }
/// <summary>
/// Initializes a new instance of the <see cref="OplogEntry"/> class.
/// </summary>
/// <param name="collection">The collection name.</param>
/// <param name="key">The document key.</param>
/// <param name="operation">The operation type.</param>
/// <param name="payload">The serialized payload.</param>
/// <param name="timestamp">The logical timestamp.</param>
/// <param name="previousHash">The previous entry hash.</param>
/// <param name="hash">The current entry hash. If null, it is computed.</param>
public OplogEntry(string collection, string key, OperationType operation, JsonElement? payload, HlcTimestamp timestamp, string previousHash, string? hash = null)
{
Collection = collection;
Key = key;
Operation = operation;
Payload = payload;
Timestamp = timestamp;
PreviousHash = previousHash ?? string.Empty;
Hash = hash ?? this.ComputeHash();
}
/// <summary>
/// Verifies if the stored Hash matches the content.
/// </summary>
public bool IsValid()
{
return Hash == this.ComputeHash();
}
}

View File

@@ -0,0 +1,44 @@
using System;
namespace ZB.MOM.WW.CBDDC.Core;
/// <summary>
/// Represents a persisted confirmation watermark for a tracked peer and source node.
/// </summary>
public class PeerOplogConfirmation
{
/// <summary>
/// Gets or sets the tracked peer node identifier.
/// </summary>
public string PeerNodeId { get; set; } = "";
/// <summary>
/// Gets or sets the source node identifier this confirmation applies to.
/// </summary>
public string SourceNodeId { get; set; } = "";
/// <summary>
/// Gets or sets the physical wall-clock component of the confirmed HLC timestamp.
/// </summary>
public long ConfirmedWall { get; set; }
/// <summary>
/// Gets or sets the logical counter component of the confirmed HLC timestamp.
/// </summary>
public int ConfirmedLogic { get; set; }
/// <summary>
/// Gets or sets the confirmed hash at the watermark.
/// </summary>
public string ConfirmedHash { get; set; } = "";
/// <summary>
/// Gets or sets when this confirmation record was last updated in UTC.
/// </summary>
public DateTimeOffset LastConfirmedUtc { get; set; } = DateTimeOffset.UtcNow;
/// <summary>
/// Gets or sets whether this tracked peer is active for pruning/sync gating.
/// </summary>
public bool IsActive { get; set; } = true;
}

View File

@@ -0,0 +1,225 @@
using System.Text.Json;
namespace ZB.MOM.WW.CBDDC.Core;
public abstract class QueryNode { }
public class Eq : QueryNode
{
/// <summary>
/// Gets the field name to compare.
/// </summary>
public string Field { get; }
/// <summary>
/// Gets the value to compare against.
/// </summary>
public object Value { get; }
/// <summary>
/// Initializes a new equality query node.
/// </summary>
/// <param name="field">The field name to compare.</param>
/// <param name="value">The value to compare against.</param>
public Eq(string field, object value) { Field = field; Value = value; }
}
public class Gt : QueryNode
{
/// <summary>
/// Gets the field name to compare.
/// </summary>
public string Field { get; }
/// <summary>
/// Gets the threshold value.
/// </summary>
public object Value { get; }
/// <summary>
/// Initializes a new greater-than query node.
/// </summary>
/// <param name="field">The field name to compare.</param>
/// <param name="value">The threshold value.</param>
public Gt(string field, object value) { Field = field; Value = value; }
}
public class Lt : QueryNode
{
/// <summary>
/// Gets the field name to compare.
/// </summary>
public string Field { get; }
/// <summary>
/// Gets the threshold value.
/// </summary>
public object Value { get; }
/// <summary>
/// Initializes a new less-than query node.
/// </summary>
/// <param name="field">The field name to compare.</param>
/// <param name="value">The threshold value.</param>
public Lt(string field, object value) { Field = field; Value = value; }
}
public class Gte : QueryNode
{
/// <summary>
/// Gets the field name to compare.
/// </summary>
public string Field { get; }
/// <summary>
/// Gets the threshold value.
/// </summary>
public object Value { get; }
/// <summary>
/// Initializes a new greater-than-or-equal query node.
/// </summary>
/// <param name="field">The field name to compare.</param>
/// <param name="value">The threshold value.</param>
public Gte(string field, object value) { Field = field; Value = value; }
}
public class Lte : QueryNode
{
/// <summary>
/// Gets the field name to compare.
/// </summary>
public string Field { get; }
/// <summary>
/// Gets the threshold value.
/// </summary>
public object Value { get; }
/// <summary>
/// Initializes a new less-than-or-equal query node.
/// </summary>
/// <param name="field">The field name to compare.</param>
/// <param name="value">The threshold value.</param>
public Lte(string field, object value) { Field = field; Value = value; }
}
public class Neq : QueryNode
{
/// <summary>
/// Gets the field name to compare.
/// </summary>
public string Field { get; }
/// <summary>
/// Gets the value to compare against.
/// </summary>
public object Value { get; }
/// <summary>
/// Initializes a new not-equal query node.
/// </summary>
/// <param name="field">The field name to compare.</param>
/// <param name="value">The value to compare against.</param>
public Neq(string field, object value) { Field = field; Value = value; }
}
public class In : QueryNode
{
/// <summary>
/// Gets the field name to compare.
/// </summary>
public string Field { get; }
/// <summary>
/// Gets the set of values to compare against.
/// </summary>
public object[] Values { get; }
/// <summary>
/// Initializes a new in-list query node.
/// </summary>
/// <param name="field">The field name to compare.</param>
/// <param name="values">The set of values to compare against.</param>
public In(string field, object[] values) { Field = field; Values = values; }
}
public class Contains : QueryNode
{
/// <summary>
/// Gets the field name to compare.
/// </summary>
public string Field { get; }
/// <summary>
/// Gets the substring value to search for.
/// </summary>
public string Value { get; }
/// <summary>
/// Initializes a new contains query node.
/// </summary>
/// <param name="field">The field name to compare.</param>
/// <param name="value">The substring value to search for.</param>
public Contains(string field, string value) { Field = field; Value = value; }
}
public class NotContains : QueryNode
{
/// <summary>
/// Gets the field name to compare.
/// </summary>
public string Field { get; }
/// <summary>
/// Gets the substring value to exclude.
/// </summary>
public string Value { get; }
/// <summary>
/// Initializes a new not-contains query node.
/// </summary>
/// <param name="field">The field name to compare.</param>
/// <param name="value">The substring value to exclude.</param>
public NotContains(string field, string value) { Field = field; Value = value; }
}
public class And : QueryNode
{
/// <summary>
/// Gets the left side of the logical operation.
/// </summary>
public QueryNode Left { get; }
/// <summary>
/// Gets the right side of the logical operation.
/// </summary>
public QueryNode Right { get; }
/// <summary>
/// Initializes a new logical AND query node.
/// </summary>
/// <param name="left">The left query node.</param>
/// <param name="right">The right query node.</param>
public And(QueryNode left, QueryNode right) { Left = left; Right = right; }
}
public class Or : QueryNode
{
/// <summary>
/// Gets the left side of the logical operation.
/// </summary>
public QueryNode Left { get; }
/// <summary>
/// Gets the right side of the logical operation.
/// </summary>
public QueryNode Right { get; }
/// <summary>
/// Initializes a new logical OR query node.
/// </summary>
/// <param name="left">The left query node.</param>
/// <param name="right">The right query node.</param>
public Or(QueryNode left, QueryNode right) { Left = left; Right = right; }
}

View File

@@ -0,0 +1,105 @@
# ZB.MOM.WW.CBDDC.Core
Core abstractions and logic for **CBDDC**, a peer-to-peer data synchronization middleware for .NET.
## What Is CBDDC?
CBDDC is **not** a database — it's a sync layer that plugs into your existing data store (BLite) and enables automatic P2P replication across nodes in a mesh network. Your application reads and writes to its database as usual; CBDDC handles synchronization in the background.
## What's In This Package
- **Interfaces**: `IDocumentStore`, `IOplogStore`, `IVectorClockService`, `IConflictResolver`
- **Models**: `OplogEntry`, `Document`, `HlcTimestamp`, `VectorClock`
- **Conflict Resolution**: `LastWriteWinsConflictResolver`, `RecursiveNodeMergeConflictResolver`
- **Production Features**: Document caching (LRU), offline queue, health monitoring, retry policies
## Installation
```bash
# Pick a persistence provider
dotnet add package ZB.MOM.WW.CBDDC.Persistence # Embedded document DB
# Add networking
dotnet add package ZB.MOM.WW.CBDDC.Network
```
## Quick Start
```csharp
// 1. Define your DbContext
public class MyDbContext : CBDDCDocumentDbContext
{
public DocumentCollection<string, User> Users { get; private set; }
public MyDbContext(string path) : base(path) { }
}
// 2. Create your DocumentStore (the sync bridge)
public class MyDocumentStore : BLiteDocumentStore<MyDbContext>
{
public MyDocumentStore(MyDbContext ctx, IPeerNodeConfigurationProvider cfg,
IVectorClockService vc, ILogger<MyDocumentStore>? log = null)
: base(ctx, cfg, vc, logger: log)
{
WatchCollection("Users", ctx.Users, u => u.Id);
}
protected override async Task ApplyContentToEntityAsync(
string collection, string key, JsonElement content, CancellationToken ct)
{
var user = content.Deserialize<User>()!;
user.Id = key;
var existing = _context.Users.Find(u => u.Id == key).FirstOrDefault();
if (existing != null) _context.Users.Update(user);
else _context.Users.Insert(user);
await _context.SaveChangesAsync(ct);
}
// ... implement other abstract methods
}
// 3. Register and use
builder.Services.AddCBDDCCore()
.AddCBDDCBLite<MyDbContext, MyDocumentStore>(
sp => new MyDbContext("data.blite"))
.AddCBDDCNetwork<StaticPeerNodeConfigurationProvider>();
```
## Key Concepts
| Concept | Description |
|---------|-------------|
| **CDC** | Change Data Capture — watches collections registered via `WatchCollection()` |
| **Oplog** | Append-only hash-chained journal of changes per node |
| **VectorClock** | Tracks causal ordering across the mesh |
| **DocumentStore** | Your bridge between entities and the sync engine |
## Architecture
```
Your App ? DbContext.SaveChangesAsync()
?
? CDC Trigger
DocumentStore.CreateOplogEntryAsync()
?
??? OplogEntry (hash-chained, HLC timestamped)
??? VectorClockService.Update()
?
?
SyncOrchestrator (background)
??? Push to peers
??? Pull from peers ? ApplyBatchAsync
```
## Related Packages
- **ZB.MOM.WW.CBDDC.Persistence** � BLite embedded provider (.NET 10+)
- **ZB.MOM.WW.CBDDC.Network** — P2P networking (UDP discovery, TCP sync, Gossip)
## Documentation
- **[Complete Documentation](https://github.com/CBDDC/ZB.MOM.WW.CBDDC.Net)**
- **[Sample Application](https://github.com/CBDDC/ZB.MOM.WW.CBDDC.Net/tree/main/samples/ZB.MOM.WW.CBDDC.Sample.Console)**
- **[Integration Guide](https://github.com/CBDDC/ZB.MOM.WW.CBDDC.Net#integrating-with-your-database)**
## License
MIT — see [LICENSE](https://github.com/CBDDC/ZB.MOM.WW.CBDDC.Net/blob/main/LICENSE)

View File

@@ -0,0 +1,27 @@
using System;
using System.Threading;
using System.Threading.Tasks;
namespace ZB.MOM.WW.CBDDC.Core.Resilience
{
public interface IRetryPolicy
{
/// <summary>
/// Executes an asynchronous operation with retry handling.
/// </summary>
/// <param name="operation">The operation to execute.</param>
/// <param name="operationName">The operation name used for diagnostics.</param>
/// <param name="cancellationToken">A token used to cancel the operation.</param>
/// <returns>A task that represents the asynchronous execution.</returns>
Task ExecuteAsync(Func<Task> operation, string operationName, CancellationToken cancellationToken = default);
/// <summary>
/// Executes an asynchronous operation with retry handling and returns a result.
/// </summary>
/// <typeparam name="T">The result type.</typeparam>
/// <param name="operation">The operation to execute.</param>
/// <param name="operationName">The operation name used for diagnostics.</param>
/// <param name="cancellationToken">A token used to cancel the operation.</param>
/// <returns>A task that represents the asynchronous execution and yields the operation result.</returns>
Task<T> ExecuteAsync<T>(Func<Task<T>> operation, string operationName, CancellationToken cancellationToken = default);
}
}

View File

@@ -0,0 +1,116 @@
using System;
using System.Threading;
using System.Threading.Tasks;
using ZB.MOM.WW.CBDDC.Core.Exceptions;
using ZB.MOM.WW.CBDDC.Core.Network;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Logging.Abstractions;
namespace ZB.MOM.WW.CBDDC.Core.Resilience;
/// <summary>
/// Provides retry logic for transient failures.
/// </summary>
public class RetryPolicy : IRetryPolicy
{
private readonly IPeerNodeConfigurationProvider _peerNodeConfigurationProvider;
private readonly ILogger<RetryPolicy> _logger;
/// <summary>
/// Initializes a new instance of the <see cref="RetryPolicy"/> class.
/// </summary>
/// <param name="peerNodeConfigurationProvider">The provider for retry configuration values.</param>
/// <param name="logger">The logger instance.</param>
public RetryPolicy(IPeerNodeConfigurationProvider peerNodeConfigurationProvider, ILogger<RetryPolicy>? logger = null)
{
_logger = logger ?? NullLogger<RetryPolicy>.Instance;
_peerNodeConfigurationProvider = peerNodeConfigurationProvider
?? throw new ArgumentNullException(nameof(peerNodeConfigurationProvider));
}
/// <summary>
/// Executes an operation with retry logic.
/// </summary>
/// <typeparam name="T">The result type returned by the operation.</typeparam>
/// <param name="operation">The asynchronous operation to execute.</param>
/// <param name="operationName">The operation name used for logging.</param>
/// <param name="cancellationToken">A token used to cancel retry delays.</param>
public async Task<T> ExecuteAsync<T>(
Func<Task<T>> operation,
string operationName,
CancellationToken cancellationToken = default)
{
var config = await _peerNodeConfigurationProvider.GetConfiguration();
Exception? lastException = null;
for (int attempt = 1; attempt <= config.RetryAttempts; attempt++)
{
try
{
_logger.LogDebug("Executing {Operation} (attempt {Attempt}/{Max})",
operationName, attempt, config.RetryAttempts);
return await operation();
}
catch (Exception ex) when (attempt < config.RetryAttempts && IsTransient(ex))
{
lastException = ex;
var delay = config.RetryDelayMs * attempt; // Exponential backoff
_logger.LogWarning(ex,
"Operation {Operation} failed (attempt {Attempt}/{Max}). Retrying in {Delay}ms...",
operationName, attempt, config.RetryAttempts, delay);
await Task.Delay(delay, cancellationToken);
}
}
if (lastException != null)
{
_logger.LogError(lastException,
"Operation {Operation} failed after {Attempts} attempts",
operationName, config.RetryAttempts);
}
else
{
_logger.LogError(
"Operation {Operation} failed after {Attempts} attempts",
operationName, config.RetryAttempts);
}
throw new CBDDCException("RETRY_EXHAUSTED",
$"Operation '{operationName}' failed after {config.RetryAttempts} attempts",
lastException!);
}
/// <summary>
/// Executes an operation with retry logic (void return).
/// </summary>
/// <param name="operation">The asynchronous operation to execute.</param>
/// <param name="operationName">The operation name used for logging.</param>
/// <param name="cancellationToken">A token used to cancel retry delays.</param>
public async Task ExecuteAsync(
Func<Task> operation,
string operationName,
CancellationToken cancellationToken = default)
{
await ExecuteAsync(async () =>
{
await operation();
return true;
}, operationName, cancellationToken);
}
private static bool IsTransient(Exception ex)
{
// Network errors are typically transient
if (ex is NetworkException or System.Net.Sockets.SocketException or System.IO.IOException)
return true;
// Timeout errors are transient
if (ex is Exceptions.TimeoutException or OperationCanceledException)
return true;
return false;
}
}

View File

@@ -0,0 +1,21 @@
namespace ZB.MOM.WW.CBDDC.Core;
public class SnapshotMetadata
{
/// <summary>
/// Gets or sets the node identifier associated with the snapshot.
/// </summary>
public string NodeId { get; set; } = "";
/// <summary>
/// Gets or sets the physical time component of the snapshot timestamp.
/// </summary>
public long TimestampPhysicalTime { get; set; }
/// <summary>
/// Gets or sets the logical counter component of the snapshot timestamp.
/// </summary>
public int TimestampLogicalCounter { get; set; }
/// <summary>
/// Gets or sets the snapshot hash.
/// </summary>
public string Hash { get; set; } = "";
}

View File

@@ -0,0 +1,16 @@
using System;
namespace ZB.MOM.WW.CBDDC.Core.Storage;
/// <summary>
/// Represents an error that occurs when a database is found to be corrupt.
/// </summary>
public class CorruptDatabaseException : Exception
{
/// <summary>
/// Initializes a new instance of the <see cref="CorruptDatabaseException"/> class.
/// </summary>
/// <param name="message">The exception message.</param>
/// <param name="innerException">The underlying exception that caused this error.</param>
public CorruptDatabaseException(string message, Exception innerException) : base(message, innerException) { }
}

View File

@@ -0,0 +1,108 @@
using System.Collections.Generic;
using System.Threading;
using System.Threading.Tasks;
namespace ZB.MOM.WW.CBDDC.Core.Storage;
/// <summary>
/// Defines the contract for storing and retrieving document metadata for sync tracking.
/// Document metadata stores HLC timestamps and deleted state without modifying application entities.
/// </summary>
public interface IDocumentMetadataStore : ISnapshotable<DocumentMetadata>
{
/// <summary>
/// Gets the metadata for a specific document.
/// </summary>
/// <param name="collection">The collection name.</param>
/// <param name="key">The document key.</param>
/// <param name="cancellationToken">A cancellation token.</param>
/// <returns>The document metadata if found; otherwise null.</returns>
Task<DocumentMetadata?> GetMetadataAsync(string collection, string key, CancellationToken cancellationToken = default);
/// <summary>
/// Gets metadata for all documents in a collection.
/// </summary>
/// <param name="collection">The collection name.</param>
/// <param name="cancellationToken">A cancellation token.</param>
/// <returns>Enumerable of document metadata for the collection.</returns>
Task<IEnumerable<DocumentMetadata>> GetMetadataByCollectionAsync(string collection, CancellationToken cancellationToken = default);
/// <summary>
/// Upserts (inserts or updates) metadata for a document.
/// </summary>
/// <param name="metadata">The metadata to upsert.</param>
/// <param name="cancellationToken">A cancellation token.</param>
Task UpsertMetadataAsync(DocumentMetadata metadata, CancellationToken cancellationToken = default);
/// <summary>
/// Upserts metadata for multiple documents in batch.
/// </summary>
/// <param name="metadatas">The metadata items to upsert.</param>
/// <param name="cancellationToken">A cancellation token.</param>
Task UpsertMetadataBatchAsync(IEnumerable<DocumentMetadata> metadatas, CancellationToken cancellationToken = default);
/// <summary>
/// Marks a document as deleted by setting IsDeleted=true and updating the timestamp.
/// </summary>
/// <param name="collection">The collection name.</param>
/// <param name="key">The document key.</param>
/// <param name="timestamp">The HLC timestamp of the deletion.</param>
/// <param name="cancellationToken">A cancellation token.</param>
Task MarkDeletedAsync(string collection, string key, HlcTimestamp timestamp, CancellationToken cancellationToken = default);
/// <summary>
/// Gets all document metadata with timestamps after the specified timestamp.
/// Used for incremental sync to find documents modified since last sync.
/// </summary>
/// <param name="since">The timestamp to compare against.</param>
/// <param name="collections">Optional collection filter.</param>
/// <param name="cancellationToken">A cancellation token.</param>
/// <returns>Documents modified after the specified timestamp.</returns>
Task<IEnumerable<DocumentMetadata>> GetMetadataAfterAsync(HlcTimestamp since, IEnumerable<string>? collections = null, CancellationToken cancellationToken = default);
}
/// <summary>
/// Represents metadata for a document used in sync tracking.
/// </summary>
public class DocumentMetadata
{
/// <summary>
/// Gets or sets the collection name.
/// </summary>
public string Collection { get; set; } = "";
/// <summary>
/// Gets or sets the document key.
/// </summary>
public string Key { get; set; } = "";
/// <summary>
/// Gets or sets the HLC timestamp of the last modification.
/// </summary>
public HlcTimestamp UpdatedAt { get; set; }
/// <summary>
/// Gets or sets whether this document is marked as deleted (tombstone).
/// </summary>
public bool IsDeleted { get; set; }
/// <summary>
/// Initializes a new instance of the <see cref="DocumentMetadata"/> class.
/// </summary>
public DocumentMetadata() { }
/// <summary>
/// Initializes a new instance of the <see cref="DocumentMetadata"/> class.
/// </summary>
/// <param name="collection">The collection name.</param>
/// <param name="key">The document key.</param>
/// <param name="updatedAt">The last update timestamp.</param>
/// <param name="isDeleted">Whether the document is marked as deleted.</param>
public DocumentMetadata(string collection, string key, HlcTimestamp updatedAt, bool isDeleted = false)
{
Collection = collection;
Key = key;
UpdatedAt = updatedAt;
IsDeleted = isDeleted;
}
}

View File

@@ -0,0 +1,103 @@
using System;
using System.Collections.Generic;
using System.Threading;
using System.Threading.Tasks;
namespace ZB.MOM.WW.CBDDC.Core.Storage;
/// <summary>
/// Handles basic CRUD operations for documents.
/// </summary>
public interface IDocumentStore : ISnapshotable<Document>
{
/// <summary>
/// Gets the collections this store is interested in.
/// </summary>
IEnumerable<string> InterestedCollection { get; }
/// <summary>
/// Asynchronously retrieves a incoming from the specified collection by its key.
/// </summary>
/// <param name="collection">The name of the collection containing the incoming to retrieve. Cannot be null or empty.</param>
/// <param name="key">The unique key identifying the incoming within the collection. Cannot be null or empty.</param>
/// <param name="cancellationToken">A cancellation token that can be used to cancel the operation.</param>
/// <returns>A task that represents the asynchronous operation. The task result contains the incoming if found; otherwise, null.</returns>
Task<Document?> GetDocumentAsync(string collection, string key, CancellationToken cancellationToken = default);
/// <summary>
/// Asynchronously retrieves all documents belonging to the specified collection.
/// </summary>
/// <param name="collection">The name of the collection from which to retrieve documents. Cannot be null or empty.</param>
/// <param name="cancellationToken">A cancellation token that can be used to cancel the asynchronous operation.</param>
/// <returns>A task that represents the asynchronous operation. The task result contains an enumerable collection of
/// documents in the specified collection. The collection is empty if no documents are found.</returns>
Task<IEnumerable<Document>> GetDocumentsByCollectionAsync(string collection, CancellationToken cancellationToken = default);
/// <summary>
/// Asynchronously inserts a batch of documents into the data store.
/// </summary>
/// <param name="documents">The collection of documents to insert. Cannot be null or contain null elements.</param>
/// <param name="cancellationToken">A cancellation token that can be used to cancel the operation.</param>
/// <returns>A task that represents the asynchronous operation. The task result is <see langword="true"/> if all documents
/// were inserted successfully; otherwise, <see langword="false"/>.</returns>
Task<bool> InsertBatchDocumentsAsync(IEnumerable<Document> documents, CancellationToken cancellationToken = default);
/// <summary>
/// Asynchronously updates the specified incoming in the data store.
/// </summary>
/// <param name="document">The incoming to update. Cannot be null.</param>
/// <param name="cancellationToken">A cancellation token that can be used to cancel the update operation.</param>
/// <returns>A task that represents the asynchronous operation. The task result is <see langword="true"/> if the incoming was
/// successfully updated; otherwise, <see langword="false"/>.</returns>
Task<bool> PutDocumentAsync(Document document, CancellationToken cancellationToken = default);
/// <summary>
/// Asynchronously updates a batch of documents in the data store.
/// </summary>
/// <param name="documents">The collection of documents to update. Cannot be null or contain null elements.</param>
/// <param name="cancellationToken">A cancellation token that can be used to cancel the operation.</param>
/// <returns>A task that represents the asynchronous operation. The task result is <see langword="true"/> if all documents
/// were updated successfully; otherwise, <see langword="false"/>.</returns>
Task<bool> UpdateBatchDocumentsAsync(IEnumerable<Document> documents, CancellationToken cancellationToken = default);
/// <summary>
/// Asynchronously deletes a incoming identified by the specified key from the given collection.
/// </summary>
/// <param name="collection">The name of the collection containing the incoming to delete. Cannot be null or empty.</param>
/// <param name="key">The unique key identifying the incoming to delete. Cannot be null or empty.</param>
/// <param name="cancellationToken">A cancellation token that can be used to cancel the delete operation.</param>
/// <returns>A task that represents the asynchronous delete operation. The task result is <see langword="true"/> if the
/// incoming was successfully deleted; otherwise, <see langword="false"/>.</returns>
Task<bool> DeleteDocumentAsync(string collection, string key, CancellationToken cancellationToken = default);
/// <summary>
/// Asynchronously deletes a batch of documents identified by their keys.
/// </summary>
/// <remarks>
/// If any of the specified documents cannot be deleted, the method returns <see langword="false"/> but does not
/// throw an exception. The operation is performed asynchronously and may complete partially if cancellation is requested.
/// </remarks>
/// <param name="documentKeys">A collection of incoming keys that specify the documents to delete. Cannot be null or contain null or empty
/// values.</param>
/// <param name="cancellationToken">A cancellation token that can be used to cancel the delete operation.</param>
/// <returns>A task that represents the asynchronous delete operation. The task result is <see langword="true"/> if all
/// specified documents were successfully deleted; otherwise, <see langword="false"/>.</returns>
Task<bool> DeleteBatchDocumentsAsync(IEnumerable<string> documentKeys, CancellationToken cancellationToken = default);
/// <summary>
/// Asynchronously merges the specified incoming with existing data and returns the updated incoming.
/// </summary>
/// <param name="incoming">The incoming to merge. Cannot be null.</param>
/// <param name="cancellationToken">A cancellation token that can be used to cancel the merge operation.</param>
/// <returns>A task that represents the asynchronous merge operation. The task result contains the merged incoming.</returns>
Task<Document> MergeAsync(Document incoming, CancellationToken cancellationToken = default);
/// <summary>
/// Asynchronously retrieves documents identified by the specified collection and key pairs.
/// </summary>
/// <param name="documentKeys">A list of tuples, each containing the collection name and the document key that uniquely identify the documents
/// to retrieve. Cannot be null or empty.</param>
/// <param name="cancellationToken">A cancellation token that can be used to cancel the asynchronous operation.</param>
/// <returns>A task that represents the asynchronous retrieval operation.</returns>
Task<IEnumerable<Document>> GetDocumentsAsync(List<(string Collection, string Key)> documentKeys, CancellationToken cancellationToken);
}

View File

@@ -0,0 +1,101 @@
using System;
using System.Buffers;
using System.Collections.Generic;
using System.Threading;
using System.Threading.Tasks;
namespace ZB.MOM.WW.CBDDC.Core.Storage;
/// <summary>
/// Handles operations related to the Operation Log (Oplog), synchronization, and logical clocks.
/// </summary>
public interface IOplogStore : ISnapshotable<OplogEntry>
{
/// <summary>
/// Occurs when changes are applied to the store from external sources (sync).
/// </summary>
event EventHandler<ChangesAppliedEventArgs> ChangesApplied;
/// <summary>
/// Appends a new entry to the operation log asynchronously.
/// </summary>
/// <param name="entry">The operation log entry to append. Cannot be null.</param>
/// <param name="cancellationToken">A cancellation token that can be used to cancel the append operation.</param>
/// <returns>A task that represents the asynchronous append operation.</returns>
Task AppendOplogEntryAsync(OplogEntry entry, CancellationToken cancellationToken = default);
/// <summary>
/// Asynchronously retrieves all oplog entries that occurred after the specified timestamp.
/// </summary>
/// <param name="timestamp">The timestamp after which oplog entries should be returned.</param>
/// <param name="collections">An optional collection of collection names to filter the results.</param>
/// <param name="cancellationToken">A cancellation token that can be used to cancel the asynchronous operation.</param>
/// <returns>A task that represents the asynchronous operation containing matching oplog entries.</returns>
Task<IEnumerable<OplogEntry>> GetOplogAfterAsync(HlcTimestamp timestamp, IEnumerable<string>? collections = null, CancellationToken cancellationToken = default);
/// <summary>
/// Asynchronously retrieves the latest observed hybrid logical clock (HLC) timestamp.
/// </summary>
/// <param name="cancellationToken">A cancellation token that can be used to cancel the operation.</param>
/// <returns>A task that represents the asynchronous operation containing the latest HLC timestamp.</returns>
Task<HlcTimestamp> GetLatestTimestampAsync(CancellationToken cancellationToken = default);
/// <summary>
/// Asynchronously retrieves the current vector clock representing the state of distributed events.
/// </summary>
/// <param name="cancellationToken">A cancellation token that can be used to cancel the asynchronous operation.</param>
/// <returns>A task that represents the asynchronous operation containing the current vector clock.</returns>
Task<VectorClock> GetVectorClockAsync(CancellationToken cancellationToken = default);
/// <summary>
/// Retrieves a collection of oplog entries for the specified node that occurred after the given timestamp.
/// </summary>
/// <param name="nodeId">The unique identifier of the node for which to retrieve oplog entries. Cannot be null or empty.</param>
/// <param name="since">The timestamp after which oplog entries should be returned.</param>
/// <param name="collections">An optional collection of collection names to filter the oplog entries.</param>
/// <param name="cancellationToken">A cancellation token that can be used to cancel the asynchronous operation.</param>
/// <returns>A task that represents the asynchronous operation containing oplog entries for the specified node.</returns>
Task<IEnumerable<OplogEntry>> GetOplogForNodeAfterAsync(string nodeId, HlcTimestamp since, IEnumerable<string>? collections = null, CancellationToken cancellationToken = default);
/// <summary>
/// Asynchronously retrieves the hash of the most recent entry for the specified node.
/// </summary>
/// <param name="nodeId">The unique identifier of the node for which to retrieve the last entry hash. Cannot be null or empty.</param>
/// <param name="cancellationToken">A cancellation token that can be used to cancel the operation.</param>
/// <returns>A task that represents the asynchronous operation containing the hash string of the last entry or null.</returns>
Task<string?> GetLastEntryHashAsync(string nodeId, CancellationToken cancellationToken = default);
/// <summary>
/// Asynchronously retrieves a sequence of oplog entries representing the chain between the specified start and end hashes.
/// </summary>
/// <param name="startHash">The hash of the first entry in the chain range. Cannot be null or empty.</param>
/// <param name="endHash">The hash of the last entry in the chain range. Cannot be null or empty.</param>
/// <param name="cancellationToken">A cancellation token that can be used to cancel the asynchronous operation.</param>
/// <returns>A task that represents the asynchronous operation containing OplogEntry objects in chain order.</returns>
Task<IEnumerable<OplogEntry>> GetChainRangeAsync(string startHash, string endHash, CancellationToken cancellationToken = default);
/// <summary>
/// Asynchronously retrieves the oplog entry associated with the specified hash value.
/// </summary>
/// <param name="hash">The hash string identifying the oplog entry to retrieve. Cannot be null or empty.</param>
/// <param name="cancellationToken">A cancellation token that can be used to cancel the asynchronous operation.</param>
/// <returns>A task representing the asynchronous operation containing the OplogEntry if found, otherwise null.</returns>
Task<OplogEntry?> GetEntryByHashAsync(string hash, CancellationToken cancellationToken = default);
/// <summary>
/// Applies a batch of oplog entries asynchronously to the target data store.
/// </summary>
/// <param name="oplogEntries">A collection of OplogEntry objects representing the operations to apply. Cannot be null.</param>
/// <param name="cancellationToken">A cancellation token that can be used to cancel the batch operation.</param>
/// <returns>A task that represents the asynchronous batch apply operation.</returns>
Task ApplyBatchAsync(IEnumerable<OplogEntry> oplogEntries, CancellationToken cancellationToken = default);
/// <summary>
/// Asynchronously removes entries from the oplog that are older than the specified cutoff timestamp.
/// </summary>
/// <param name="cutoff">The timestamp that defines the upper bound for entries to be pruned.</param>
/// <param name="cancellationToken">A cancellation token that can be used to cancel the prune operation.</param>
/// <returns>A task that represents the asynchronous prune operation.</returns>
Task PruneOplogAsync(HlcTimestamp cutoff, CancellationToken cancellationToken = default);
}

View File

@@ -0,0 +1,41 @@
using System.Collections.Generic;
using System.Threading;
using System.Threading.Tasks;
using ZB.MOM.WW.CBDDC.Core.Network;
namespace ZB.MOM.WW.CBDDC.Core.Storage;
/// <summary>
/// Handles storage and retrieval of remote peer configurations.
/// </summary>
public interface IPeerConfigurationStore : ISnapshotable<RemotePeerConfiguration>
{
/// <summary>
/// Saves or updates a remote peer configuration in the persistent store.
/// </summary>
/// <param name="peer">The remote peer configuration to save.</param>
/// <param name="cancellationToken">Cancellation token.</param>
Task SaveRemotePeerAsync(RemotePeerConfiguration peer, CancellationToken cancellationToken = default);
/// <summary>
/// Retrieves all remote peer configurations from the persistent store.
/// </summary>
/// <param name="cancellationToken">Cancellation token.</param>
/// <returns>Collection of remote peer configurations.</returns>
Task<IEnumerable<RemotePeerConfiguration>> GetRemotePeersAsync(CancellationToken cancellationToken = default);
/// <summary>
/// Asynchronously retrieves the configuration for a remote peer identified by the specified node ID.
/// </summary>
/// <param name="nodeId">The unique identifier of the remote peer whose configuration is to be retrieved.</param>
/// <param name="cancellationToken">A cancellation token that can be used to cancel the asynchronous operation.</param>
/// <returns>A task containing the RemotePeerConfiguration if found; otherwise, null.</returns>
Task<RemotePeerConfiguration?> GetRemotePeerAsync(string nodeId, CancellationToken cancellationToken);
/// <summary>
/// Removes a remote peer configuration from the persistent store.
/// </summary>
/// <param name="nodeId">The unique identifier of the peer to remove.</param>
/// <param name="cancellationToken">Cancellation token.</param>
Task RemoveRemotePeerAsync(string nodeId, CancellationToken cancellationToken = default);
}

View File

@@ -0,0 +1,71 @@
using System.Collections.Generic;
using System.Threading;
using System.Threading.Tasks;
using ZB.MOM.WW.CBDDC.Core.Network;
namespace ZB.MOM.WW.CBDDC.Core.Storage;
/// <summary>
/// Defines persistence operations for peer oplog confirmation tracking.
/// </summary>
public interface IPeerOplogConfirmationStore : ISnapshotable<PeerOplogConfirmation>
{
/// <summary>
/// Ensures the specified peer is tracked for confirmation-based pruning.
/// </summary>
/// <param name="peerNodeId">The peer node identifier.</param>
/// <param name="address">The peer network address.</param>
/// <param name="type">The peer type.</param>
/// <param name="cancellationToken">A cancellation token.</param>
Task EnsurePeerRegisteredAsync(
string peerNodeId,
string address,
PeerType type,
CancellationToken cancellationToken = default);
/// <summary>
/// Updates the confirmation watermark for a tracked peer and source node.
/// </summary>
/// <param name="peerNodeId">The tracked peer node identifier.</param>
/// <param name="sourceNodeId">The source node identifier of the confirmed oplog stream.</param>
/// <param name="timestamp">The confirmed HLC timestamp.</param>
/// <param name="hash">The confirmed hash at the specified timestamp.</param>
/// <param name="cancellationToken">A cancellation token.</param>
Task UpdateConfirmationAsync(
string peerNodeId,
string sourceNodeId,
HlcTimestamp timestamp,
string hash,
CancellationToken cancellationToken = default);
/// <summary>
/// Gets all persisted peer confirmations.
/// </summary>
/// <param name="cancellationToken">A cancellation token.</param>
/// <returns>All peer confirmations.</returns>
Task<IEnumerable<PeerOplogConfirmation>> GetConfirmationsAsync(CancellationToken cancellationToken = default);
/// <summary>
/// Gets persisted confirmations for a specific tracked peer.
/// </summary>
/// <param name="peerNodeId">The peer node identifier.</param>
/// <param name="cancellationToken">A cancellation token.</param>
/// <returns>Peer confirmations for the requested peer.</returns>
Task<IEnumerable<PeerOplogConfirmation>> GetConfirmationsForPeerAsync(
string peerNodeId,
CancellationToken cancellationToken = default);
/// <summary>
/// Deactivates tracking for the specified peer.
/// </summary>
/// <param name="peerNodeId">The peer node identifier.</param>
/// <param name="cancellationToken">A cancellation token.</param>
Task RemovePeerTrackingAsync(string peerNodeId, CancellationToken cancellationToken = default);
/// <summary>
/// Gets all active tracked peer identifiers.
/// </summary>
/// <param name="cancellationToken">A cancellation token.</param>
/// <returns>Distinct active tracked peer identifiers.</returns>
Task<IEnumerable<string>> GetActiveTrackedPeersAsync(CancellationToken cancellationToken = default);
}

View File

@@ -0,0 +1,48 @@
using System.Collections.Generic;
using System.Threading;
using System.Threading.Tasks;
namespace ZB.MOM.WW.CBDDC.Core.Storage;
public interface ISnapshotMetadataStore : ISnapshotable<SnapshotMetadata>
{
/// <summary>
/// Asynchronously retrieves the snapshot metadata associated with the specified node identifier.
/// </summary>
/// <param name="nodeId">The unique identifier of the node for which to retrieve snapshot metadata. Cannot be null or empty.</param>
/// <param name="cancellationToken">A token to monitor for cancellation requests.</param>
/// <returns>A task that represents the asynchronous operation. The task result contains the <see cref="SnapshotMetadata"/>
/// for the specified node if found; otherwise, <see langword="null"/>.</returns>
Task<SnapshotMetadata?> GetSnapshotMetadataAsync(string nodeId, CancellationToken cancellationToken = default);
/// <summary>
/// Asynchronously inserts the specified snapshot metadata into the data store.
/// </summary>
/// <param name="metadata">The snapshot metadata to insert. Cannot be null.</param>
/// <param name="cancellationToken">A cancellation token that can be used to cancel the asynchronous operation.</param>
/// <returns>A task that represents the asynchronous insert operation.</returns>
Task InsertSnapshotMetadataAsync(SnapshotMetadata metadata, CancellationToken cancellationToken = default);
/// <summary>
/// Asynchronously updates the metadata for an existing snapshot.
/// </summary>
/// <param name="existingMeta">The metadata object representing the snapshot to update. Cannot be null.</param>
/// <param name="cancellationToken">A cancellation token that can be used to cancel the asynchronous operation.</param>
/// <returns>A task that represents the asynchronous update operation.</returns>
Task UpdateSnapshotMetadataAsync(SnapshotMetadata existingMeta, CancellationToken cancellationToken = default);
/// <summary>
/// Asynchronously retrieves the hash of the current snapshot for the specified node.
/// </summary>
/// <param name="nodeId">The unique identifier of the node for which to obtain the snapshot hash.</param>
/// <param name="cancellationToken">A cancellation token that can be used to cancel the operation.</param>
/// <returns>A task containing the snapshot hash as a string, or null if no snapshot is available.</returns>
Task<string?> GetSnapshotHashAsync(string nodeId, CancellationToken cancellationToken = default);
/// <summary>
/// Gets all snapshot metadata entries. Used for initializing VectorClock cache.
/// </summary>
/// <param name="cancellationToken">A cancellation token.</param>
/// <returns>All snapshot metadata entries.</returns>
Task<IEnumerable<SnapshotMetadata>> GetAllSnapshotMetadataAsync(CancellationToken cancellationToken = default);
}

View File

@@ -0,0 +1,35 @@
using System.IO;
using System.Threading;
using System.Threading.Tasks;
namespace ZB.MOM.WW.CBDDC.Core.Storage;
/// <summary>
/// Handles full database lifecycle operations such as snapshots, replacement, and clearing data.
/// </summary>
public interface ISnapshotService
{
/// <summary>
/// Asynchronously creates a snapshot of the current state and writes it to the specified destination stream.
/// </summary>
/// <param name="destination">The stream to which the snapshot data will be written.</param>
/// <param name="cancellationToken">A cancellation token that can be used to cancel the snapshot creation operation.</param>
/// <returns>A task that represents the asynchronous snapshot creation operation.</returns>
Task CreateSnapshotAsync(Stream destination, CancellationToken cancellationToken = default);
/// <summary>
/// Replaces the existing database with the contents provided in the specified stream asynchronously.
/// </summary>
/// <param name="databaseStream">A stream containing the new database data to be used for replacement.</param>
/// <param name="cancellationToken">A cancellation token that can be used to cancel the operation.</param>
/// <returns>A task that represents the asynchronous database replacement operation.</returns>
Task ReplaceDatabaseAsync(Stream databaseStream, CancellationToken cancellationToken = default);
/// <summary>
/// Merges the provided snapshot stream into the current data store asynchronously.
/// </summary>
/// <param name="snapshotStream">A stream containing the snapshot data to be merged.</param>
/// <param name="cancellationToken">A cancellation token that can be used to cancel the merge operation.</param>
/// <returns>A task that represents the asynchronous merge operation.</returns>
Task MergeSnapshotAsync(Stream snapshotStream, CancellationToken cancellationToken = default);
}

Some files were not shown because too many files have changed in this diff Show More