Initial import of the CBDDC codebase with docs and tests. Add a .NET-focused gitignore to keep generated artifacts out of source control.
Some checks failed
CI / verify (push) Has been cancelled
Some checks failed
CI / verify (push) Has been cancelled
This commit is contained in:
32
.github/ISSUE_TEMPLATE/bug_report.md
vendored
Executable file
32
.github/ISSUE_TEMPLATE/bug_report.md
vendored
Executable 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
20
.github/ISSUE_TEMPLATE/feature_request.md
vendored
Executable 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
25
.github/pull_request_template.md
vendored
Executable 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
52
.github/workflows/ci.yml
vendored
Normal 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
504
.gitignore
vendored
Executable 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
7
CBDDC-docs-fixed.md
Normal 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
456
CBDDC-docs-issues.md
Normal 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
19
CBDDC.Libs.slnx
Normal 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
23
CBDDC.slnx
Normal 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
7
Core-docs-fixed.md
Normal 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
1896
Core-docs-issues.md
Normal file
File diff suppressed because it is too large
Load Diff
7
Directory.Build.props
Normal file
7
Directory.Build.props
Normal 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
7
Network-docs-fixed.md
Normal 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
1176
Network-docs-issues.md
Normal file
File diff suppressed because it is too large
Load Diff
7
Persistence-docs-fixed.md
Normal file
7
Persistence-docs-fixed.md
Normal 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
546
Persistence-docs-issues.md
Normal 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
751
README.md
Executable file
@@ -0,0 +1,751 @@
|
||||
# CBDDC
|
||||
|
||||
<div align="center">
|
||||
|
||||
**Peer-to-Peer Data Synchronization Middleware for .NET**
|
||||
|
||||
[](https://dotnet.microsoft.com/)
|
||||
[](LICENSE)
|
||||
|
||||
## Status
|
||||

|
||||

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