Fork me on GitHub
#hyperfiddle
<
2023-06-01
>
Amos09:06:09

Hi, I’m looking into the xtdb starter code. I wonder why we have to define a latest db like https://github.com/hyperfiddle/electric-xtdb-starter/blob/ffe3ed23cc51e7dd7a001263b52d52a6fd00738a/src/app/todo_list.cljc#L69, since the xtdb tutorial doesn’t do this.

Dustin Getz10:06:41

it’s reactive, that function is subscribing to the stream of tx changes and using that to automatically refresh the db val

👍 2
braai engineer13:06:52

Any known issues with slow Internet causing Electric Websocket to drop on timeout and render a blank screen? Requires reload to recover:

2023-06-01T11:07:44Z app[3d8d1ddbe43e89] jnb [info]ERROR hyperfiddle.electric-jetty-adapter: Websocket handler failure
2023-06-01T11:07:44Z app[3d8d1ddbe43e89] jnb [info]clojure.lang.ExceptionInfo: Websocket pong timeout.
2023-06-01T11:07:44Z app[3d8d1ddbe43e89] jnb [info]	at hyperfiddle.electric_jetty_adapter$make_heartbeat$cr7506_block_4__7516.invoke(electric_jetty_adapter.clj:22)
2023-06-01T11:07:44Z app[3d8d1ddbe43e89] jnb [info]	at cloroutine.impl$coroutine$fn__5168.invoke(impl.cljc:60)
2023-06-01T11:07:44Z app[3d8d1ddbe43e89] jnb [info]	at missionary.impl.Sequential.step(Sequential.java:88)
2023-06-01T11:07:44Z app[3d8d1ddbe43e89] jnb [info]	at missionary.impl.Sequential$1.invoke(Sequential.java:112)
2023-06-01T11:07:44Z app[3d8d1ddbe43e89] jnb [info]	at missionary.core$absolve$fn__5532$fn__5533.invoke(core.cljc:141)
2023-06-01T11:07:44Z app[3d8d1ddbe43e89] jnb [info]	at missionary.impl.RaceJoin.terminated(RaceJoin.java:51)
2023-06-01T11:07:44Z app[3d8d1ddbe43e89] jnb [info]	at missionary.impl.RaceJoin$1.invoke(RaceJoin.java:71)
2023-06-01T11:07:44Z app[3d8d1ddbe43e89] jnb [info]	at missionary.impl.RaceJoin$2.invoke(RaceJoin.java:86)
2023-06-01T11:07:44Z app[3d8d1ddbe43e89] jnb [info]	at missionary.impl.Sleep.trigger(Sleep.java:63)
2023-06-01T11:07:44Z app[3d8d1ddbe43e89] jnb [info]	at missionary.impl.Sleep$Scheduler.run(Sleep.java:35)

braai engineer14:06:55

Hmmk, blank screen issue might just be OOM.

Dustin Getz18:06:07

Yeah this is the 60 second timeout, if a heartbeat is missed for 60 seconds the connection is dropped

Dustin Getz18:06:36

I believe the starter app is configured to auto-reconnect

Dustin Getz18:06:10

Yes, the page will teardown (turn white) on disconnect (losing state), and then boot again on reconnect. In the future, it won't lose client state, rather it will preserve the state and gracefully reconnect as if nothing had happened – this feature is blocked on the big refactor that Leo has been working on

👍 4
Dustin Getz18:06:30

However making an interactive app with forms and such work gracefully despite intermittent connectivity is a tall order, essentially asking for offline-first software. This is something we're thinking a lot about for electric-ui5 which we've been designing, however I can't promise we will solve the problem of offline-first as that's not actually the problem we're trying to solve, we're actually working on optimistic updates which is closely related to offline first but not quite the same

👍 2
2
braai engineer09:06:12

Thx. Possible to throw an Exception like Pending when Websocket is down so I can catch it and disable things on UI, but preserve read-only state? E.g. (try … (catch Offline ex …)) ? I’m okay with Electric throwing an exception if it can’t run a Fn that happens on the server.

Dave Mays18:09:29

If offline-first worked with Electric it feels like it would be the best of all worlds. I know the focus is internal web apps, but offline first opens the doors to all kinds of things eventually like mobile apps. Im curious though, would this involve somehow getting XTDB (or some other DB) to work in the client as well as the back end, and sync between them? I'd love an offline-first capable datalog DB, and haven't found anything except for those below. Is a client-side XTDB part of what would be required for optimistic updates? https://www.instantdb.com/ (Datahike seems to have left the client side for now, and Datascript only seemed maybe made for this?)

Yab Mas15:06:48

I'm trying to better understand what does/doesn't result in efficient network-traffic/rendering but I'm not really comprehending the behaviour I see. The following code is adapted from your system-properties example:

#?(:cljs (def !search (atom "")))

(e/def search (e/client (e/watch !search)))

(e/defn ActiveSearch []
  (e/client
    (let [
          ;search (e/watch !search)
          result (e/server (take 10 (when (seq search) (search-by search))))]
      (dom/div
        (ui/input search (e/fn [v] (reset! !search v))
                  (dom/props {:type "search" :placeholder "Search by"}))
        (when (seq result)
          (dom/ul
            (e/for-by identity [item result]
                      (dom/li
                        (dom/text item))))))))) 
Like this the code runs fine, but if I do the e/watch inside the let-binding (like is now commented-out) I get obvious flickering and the inline-style trick shows that the results are indeed rerendered on every change. What's the big difference between these scenario's?

2
Dustin Getz18:06:35

adding line numbers

Dustin Getz18:06:05

on L9, result is in client scope, so result transfers at this point

Dustin Getz18:06:58

the pattern you want is this:

(e/server
  (e/for-by [item (take 10 (when (seq search) (search-by search)))]
    (e/client (dom/text (pr-str item)))))
here, each individual item is in server scope on L2 and transfers as an individual item on L3, resulting in streamy network

Yab Mas18:06:29

Yeah thats actually the understanding I had, it's just not the code I would have written myself if I hadn't seen the demo. So I tried to verify that what I would write indeed doesn't work efficiently but got confused by the results I got. Looking at it again I think I now understand why it seemed to work efficiently to me. I concluded this because the added inline-style stayed on the item even after getting new results. But I guess e/for didn't update the item because the key didn't change but the whole result-set is still transfered over the network on every change. Is that right? That doesn't explain the other behaviour I reported though. If I uncomment line 8 I get obvious flickering and the inline-style goes away on every change, I don't have that if I use the global definition of line 3. Is that to be expected?

Dustin Getz19:06:12

You observe a difference in behavior when swapping L3 for L8, one flickers the other doesn't?

Dustin Getz19:06:50

That surprises me, i'll need to repro that

Yab Mas19:06:29

Ok. This was on an up-to-date version of the starter-app.

Dustin Getz19:06:14

Can you send me your repo link that can repro the issue?

Yab Mas19:06:29

I guess it also has to do with the when, if I remove that I don't get the flickering

Dustin Getz19:06:14

This is what I see :

Yab Mas19:06:52

yeah, just press any number

Yab Mas19:06:53

Yes... If I press 1, add an inline style on item-12, than press 2, it's gone

Yab Mas19:06:09

If I use the global search-def it stays

Dustin Getz19:06:54

oh by "flickering" you mean "it lost the style" ?

Yab Mas19:06:30

No I also see flickering, but that's a bit less concrete than the example I gave now

👀 2
Dustin Getz20:06:42

Ok I repped and confirm it's the when , im not sure exactly what's happening, it seems like a very subtle race condition

Dustin Getz20:06:08

Regardless the answer here is to fix the e/for usage to have efficient transfer, do you have that working now?

Yab Mas20:06:53

Yes, got it working and have a better understanding of how/why

Dustin Getz20:06:35

I think the when is trashing the body when the test blinks Pending

Dustin Getz20:06:07

the question is really, why isn't there a Pending when we use the global def vs the local def

Amos16:06:04

I’m trying to look into the starter code from xtdb. I found a e/for-by function, which is (e/for-by :xt/id [{:keys [xt/id]} (e/offload #(todo-records db))] (TodoItem. id)) . I found it difficult to understand the syntax of the api. 1. What is the :xt/id means that next to the e/for-by? 2. In (TodoItem. id) , where is the id comes from, is this the special name when you are using e/for-by?

2
alexdavis16:06:48

id comes from the {:keys [xt/id]}

alexdavis16:06:15

its the syntax for destructuring namespaced keywords in clojure

Dustin Getz18:06:05

if you have used React.js, :xb/id is used here in a way that is similar to "react key"

👍 2
Amos01:06:04

Thank you. But I wonder why it will be come`id` not xt/id . For example, in https://github.com/hyperfiddle/electric/blob/15fb0219ff0d4daf507bd513750c698502700d2a/scratch/dustin/y2023/pokemon.md?plain=1#L29, it will be display_name after using the :key to destructuring.