question-mark
Stuck on an issue?

Lightrun Answers was designed to reduce the constant googling that comes with debugging 3rd party libraries. It collects links to all the places you might be looking at while hunting down a tough bug.

And, if you’re still stuck at the end, we’re happy to hop on a call to see how we can help out.

Beta dependency tracking executes watchers with inconsistent values

See original GitHub issue

Version

2.5.17-beta.0

Reproduction link

https://jsfiddle.net/w5d9gqmo/3/

Steps to reproduce

In provided example:

  • open console
  • hit increment
  • observe computed value evaluations in console

In vue terms:

  • create dependency structure with many paths to the same watcher (A->B, B->C, A->C)
  • Notify leaf watcher about change (data change)
  • Observe too eager synchronous updates of watchers on every intermediate change

What is expected?

All updated computed values are executed once and with correct dependant values.

twoCounts evaluated to: {count: 2, countPlusOne: 3}

What is actually happening?

Computed values are executed multiple times with all intermediate dependency values.

twoCounts evaluated to: {count: 2, countPlusOne: 2}
twoCounts evaluated to: {count: 2, countPlusOne: 3}

The issue is clearly caused by this commit: https://github.com/vuejs/vue/commit/653aac2c57d15f0e93a2c1cc7e6fad156658df19

I did some debugging and found the cause. Dependencies are notified synchronously about the watcher update in the notify loop, but the updating watchers might be accessed too early with stale value and no information about necessary recomputation.

I attempted a fix by splitting Dep.notify into two phases with separate loops for dirtify and update. It worked for simple situations where there is triangle of dependencies (countPlusOne in the example), but it stops working when the graph is any more complicated (paths are larger than 1). This is illustrated by countPlusTwo computed in the example.

Possible full fix would involve either traversing the full dependency graph to dirtify all deep dependants first, or collecting the the array of dependants of my dependants like in previous implementation.

Issue Analytics

  • State:closed
  • Created 5 years ago
  • Reactions:4
  • Comments:9 (3 by maintainers)

github_iconTop GitHub Comments

4reactions
Frizicommented, Oct 10, 2018

@indirectlylit

In an ideal world though what I’d love is kind of a combination that gets used under-the-hood: eagerly set ‘dirty’ flags without recomputing, and lazily recompute dirty values when requested.

This is exactly what’s happening in standard computed values 😄

Computed values are lazy watchers, which mean that on dependency change, a dirty flag is set. https://github.com/vuejs/vue/blob/0737d11a146818d975266953ee547a12a63a1a41/src/core/observer/watcher.js#L159-L172

Once a value is requested, the cached one is used or it is recomputed when dirty. https://github.com/vuejs/vue/blob/0737d11a146818d975266953ee547a12a63a1a41/src/core/instance/state.js#L244-L255

The important part is that transitive dependencies are always treated like direct dependencies. In means if a computed value A calls computed value B, all dependencies of B are copied to A. This is done like so, because when B’s dependency change and you request value of A, it has to be recomputed or it risks being outdated.

https://github.com/vuejs/vue/blob/0737d11a146818d975266953ee547a12a63a1a41/src/core/observer/watcher.js#L214-L222

Changing that behaviour to first check if B was actually changed is not that simple, as you have to keep that deferred up to the point when A is requested (or any other dependant computed value), while caring about A to not recompute if possible. I think that this is achievable, but the complexity is much bigger and in many cases it might turn out that benefits outweighs the costs, both in complexity and speed. In today’s hardware, the memory fetch is usually the slowest action you can take, so caching every trivial computation like that can actually be a serious performance hog. There is still a place for methods 😄

4reactions
LinusBorgcommented, Sep 18, 2018

Most importantly, the original behavior does not lead to duplicated computation even in the worst case scenario, while the now-reverted behavior could lead to potentially much more wasted CPU cycles. So we are reverting this change.

While that’s probably true statistically, it’s still unfortunate for situations where a computed property does some heavy calulation over and over because a previous computed property often updates, but always returns the same value.

I guess in these situations the user must do some manual caching in this computed property, or use a memoized method instead?

Read more comments on GitHub >

github_iconTop Results From Across the Web

Tracking Failure Origins - The Debugging Book
Such dependencies are crucial for debugging, as they allow to determine the origins of individual values (and notably incorrect values). To determine dynamic ......
Read more >
Inconsistent dependency when i do terraform apply from plan ...
Firstly, an up-to-date dependency lock file should be recorded in your version control system so that your automation is only reinstalling ...
Read more >
Unity 2022.2.0b15
VFX Graph: Fixed an issue that values modified in spawner or init context automatically trigger a reinit of the attached VFX component.
Read more >
@sharekey/meteor-desktop - npm
Start using @sharekey/meteor-desktop in your project by running `npm i @sharekey/meteor-desktop`. There are no other projects in the npm registry using ...
Read more >
Aha! Big Ideas
Releases · View release phases by weeks rather than dates · Make it harder to accidentally delete release phase dependencies in "release detailed"...
Read more >

github_iconTop Related Medium Post

No results found

github_iconTop Related StackOverflow Question

No results found

github_iconTroubleshoot Live Code

Lightrun enables developers to add logs, metrics and snapshots to live code - no restarts or redeploys required.
Start Free

github_iconTop Related Reddit Thread

No results found

github_iconTop Related Hackernoon Post

No results found

github_iconTop Related Tweet

No results found

github_iconTop Related Dev.to Post

No results found

github_iconTop Related Hashnode Post

No results found