You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
The polyfill doesn't blackhole a Computed until producerRecomputeValue is called. But evaluation of other computations can happen before that, while polling producers for changes. This can lead to a situation where:
In a previous run, we created Computeds A and B with A depending on B.
We're currently making sure A is up to date, after making a change that will cause B to need to rerun. We get to a state that looks like:
A'sproducerUpdateValueVersion
A's consumerPollProducersForChange
B's producerUpdateValueVersion
B's producerRecomputeValue
At this point, if B's compute function calls A.get(), we don't see this as a cycle. Instead we go through this process again, polling A's producers, discovering that B needs to rerun, and trying to recompute B... which finally throws.
In essence, producerUpdateValueVersion produces a stack with two types of relevant frames in it:
consumerPollProducersForChange, when we don't know whether a node is dirty and have to poll its producers;
producerRecomputeValue, when we do know we have to recompute.
But only producerRecomputeValue is blackholing the Computed. This seems too late to me; by the time we ask for A while A is already doing producerUpdateValueVersion we know something has gone wrong. B's reevaluation should throw as soon as it reads A, IMO. As far as I can tell the current state isn't lax enough to construct a true cycle, but the fact that we don't catch the cycle until the second time through is likely to be observable in debugging tools/error messages.
I have a test case in 406f7ac to use for debugging, but the assertion isn't ideal -- we don't have enough information on the exception to easily tell that it threw too late/for the wrong reasons. Technically A should run twice -- the second time it'll just get the (already-computed) cached exception when it reads B. (It's enough to get to vitest --inspect-brk --no-file-parallelism and see what's on the stack, though, so I thought it was worth sharing.)
The text was updated successfully, but these errors were encountered:
The polyfill doesn't blackhole a Computed until
producerRecomputeValue
is called. But evaluation of other computations can happen before that, while polling producers for changes. This can lead to a situation where:producerUpdateValueVersion
consumerPollProducersForChange
producerUpdateValueVersion
producerRecomputeValue
A.get()
, we don't see this as a cycle. Instead we go through this process again, polling A's producers, discovering that B needs to rerun, and trying to recompute B... which finally throws.In essence,
producerUpdateValueVersion
produces a stack with two types of relevant frames in it:consumerPollProducersForChange
, when we don't know whether a node is dirty and have to poll its producers;producerRecomputeValue
, when we do know we have to recompute.But only
producerRecomputeValue
is blackholing the Computed. This seems too late to me; by the time we ask for A while A is already doingproducerUpdateValueVersion
we know something has gone wrong. B's reevaluation should throw as soon as it reads A, IMO. As far as I can tell the current state isn't lax enough to construct a true cycle, but the fact that we don't catch the cycle until the second time through is likely to be observable in debugging tools/error messages.I have a test case in 406f7ac to use for debugging, but the assertion isn't ideal -- we don't have enough information on the exception to easily tell that it threw too late/for the wrong reasons. Technically A should run twice -- the second time it'll just get the (already-computed) cached exception when it reads B. (It's enough to get to
vitest --inspect-brk --no-file-parallelism
and see what's on the stack, though, so I thought it was worth sharing.)The text was updated successfully, but these errors were encountered: