Repository navigation
Releases: dXnSix/dXn.Reactive
Releases · dXnSix/dXn.Reactive
Release list
v1.1.0
Features
- (repository) rename the crate to dxn_reactive
Migration: depend ondxn_reactive = { git = "https://github.com/dXnSix/dXn.Reactive", tag = "v1.1.0" }instead ofrayx_reactive, and replacerayx_reactive::paths withdxn_reactive::.
v1.0.0
The first tagged release. Before it, consumers built the untagged 0.1 sources; the breaking changes
below are relative to those sources, each with a Migration: note.
Delivery
- Fixed: a subscriber panic no longer stalls its delivery queue. The panic still propagates to
the thread draining the queue, but the items queued behind the panicking one, and later ones,
are delivered to every live subscriber (including new ones) by the next publication. This
applies to every signal, event bus and collection source. - Changed: subscribers of one queue are invoked in subscription order (previously unspecified),
and a subscriber disposed on the delivering thread, including by another subscriber while the
same item is in flight, is never invoked again. - Changed:
Signal::set/replace/updateandEventBus::emitdeliver in commit order when
several threads write concurrently. - Added:
DeliveryQueue::push,DeliveryQueue::drainandDeliveryQueue::pending_len.
enqueuekeeps its contract (pushfollowed bydrain).
Errors and interop
- Breaking:
ReactiveErrorhas a new variantExternal(Arc<dyn Error + Send + Sync>)carrying
an error from another layer. Migration: add a wildcard or anExternal(_)arm to exhaustive
matches onReactiveError. - Added:
impl From<CollectionError> for ReactiveError(PoisonedstaysPoisoned, every
other variant becomesExternalholding the original), so?on collection subscriptions works
insidewhen_activatedcallbacks.CollectionError::from(ReactiveError::External(..))recovers a
wrapped collection error; other external errors map toPoisoned. - Added:
ValueStream::map_errandValueStream::err_into, andReactiveError::external. - Breaking (generic error parameters):
ReactiveValueStreamExt<T, E = ReactiveError>and
OptionValueStreamExt<T, StreamError = CollectionError>are implemented for streams of any error
type, andEffect::watchaccepts&ValueStream<T, E>and returnsResult<Effect, E>.
Migration: code that built a stream withValueStream::new(..)and relied on a scalar
operator to infer the error type now needs an annotation (ValueStream::<T, ReactiveError>::new)
orValueStream::from_reactive. - Breaking (generic error parameters): every collection operator that takes a stream (sort
comparers and resort triggers, page/virtual requests includingsort_and_*,batch_if/buffer_if
pause streams, regroupers, group selectors, forced-transform triggers, tree predicates,switch,
populate_from*, theto_*_change_set*andedit_diff*bridges, buffered change-set streams)
and every per-item selector returning one (filter_on_observable,auto_refresh*,
transform_on_observable,group_on_observable,merge_many*,when_*value_changed*,
true_for_*,query_when_changed_on*) acceptsValueStream<_, E>with
CollectionError: From<E>. Passsignal.values()directly; thecollection_streamadapter shown
in earlier docs is no longer needed. Two-stream operators
(sort_*_dynamic_with_resort,sort_and_*_dynamic*,group_dynamic_with_regrouper) take an
independent error type for each stream. Migration: existing call sites keep compiling, except
a stream built inline withprimitives::ValueStream::new(..)(the crate-root one) whose error
type was inferred from the operator: annotate it (ValueStream::<_, CollectionError>::new) or
build it with thecollections::ValueStreamalias, which fixesCollectionError. - Added:
Computed::from_stream(&stream, initial)holds a stream's latest value as scalar state
(for example a collectioncount()drivingCommand::with_can_execute). - Added:
ValueStreamcombinators onReactiveValueStreamExt:merge,combine_latest,
with_latest_from,switch_map,scan,take,first,take_until,pairwise,
distinct_until_changed_by,shareandshare_replay.
Collection sources
- Breaking:
CollectionErrorhas a new variantContended. Migration: add a wildcard or a
Contendedarm to exhaustivematches onCollectionError. - Fixed (behavior): cross-thread contention no longer fails with
NestedUpdate. On native
targets edits, reads,connect,lookup,watch*,suspend_notificationsandcount_changed
wait for the lock, so concurrent writers and readers all succeed; onwasm32they return
Contendedwithout blocking and without leaving partial subscriptions.NestedUpdatenow means
same-thread re-entry only, and is also returned (instead of deadlocking) for reads such as
items()/len()/count()/keys()/version()andsuspend_notificationsmade from inside the
source's own edit closure, key selector or aT::clonerunning under its lock. The same applies
to live groups (RayListGroup,RayCacheGroup). Migration: retry loops onNestedUpdatefor
contention are no longer needed. - Fixed:
changes(),preview(),connect()andcount_changed()deliver batches in commit
(version) order with several writer threads; an edit made inside apreview()callback is
delivered after the batch that triggered it. - Fixed:
count_changed()registers before reading its baseline: counts changed during the
initial callback or concurrently are reported, and the last reported count equalslen()/count(). - Fixed:
connect(),watch(key),watch_value(key)andwatch_optional(key)subscribed while
notifications are suspended no longer receive changes their snapshot already covers when the
suspension ends. The resumed publication is still one publicChangeSet. - Fixed: dropping the last notification-suspension guard always resumes publication, also when
it is dropped inside the source's own edit or while another thread holds the source under the
non-blocking policy (the resume is applied when the lock is released). - Fixed: a live group connection made between a grouping operator staging a change and
publishing it no longer receives that change twice, and disposing a keyed watch whose subscriber
owns another watch on the same cache no longer deadlocks. - Documented: native targets now wait on contention, so accessing source B from inside source
A's edit closure while another thread does the reverse can deadlock; keep one global order. - Added: updater reads (
ListUpdater::items/len/is_empty/get,
CacheUpdater::lookup/contains_key/count/keys) reflecting the edit's own changes, making
read-modify-write inside oneeditatomic. - Added:
SourceList::get/contains/read,SourceCache::contains_key/read(with the new
CacheView), the same onObservableList/ObservableCache, andSourceCache::from_items. - Changed (behavior):
watch_valueandwatch_optionalemit at most one value per batch, the
key's final state in it (aloadthat keeps the key emits only the new value, with noNone
flicker), andChangeStream::watch(key)forwards aClearwith only the watched key's entry.
Materialization and composition
- Fixed:
populate_into,populate_from*, theto_*_change_set*adapters,
ObservableList/ObservableCache::from_stream(andas_observable_*) and the source size limits
no longer discard a batch whose destination edit fails: the batch is kept and retried, in order,
when the destination's lock is next released (and in any case before the next batch). A failure
that cannot clear (Poisoned) is reported and its batch dropped. A retry runs on the thread that
releases the lock, so the destination's subscribers can be called from that thread. - Added:
populate_into_with_errors,populate_from_with_errors,
populate_from_batches_with_errors, theto_list_change_set*_with_errors/
to_cache_change_set*_with_errorsadapters,limit_size_to_with_errorsonSourceListand
SourceCache, andObservableList::errors()/ObservableCache::errors(), which report each
failed attempt. - Fixed: the source size limits (
SourceList/SourceCache::limit_size_to) recompute an eviction
whose edit failed instead of queueing it, so an item is never evicted twice or in place of another,
and emit only the items actually removed. - Fixed:
ref_countrolls back a subscriber whose upstream connection failed; a subscriber that
joined from another thread meanwhile waits for the outcome and connects again itself (on wasm32 it
getsContendedinstead of waiting). A late subscriber's snapshot always comes before live
batches, and batches from a released upstream are ignored. - Fixed:
switchdelivers its output in order and never concurrently, never forwards a
superseded inner's batches after itsClear, and leaves no inner subscription after disposal.
Added:switch_with_errorsreports an inner subscription that failed; the switch retries when
the outer stream emits again.
Operator emission
- Fixed: operators with several inputs (dynamic filters, sorts, page/virtualize and group
operators,*_with_resort,*_with_regrouper,sort_and_*_dynamic*, all joins, static and
dynamic combiners, forced transforms,transform_to_tree_with_predicateand
merge_many_*_change_sets) no longer deliver batches out of order or concurrently when their
inputs are driven from different threads; each subscription has one ordered output, possibly
delivered by the thread that is already draining it. The live groups created by the group
operators and the right groups of*_join_manyare notified the same way: their own streams
receive batches in computation order, after the operator released its lock, so a group's
subscriber may edit the operator's inputs (it used to deadlock*_join_many). - Fixed:
reverse,virtualize,group_by,ensure_unique_keys, cachetransform_manyand
collect_update_statsno longer call their subscrib...