Implement update echo messages in a backward-compatible way
See original GitHub issueI think we can implement the update echo feature from https://github.com/jupyter-widgets/ipywidgets/pull/3195 and further discussed in https://github.com/jupyter-widgets/ipywidgets/pull/3343 in a backward-compatible way.
To summarize, when the kernel receives an update message, it will apply the message and then always broadcast out a message to clients, where some of the attribute updates in the broadcast message may be designated as echoed updates. Clients ignore echoed updates until their own update is echoed back to them. They should ignore because it lets them optimistically update their own UX. Essentially, seeing the echo of their own update lets them know the order in which the kernel is applying updates from multiple clients, and ignore those echoed updates if it knows that the kernel still has to apply its own update.
If a client does not ignore echo updates until it sees its own update echoed, and if the client is optimistically updating its own UX to reflect its update, we will have jitter. For example, imagine two clients displaying the same slider model, and both sliders are being dragged, and slider A sends a message updating the value to 5 while slider B sends a message updating the value to 10. Each slider is updated to its own update value, but we don’t have consensus on what the kernel is going to pick. The kernel processes these messages in order, and sends out slider A’s update as an echo, then slider B’s update as an echo. Slider A receives its own echoed update, then B’s echoed update, and applies B’s update since it came after its own echo. However, slider B receives A’s echoed update and ignores it, since it knows the kernel has yet to apply its own update. Then slider B receives its own echoed update, which it can ignore since it has already updated the UX optimistically for that value. The end result is that both clients and the kernel have agreed that B’s update wins.
We said this protocol wasn’t backwards compatible since if B doesn’t know to ignore comm echo updates, it will jitter since it will apply A’s update, then apply its own echo update. The real problem, though, is that the echo updates and normal updates are intermingled in the single key in the update message, so a client that doesn’t know about comm echo updates cannot distinguish between the two types of updates.
However, what if we send echoed state updates in their own echo_state
key, rather than conflating it with the normal state
key for normal updates from the kernel? Then I think things are backwards compatible - clients that don’t know about the echoed updates will ignore any echoed updates, so there is no change in their behavior. Clients that understand the echoed update messages can implement this consensus protocol from above. I think in this case, we can implement this logic in 7.7, to give it to people that won’t be able to update to 8.0 right away.
Disclosure: At Databricks, my current employer, we’d like to get this feature in 7.7 if we can, which led me to reevaluating the problem to see if there was a way we could do it in a backwards compatible way.
Issue Analytics
- State:
- Created 2 years ago
- Reactions:2
- Comments:11 (10 by maintainers)
Top GitHub Comments
In other words, I’m suggesting that the protocol be amended this way:
Synchronizing multiple frontends:
update
with echoStarting with protocol version
2.1.0
the kernel can send a special update message back, to allow all connected frontends to be in sync with the kernel state. This allows multiple frontends to be connected to a single kernel but also resolves a possible out of sync situation when the kernel and a frontend send out an update message at the same time, causing both to think they have the latest state.In protocol version
2.1.0
the kernel is considered the single source of truth and is expected to send back to the frontends an update message that contains an extra state update dictionary indicating which state updates are just reflections of frontend state updates.The
state
dictionary contains updates directly from the kernel, whileecho_state
contains updates from other frontends. If the same attribute is in both dictionaries, thestate
update takes precedence.state
must be present, but may be empty, andecho_state
is optional.In situations where a user does many changes to a widget on the frontend (e.g. moving a slider), the frontend will receive from the kernel many update messages (with the
echo_state
key containing echoed updates) from the kernel that can be considered old values. A frontend can choose to ignore all updates inecho_state
that are not originating from the last update it send to the kernel. This can be implemented by keeping track of themsg_id
for each attribute for which we send out an update message to the kernel, and ignoring allecho_state
updates as a result from anecho
for which themsg_id
of the parent header is not equal tomsg_id
we kept track of.For situations where sending back an echo update for a property is considered to expensive, we have implemented an opt-out mechanism in ipywidgets. A trait can have a
no_echo
metadata attribute to flag that the kernel should not send back an update to the frontends. We suggest other implementations implement a similar opt-out mechanism.@maartenbreddels already did some good work on this, much appreciated.