invisible-college / statebus

All aboard the STATEBUS!!!
118 stars 5 forks source link

Flashbacks #30

Closed tkriplean closed 7 years ago

tkriplean commented 7 years ago

I get lots of flashbacks when dragging a slider, when the slider value gets saved to the server immediately on change. For example, you can see lots of annoying flashbacks when dragging at https://considerit.us:3006/blah.

I created this simple test case for figuring out the problem. Flashbacks aren't as annoying in this test case as at considerit.us, but they still occur pretty frequently.

<script type="statebus"> # -*- mode: coffee -*-

bus.honk = true 

dom.body = ->

  remote = fetch '/remote_slider'
  return SPAN null if @loading()

  # initialize, making sure remote and @local start with same value
  @local.value ?= remote.value or 50
  remote.value ?= @local.value

  # we have a flashback if the remote value ever diverges from the local value
  @local.flashbacks ?= []
  if remote.value != @local.value
    @local.flashbacks.push [remote.value, @local.value]

  DIV null,

    H1 null, 'Local slider'
    INPUT 
      type: "range" 
      ref: 'slider'
      min: 0 
      max: 100
      step: 1
      value: @local.value
      onChange: =>
        @local.value = remote.value = @refs.slider.getDOMNode().value
        save @local
        save remote

    H1 null, 'Server-saved slider value'
    INPUT 
      type: "range" 
      min: 0 
      max: 100
      step: 1
      value: remote.value
      disabled: true

    H1 null, 'Flashbacks'
    for flash in @local.flashbacks
      DIV null, 
        "Remote = #{flash[0]}, Local = #{flash[1]}"

#</script>
<script src="https://stateb.us/client5.js">
</script>
toomim commented 7 years ago

Most flashbacks are now fixed.

I fixed https://considerit.us:3006/blah by passing transactions through the to_save handlers on the server:

client('foo').to_save = (o, t) => {
   // do stuff...
   master.save(o, t)   // Anything else saved in this passes "t" as second arg

   // do stuff
   t.done(o)                 // And when done, use t.done(o) or bus.save.fire(o, t)
}

We should pass these t transactions through handlers as common practice. They tell statebus that the chain of reactions are all part of the same triggering event, which it knows the client has already seen.

And now for some bad news. I tried to prove to myself just now how to use transactions like this to prevent all flashbacks, and found the proof was not super simple, because there are many ways for state changes to trigger other changes to other state. But we eliminate almost all flashbacks right now... so I want to just play it empirically for now— and fix the flashbacks as they come up.

However, I haven't addressed the testcase above

The problem with this testcase is totally different from https://consider.it:3006/blah— it's in the client instead of the server.

Furthermore, it seems to happen only once every ~2000 state changes. It's hard to reproduce, and not of much impact, so I'm just leaving it alone for now.

tkriplean commented 7 years ago

Mike's transaction fix above solved this problem