Add observable and native change feed support to document stores#4
Closed
aritchie wants to merge 2 commits into
Closed
Add observable and native change feed support to document stores#4aritchie wants to merge 2 commits into
aritchie wants to merge 2 commits into
Conversation
Introduces an opt-in IObservableDocumentStore capability that emits in-process insert/update/remove/clear notifications so consumers can drive reactive UI from their own writes. - Core abstraction: IObservableDocumentStore, DocumentChange<T>, DocumentChangeType, plus a lightweight ChangeBroadcaster (no System.Reactive dependency) and Subscribe/Where/WhenChanged/ WhenDocumentChanged helper extensions. - Implemented on the relational DocumentStore (SQLite, SQLCipher, MySQL, SQL Server, PostgreSQL) and LiteDB. Changes made inside RunInTransaction are buffered and flushed only on commit; rollback discards them. Notifications are published outside the store lock so observer callbacks can safely re-enter the store. - Tests covering insert/update/remove/clear/set-property/batch, subscribe/unsubscribe semantics, per-id and per-type filtering, and transaction commit/rollback for both SQLite and LiteDB. https://claude.ai/code/session_01QXtq5v6bTDLgncKQowM6YW
Adds external-writer-aware, server-side change notifications backed by each database's own change mechanism, alongside the existing in-process IObservableDocumentStore. API: SubscribeChanges<T>(handler, ct) returns an IAsyncDisposable; dispose to stop. Backed by: - PostgreSQL: LISTEN/NOTIFY via idempotent row-level triggers (true push, full insert/update/delete fidelity). Verified end-to-end against a live PostgreSQL, including cross-connection (external writer) and dispose-stops-delivery. - SQL Server: Change Tracking polling with optional SqlDependency query notifications for low-latency wake-ups (configurable via SqlServerChangeFeedOptions; falls back to polling if notifications can't be armed). - CosmosDB: latest-version Change Feed scoped to the type's partition (delivers created/updated documents with body; no deletes). Core wiring lives in IDatabaseProvider (SupportsChangeFeed + SubscribeChangesAsync) and DocumentStore, which maps provider-level RawDocumentChange records to typed DocumentChange<T> and deserializes. Providers without a proper external-change mechanism (SQLite, LiteDB, IndexedDB, MySQL) throw NotSupportedException. Tests: a ChangeFeedTestsBase (insert/update/remove, external writer, dispose) wired for PostgreSQL (runs under Testcontainers), plus not-supported assertions for SQLite/LiteDB. https://claude.ai/code/session_01QXtq5v6bTDLgncKQowM6YW
There was a problem hiding this comment.
Pull request overview
This PR adds in-process observable change notifications and native provider-backed change feeds to DocumentDb, extending the store APIs and provider implementations for reactive and external-writer scenarios.
Changes:
- Adds public change notification APIs and core change types.
- Publishes observable changes from relational and LiteDB CRUD/transaction paths.
- Adds native change-feed implementations for PostgreSQL, SQL Server, and Cosmos DB with new integration tests and README documentation.
Reviewed changes
Copilot reviewed 21 out of 21 changed files in this pull request and generated 3 comments.
Show a summary per file
| File | Description |
|---|---|
src/Shiny.DocumentDb/DocumentChange.cs |
Adds typed change payload model. |
src/Shiny.DocumentDb/DocumentChangeType.cs |
Adds change kind enum. |
src/Shiny.DocumentDb/IObservableDocumentStore.cs |
Adds in-process observable store capability. |
src/Shiny.DocumentDb/IChangeFeedDocumentStore.cs |
Adds native change-feed store capability. |
src/Shiny.DocumentDb/ObservableDocumentStoreExtensions.cs |
Adds convenience observable/feed helpers. |
src/Shiny.DocumentDb/IDatabaseProvider.cs |
Extends providers with optional change-feed support. |
src/Shiny.DocumentDb/Internal/ChangeBroadcaster.cs |
Adds in-process observer fan-out. |
src/Shiny.DocumentDb/Internal/ChangeFeedSubscription.cs |
Adds reusable async subscription worker wrapper. |
src/Shiny.DocumentDb/Internal/RawDocumentChange.cs |
Adds provider-level raw change record. |
src/Shiny.DocumentDb/DocumentStore.cs |
Wires observable notifications and native feed subscription into relational stores. |
src/Shiny.DocumentDb.LiteDb/LiteDbDocumentStore.cs |
Adds observable notifications and transaction buffering for LiteDB. |
src/Shiny.DocumentDb.PostgreSql/PostgreSqlDatabaseProvider.cs |
Adds PostgreSQL LISTEN/NOTIFY trigger-backed feed. |
src/Shiny.DocumentDb.SqlServer/SqlServerDatabaseProvider.cs |
Adds SQL Server Change Tracking polling feed. |
src/Shiny.DocumentDb.SqlServer/SqlServerChangeFeedOptions.cs |
Adds SQL Server feed configuration options. |
src/Shiny.DocumentDb.CosmosDb/CosmosDbDocumentStore.cs |
Adds Cosmos DB Change Feed subscription support. |
tests/Shiny.DocumentDb.Tests/ObservableTests.cs |
Adds observable behavior tests. |
tests/Shiny.DocumentDb.Tests/ChangeFeedTests.cs |
Adds native change-feed integration tests. |
tests/Shiny.DocumentDb.Tests/SqliteProviderTests.cs |
Wires observable tests for SQLite. |
tests/Shiny.DocumentDb.Tests/LiteDbProviderTests.cs |
Wires observable tests for LiteDB. |
tests/Shiny.DocumentDb.Tests/PostgreSqlProviderTests.cs |
Wires change-feed tests for PostgreSQL. |
readme.md |
Documents observable stores and native change feeds. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
Comment on lines
+55
to
+58
| return await this.provider.SubscribeChangesAsync( | ||
| tableName, | ||
| typeName, | ||
| async (raw, ct) => |
| // Provision the trigger/function up front so connection or permission errors surface here. | ||
| await this.EnsureChangeFeedTriggerAsync(tableName, cancellationToken).ConfigureAwait(false); | ||
| var channel = ChannelName(tableName); | ||
| return new ChangeFeedSubscription(cancellationToken, token => this.RunListenerAsync(channel, typeName, onChange, token)); |
Comment on lines
+695
to
+696
| return new ChangeFeedSubscription(cancellationToken, | ||
| token => this.RunChangeFeedAsync(container, feedRange, typeInfo, onChange, token)); |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Description of Change
This PR adds comprehensive change notification capabilities to the document store system:
Observable Document Store (
IObservableDocumentStore): In-process change notifications for documents modified through a store instance. Changes are emitted for Insert, Update, Remove, and Clear operations, with transaction support (changes are buffered until commit).Native Change Feed (
IChangeFeedDocumentStore): Server-side change notifications that observe all writers, not just the current store instance. Implemented for:LISTEN/NOTIFYwith row-level triggersCore Types:
DocumentChange<T>: Represents a single change with type, Id, and optional document bodyDocumentChangeType: Enum for Inserted, Updated, Removed, ClearedChangeBroadcaster: Internal fan-out mechanism for in-process notificationsChangeFeedSubscription: Reusable wrapper for background change feed workersExtensions:
ObservableDocumentStoreExtensionsprovides convenience helpers for consuming change streams without System.Reactive dependency.Implementation Updates:
DocumentStore(SQL-based): Publishes changes on all CRUD operations; buffers during transactionsLiteDbDocumentStore: Publishes in-process changes with transaction bufferingCosmosDbDocumentStore: Implements native change feed via Cosmos Change Feed APISupportsChangeFeedproperty andSubscribeChangesAsyncmethodComprehensive Tests:
ObservableTests: 15 tests covering in-process notifications, filtering, transactions, and unsubscribe behaviorChangeFeedTests: Integration tests for native change feeds (external writer detection, disposal)Issues Resolved
None
API Changes
New Public Types:
IObservableDocumentStoreinterfaceIChangeFeedDocumentStoreinterfaceDocumentChange<T>classDocumentChangeTypeenumObservableDocumentStoreExtensionsstatic classSqlServerChangeFeedOptionsclassNew Methods:
IObservableDocumentStore.WhenChanged<T>()→IObservable<DocumentChange<T>>IChangeFeedDocumentStore.SubscribeChanges<T>(...)→Task<IAsyncDisposable>IDatabaseProvider.SupportsChangeFeedpropertyIDatabaseProvider.SubscribeChangesAsync(...)methodModified Implementations:
DocumentStorenow implementsIObservableDocumentStoreandIChangeFeedDocumentStoreLiteDbDocumentStorenow implementsIObservableDocumentStoreCosmosDbDocumentStorenow implementsIChangeFeedDocumentStoreBehavioral Changes
Testing Procedure
ObservableTestssuite (15 tests) to verify in-process change notifications work correctly across all providersChangeFeedTestssuite (3 integration tests) on PostgreSQL and SQL Server to verify native change feed functionalitystore.WhenChanged<T>()and confirm notifications are delivered on CRUD operationsPR Checklist
https://claude.ai/code/session_01QXtq5v6bTDLgncKQowM6YW