-
Notifications
You must be signed in to change notification settings - Fork 23
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Implement transactional updates #2
Comments
The way to do this is to update the dependency graph in a breadth-first order; i.e., don't update a dependent observable until all of its dependencies have finished processing their changes. However, rather than doing this by making the dependency graph between observables explicit, I plan to implement this ordering by complicating the way changes get communicated. I'm switching over to a scheme where observables send not just changes, but change events: public enum ChangeEvent<Change: ChangeType> {
case willChange
case didNotChange
case didChange(Change)
}
public protocol ObservableType {
associatedtype Change: ChangeType
/// The current value of this observable.
var value: Change.Value { get }
/// A source that reports changes to the value of this observable.
var changeEvents: Source<ChangeEvent<Change>> { get }
} Implementation work is progressing on the The solution resembles a two-phase commit process. Whenever a
Derived observables subscribe to these notifications. When they receive a While a transaction is in progress, intermediate values must not appear in the |
So it turns out that consistent reads are way too complicated to implement for collections. However, inconsistent reads (and even inconsistent change notifications) are not the real issue — the real issue is the lack of signaling of transaction boundaries. We may allow observables to temporarily get into inconsistent state, as long as they clearly communicate this, and they let us know when it's safe to retrieve their values again. The updated plan for transactional updates is to simply send a So the
Of course, the original extension ObservableType {
/// A source that reports changes to the value of this observable.
/// Changes reported correspond to complete transactions in `self.updates`.
public var changes: Source<Change> {
return Source { sink in
var merged: Change? = nil
return self.updates.connect { event in
switch event {
case .beginTransaction:
assert(merged == nil)
case .change(let change):
if merged == nil {
merged = change
}
else {
merged!.merge(with: change)
}
case .endTransaction:
if let change = merged {
sink.receive(change)
merged = nil
}
}
}
}
}
} This mechanism is now (partially) implemented on the |
Fixed by PR #4 |
Given an observable integer
a
, we can define the observable sum of adding it to itself:The value of
sum
seems to track the original observable correctly:However, if we look into the changes reported by
sum
, we find something surprising: apparently the result of adding an integer to itself can temporarily look like an odd number?!The reason for this is quite simple: as
a
changes from 1 to 2, the plus operator ina + a
receives two changes, for both of its dependencies. It doesn't know these changes aren't independent, so it reports two separate changes to itself, going from 2 to 3, then 3 to 4.These spurious notifications can cause issues when we build complex observable expressions. At the very least, they mean more changes are generated than strictly necessary, costing performance. But the real problem is that such temporary values aren't valid, and shouldn't be reported at all.
The text was updated successfully, but these errors were encountered: