Skip to content

Improve notifications, batching and non-blocking views#238

Merged
ChrisPulman merged 2 commits into
mainfrom
Optimise
May 22, 2026
Merged

Improve notifications, batching and non-blocking views#238
ChrisPulman merged 2 commits into
mainfrom
Optimise

Conversation

@ChrisPulman
Copy link
Copy Markdown
Owner

Multiple changes to notification semantics, batching and view construction:

  • Implement INotifyPropertyChanged across QuaternaryBase, ReactiveList and ReactiveGroup and cache common PropertyChanged/NotifyCollectionChanged EventArgs to reduce allocations and ensure Count/Item[] notifications on mutations.
  • Refactor batching: introduce CreateBatch helpers, return PooledBatch with a ReturnToPool flag, and update usages to avoid returning pooled arrays containing live references. PooledBatch now conditionally returns arrays and uses ArrayPoolClearHelper when clearing.
  • Optimize ReactiveList internals: use array copies / AddRange and ReplaceAll for observable backing collections, introduce GetMultisetDifference for Edit diffs, add SetItem helper, use an integer _skipInternalNotifications counter to coordinate internal vs external stream notifications, and raise batched collection/INPC events with fewer allocations.
  • QuaternaryList: coalesce removed items by shard and emit a single batch via EmitRemovedItems, avoiding duplicate index notifications and preserving order.
  • Views: make DynamicReactiveView, DynamicSecondaryIndexReactiveView and DynamicSecondaryIndexDictionaryReactiveView non-blocking at construction by using TryGetLatest to capture an initial filter/keys without blocking on FirstAsync, and adapt subscription handling accordingly.
  • ReactiveListExtensions: make ToChangeSets emit an initial ChangeSet for preloaded sources; improve batch filtering to allocate exact-sized arrays and return non-pooled batches.
  • Core: add CP.Reactive.Internal usage and minor namespace/using adjustments.
  • Tests & benchmarks: add DynamicDataParityBenchmarks and ReactiveListNotificationComplianceTests; add a test asserting Connect emits an initial snapshot; adjust existing tests to ignore the initial Connect snapshot where appropriate.

These changes reduce allocation churn, avoid blocking on subjects at construction, and improve compliance with INPC/INCC contracts while simplifying batch lifecycle handling.

Multiple changes to notification semantics, batching and view construction:

- Implement INotifyPropertyChanged across QuaternaryBase, ReactiveList and ReactiveGroup and cache common PropertyChanged/NotifyCollectionChanged EventArgs to reduce allocations and ensure Count/Item[] notifications on mutations.
- Refactor batching: introduce CreateBatch helpers, return PooledBatch with a ReturnToPool flag, and update usages to avoid returning pooled arrays containing live references. PooledBatch now conditionally returns arrays and uses ArrayPoolClearHelper when clearing.
- Optimize ReactiveList internals: use array copies / AddRange and ReplaceAll for observable backing collections, introduce GetMultisetDifference for Edit diffs, add SetItem helper, use an integer _skipInternalNotifications counter to coordinate internal vs external stream notifications, and raise batched collection/INPC events with fewer allocations.
- QuaternaryList: coalesce removed items by shard and emit a single batch via EmitRemovedItems, avoiding duplicate index notifications and preserving order.
- Views: make DynamicReactiveView, DynamicSecondaryIndexReactiveView and DynamicSecondaryIndexDictionaryReactiveView non-blocking at construction by using TryGetLatest to capture an initial filter/keys without blocking on FirstAsync, and adapt subscription handling accordingly.
- ReactiveListExtensions: make ToChangeSets emit an initial ChangeSet for preloaded sources; improve batch filtering to allocate exact-sized arrays and return non-pooled batches.
- Core: add CP.Reactive.Internal usage and minor namespace/using adjustments.
- Tests & benchmarks: add DynamicDataParityBenchmarks and ReactiveListNotificationComplianceTests; add a test asserting Connect emits an initial snapshot; adjust existing tests to ignore the initial Connect snapshot where appropriate.

These changes reduce allocation churn, avoid blocking on subjects at construction, and improve compliance with INPC/INCC contracts while simplifying batch lifecycle handling.
Add tests and refactor quaternary collections to preserve multiset semantics, maintain an atomic cached Count, and avoid redundant notifications. Key changes:

- Tests: Added coverage for QuaternaryList.RemoveRange with duplicates and QuaternaryDictionary range operations to assert correct counts and notification behavior.
- QuadList: Added RemoveMatching to remove specific counts of duplicate items and optionally collect removed items.
- QuaternaryBase: Introduced a cached _count with SetCount/AddToCount helpers, HasChangeObservers, EmitOwnedBatchRemoved, and updated observable subscription handling to track subscribers. Clear and batch flows now update the cached count.
- QuaternaryDictionary: Update per-operation count adjustments (TryAdd, AddOrUpdate, Remove), more efficient batch removals (no temporary bucket arrays), maintain indices notifications, and set count after edits. Batch removes now only emit when actual removals occurred.
- QuaternaryList: Update Add/Remove paths to adjust cached count, refactor RemoveRange to compute per-shard removal counts, remove matching occurrences (preserving multiset semantics), collect removed items for index notifications, and emit a pooled batch for removed items. Added helpers AddRemoveCount, RemoveFromShards, RemoveFromShard, CopyRemoved and GetCountUnsafe.

These changes fix correctness around duplicate removals, improve performance and memory behavior for batch ops, and ensure Count and reactive notifications stay consistent.
@ChrisPulman ChrisPulman merged commit c9f60d7 into main May 22, 2026
1 check passed
@ChrisPulman ChrisPulman deleted the Optimise branch May 22, 2026 23:10
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant