Fork me on GitHub
#hyperfiddle
<
2023-01-02
>
denik17:01:49

Having a race condition in photon between client/server state and can’t seem to figure out how to use Unglitch properly

Dustin Getz19:01:14

The first glitch, what is the symptom you seeing? The picklist resultset changes and therefore the record for chosen index shifts?

denik19:01:51

yes, suggestions updates before the on-pick. runs

Dustin Getz19:01:21

ah, the on-pick runs only once (too late), not twice?

Dustin Getz19:01:10

recently-searched-ents is an atom on the server?

denik19:01:32

yes temporarily

denik19:01:51

there is a p/watch on it but in a different view

denik19:01:44

it runs once, even with (reset! !input "") uncommented

denik19:01:12

but it seems that because (reset! !input "") runs, input updates, suggestions are recomputed for input being "" and then those suggestions run in the p/server expression

denik19:01:52

I tried binding a nil return value of the p/server block into the client to sequentialize execution but it did not help

Dustin Getz19:01:01

i almost have it, 1 min

🙏 2
Dustin Getz19:01:19

What happens if you do this:

denik19:01:56

when (reset! !input "") is uncommented it does not work

denik19:01:38

important to note that this bug only happens when input is not= ""

👀 2
Dustin Getz19:01:50

try moving it inside the p/server

Dustin Getz19:01:56

one line down

denik19:01:43

no success

Dustin Getz19:01:10

Is there a way I can run this?

denik19:01:55

it would take a bit of setting up

Dustin Getz19:01:56

(when show-suggestions?
                            (suggestions-fn. (p/Unglitch. input)))

denik19:01:04

that also did not work

Dustin Getz19:01:07

Another option is

(when (p/server
        (let [picked (get suggestions idx)]
          (if picked
            (on-pick. picked)
            #_(on-create. input))
          true))
  (reset! !input ""))
but this will lag your UI

denik19:01:32

I tried this yesterday and it didn’t work but let me try again

Dustin Getz19:01:57

picked (get (p/Unglitch. suggestions) idx) as well, the goal is to slow down the suggestions query as it is updating too fast

Dustin Getz19:01:43

i'll try again later today, i need to eat lunch

denik19:01:21

with the when it runs twice

denik19:01:52

however, it seems only the second execution of on-pick. succeeds

Dustin Getz19:01:10

i dont understand

denik19:01:00

according to logs in the p/server block

Dustin Getz19:01:33

show me your code with logs

denik19:01:01

(do
  (.preventDefault e)
  (println :running-client)
  (when (p/server
          (println :running-server (count suggestions))
          (let [picked (get suggestions idx)]
            (if picked
              (on-pick. picked)
              #_(on-create. input))
            true))
    ;; FIXME can't run this due to glitch bug
    ;; p/Unglitch. is not working
    (reset! !input ""))
  )
;; => 
;; :running-server 2
;; :running-server 20

denik19:01:47

if this doesn’t help I’ll try to modify the Typeahead view this is based on to fail in the same way

Dustin Getz19:01:02

yeah that's where i will start when i try again

Dustin Getz19:01:15

(do
  (.preventDefault e)
  (when (p/Unglitch. input)
    (p/server
      (let [picked (get suggestions idx)]
        (if picked
          (on-pick. picked)
          #_(on-create. input))
        true)))
  (reset! !input ""))

denik19:01:32

also not working unfortunately

denik19:01:48

it’s not urgent

denik19:01:58

I’ll work on runnable repro code soon

xificurC10:01:09

I think this isn't related to the distribution glitch. on-pick and on-create might not finish running because you reset! and cause new reactions. What you're looking for is to run the reset! after the photon function runs. If a photon function transfers it will throw a Pending exception first. We can use a when or case to wait for exceptions to resolve before running subsequent code. Try this version:

(case (if picked (on-pick. picked) (on-create. input))
  (p/client (reset! !input "")))
I find this unreadable since it doesn't capture the intent so I would use a macro like:
(defmacro sequential [& body] (when (seq body) (reduce (fn [ac nx] `(case ~nx ~ac)) (reverse body))))
to write:
(sequential
  (if picked (on-pick. picked) (on-create. input))
  (p/client (reset! !input "")))
It's the same code but more intentive and the macro allows chaining further without nesting, e.g.
(case (foo)
  (case (bar)
    (case (baz)
      (quux))))

;; vs.

(sequential (foo) (bar) (baz) (quux))
Please consider this a hack more than a solution. Our goal is to find patterns where code like this is not necessary. If this still doesn't resolve your issue it'd be great to have a snippet we can run locally 😉

Dustin Getz14:01:41

the problem with "sequential" is it delays the UI from updating locally until after the server effect finishes

Dustin Getz14:01:23

the problem iiuc is when we press enter, we race the clearing of the input (and subsequent refresh of the options due to the input clearing) - we race that with the change handler which is intended to be a discrete side effect that should happen only once. The goal is to make the keyboard-event DAG unmount before the change handler runs again, and unglitch is a way to delay the query reactions in response to clearing the input until after that keyboard-event DAG unmounts.

Dustin Getz14:01:12

Perhaps calling it the d-glitch is not accurate but i think unglitch solves it for now

Dustin Getz14:01:52

obviously i am vulnerable to having missed something because my suggestions didn't work (including the suggestion to sequence the effect and accept a lagged UI)

Dustin Getz14:01:23

we will land a real solution to this along with photon-dom2 and photon-ui3 and the photon runtime fix to the d-glitch, which will all likely come together in about a month

denik21:01:35

@U09K620SG here’s simplified repro of the bug

👀 2
xificurC09:01:58

thanks! I just tried this locally and the temporary fix I posted solves your issue

👍 2
xificurC09:01:29

this makes sure !input is reset only after the callback has run, solving the race. We'll be working on new ui components, including new typeaheads, in the coming weeks

denik18:01:15

thanks @U09FL65DK that fixed it :)

xificurC19:01:09

glad to help!

denik17:01:25

turns out that https://clojurians.slack.com/archives/C7Q9GSHFV/p1672823518093869?thread_ts=1672681969.404549&amp;cid=C7Q9GSHFV actually still runs twice, the reason it works that on the second run picked is nil and therefore on-pick does not re-run

denik17:01:24

i fixed it by putting suggestions into an atom and derefing it instead of using suggestions directly in the callback

👍 2
Dustin Getz17:01:34

that was a great idea

🙏 2
denik17:01:03

see the two fixme’s in the code: