Fork me on GitHub
#hyperfiddle
<
2024-03-26
>
Andrew Wilcox08:03:59

After writing this, it occurred to me that a simpler approach than this sfn implementation might be to use a standard reactive fn, and then sample the fn value at the time an event occurs.

Dustin Getz14:03:32

this is clearly skilled work, can you tell me, what are you trying to achieve by avoiding atoms?

Dustin Getz14:03:57

your goal is an alternative way to subscribe to dom events with snapshot semantics built in?

Dustin Getz14:03:33

or are you trying to achieve a specific unmounting-related problem that e/on-unmount doesn't help with?

Andrew Wilcox00:03:20

My goal at the moment is understanding the execution model 🙂 In the previous thread where I was asking if the way I was using atoms was correct, the answer was "yes, but due to very specific details of the execution model and if I make small changes it might break instead". So then I asked, well, do I actually need atoms? At all? Could I write code without atoms? If I did, would I find it easier to understand the guarantees? There's the Java/JavaScript imperative model, the Clojure immutable data plus vars/refs/agents/atoms model, and the Electric reactive model. Whenever I write code to involves multiple models there's a greater chance that I'm going to get confused because I need to understand not only the models involved but also how they interact. If I have reactive state and I don't need to put it in an atom, if I have some reactive state that can happily live entirely within Electric, that seems like it could at least potentially be easier to debug: I just need to figure out what I'm doing wrong within Electric as opposed to Electric plus atoms plus the interaction between the two. I don't think I'll end up using sfn as written, I think there may be other ways of doing that sort of thing if I end up wanting to for some reason... but I do find it interesting that son! only needs to attach the event listener at mount and remove it at unmount while dom/on! reattaches the listener when the fn updates. I don't know that it makes a practical difference, I'm not saying that there's anything wrong with reattaching the event listener on reactive updates... but, in terms of coupling between Electric and imperative JavaScript, being able to attach the event listener once seems like it could be a better fit... just, you know, probably not with sfn specifically, as that's pretty awkward. Unmount was just an interesting edge case. I was generating and consuming an event stream, and one of the events I was generating was on unmount, but of course when I was processing events at the point of the unmount event the umount event wasn't getting processed because it was getting unmounted. So I was curious if I could rescue this by processing the event flow somewhere else that wouldn't be unmounted when the observed element was unmounted. Which I don't know if I'd ever need to want to do, but good to know that it's possible if I ever might want to for some reason 🙂

👍 1
Hendrik17:03:11

Hi, I have questions about server backpressure. I have this snippet:

(e/client
   (let [!state (atom "")
         state (e/watch !state)]
     (ui/input "" (e/fn [v] (reset! !state v)))
     (e/server (println state))
     (println state)))
Now, I disable networking in chrome’s dev tools. Then I enter “asdfasdf” into the input element. Now I turn on networking again and only then
; a
; as
; asd
; asdf
; asdfa
; asdfas
; asdfasd
; asdfasdf
is printed in the server console. What, if I only care about the latest value of state on the server after network is turned on again. How, can I achieve that?

Vincent01:03:49

(ui/input (e/fn [] ...) ...) right?

Vincent01:03:51

you find backpressure undesirable is what you are saying

Vincent01:03:02

you want just the latest row in that output list is what you are saying

Vincent01:03:36

ui/input will emit on every on-change, you need to rely on a different element and a different event

Hendrik07:03:22

I want to discard values on network unavailability. This is what I found out from the docs: In case of network unavailability the websocket buffer is filled and only then backpressure is propagated. The starter app sets the buffer to 100 MB, so it is unlikely to fill it. But even then you would only discard values, which are generated after the buffer was filled up. Then I realized, that you cannot distinguish between slow network and network unavailability. So I came up with this solution so far:

(e/server (let [tx-flow (->> (e/fn [] tx)
                                      (debounce 10)
                                      (m/reductions {} nil)
                                      new)] ...)
with the idea that pilled up messages come in fast, when network is up again. In general I wanted to solve the problem, that I want to persist an input field with a cas transaction [:cas eid old-val new-val] and the initial value shown in the input field is the latest db value. However, I realized that I have to deal with this client side and implemt some rollback behaviour

Vincent09:03:29

cool. maybe important yea

Dustin Getz12:03:01

I think this kind of thing should be done in the Electric network layer automatically. The server will see each message sent from the client (i.e. websockets are discrete), but if many changes to the same value arrive synchronously (i.e. network is "turned back on" and queues flush all at once) it does seem reasonable to flatten them, and it is semantically reasonable due to electric's continuous time semantics

Dustin Getz12:03:32

In other words, maybe we should log this as a bug

Dustin Getz12:03:15

Electric v3 is coming which rebuilt the network layer and removed a ton of technical debt, maybe ask us again after v3 is delivered

🔥 2
👍 1