Fork me on GitHub
#hyperfiddle
<
2023-05-19
>
braai engineer09:05:12

Tonsky mentions Electric in latest blog post: https://tonsky.me/blog/humble-signals/

braai engineer11:05:09

Why can’t I e/watch a DataScript conn? It’s an atom, no?

xificurC11:05:50

yes, a datascript connection can be watched. Can you paste a snippet or have a repo to share?

braai engineer11:05:27

Can it be watched on the client, when conn is not on the server, but in :cljs land?

Dustin Getz11:05:39

(e/client (let [x (e/server (e/watch !d-conn))] ...))

Dustin Getz11:05:07

the connection is an unserializable reference, the watch must colocate with the conn on the server

braai engineer11:05:10

My mistake - I had a bad conn value caught in a defonce.

braai engineer11:05:08

I was getting “not Watchable” exception on (e/client (binding [db (e/watch conn)] ..., where conn is on client because conn was a bad value.

braai engineer13:05:45

Would help if the exception showed the unwatchable value, i.e. “nil”. Would have caught it instantly that way (maybe it does and I missed it)

Dustin Getz13:05:23

ok, saved this for later

braai engineer13:05:25

Any idea why I would get these frontend exceptions when I submit a tx on the the server?

(AccountPicker.
                      (:xt/id account)
                      (e/fn [new-account]
                        (log/debug "New value:" id new-account)
                        (e/server
                          (e/discard
                            (log/debug "move entry..." id new-account)
                            (data/bulk-move-entries! !xtdb [id] new-account)))
                        ;(reset! !editing? false)
                        (swap! !open-pickers disj id)))

braai engineer13:05:16

(the server tx is successful btw. and everything seems to update)

Dustin Getz13:05:53

i need to see the full context, i.e. everything

Dustin Getz13:05:59

Could your FE and BE be out of sync?

Dustin Getz13:05:40

It might be (new nil) as well

braai engineer20:05:15

How would I know if BE & FE are out of sync? Electric would know on WS (re)connect, no? I still get these exceptions sometimes and I doubt it’s because BE & FE are out of sync coz they happen even if I do a force refresh on client. There is probably a bug somewhere, but I can’t tell yet if it’s me or Electric. Will try to reproduce cleanly when I have time.

Dustin Getz22:05:16

i can take a look, can you send me a git clone link? will it be easy to run?

braai engineer22:05:08

@U09K620SG I think I found the cause of this: my index.html in transplanted app had,

<script type="text/javascript" src="/main.js"></script>
`instead of
<script type="text/javascript" src="$:hyperfiddle.client.module/main$"></script>
…which for some weird reason worked in local dev (not sure how). Haven’t verified the errors are totally resolved, but would explain why FE & BE could be out of sync.

👀 2
braai engineer14:05:36

Should I be doing,

(e/defn MyThing []
  (binding [!xtdb user/!xtdb
          db    (new (db/latest-db> user/!xtdb))] ...)
at top of every signal fn that needs db access, or just once in a parent component? Is there a cleaner way to pass in the node or db signal as an argument? Will MyThing be torn down every time db changes if it is passed down as argument?

braai engineer14:05:01

The db/latest-db> helper seems create a new listener for every signal, so each component would have a listener. How would I share this signal between components? Maybe a !db atom that gets updated and e/watch that? From Electric XTDB starter app:

(new (db/latest-db> user/!xtdb))
Source:
(defn latest-db>
  "return flow of latest XTDB tx, but only works for XTDB in-process mode. see
  "
  [!xtdb]
  (->> (m/observe (fn [!]
                    (let [listener (xt/listen !xtdb {::xt/event-type ::xt/indexed-tx :with-tx-ops? true} !)]
                      #(.close listener))))
    (m/reductions {} (xt/latest-completed-tx !xtdb)) ; initial value is the latest known tx, possibly nil
    (m/relieve {})
    (m/latest (fn [{:keys [:xt/tx-time] :as ?tx}]
                (if tx-time (xt/db !xtdb {::xt/tx-time tx-time})
                            (xt/db !xtdb))))))

Dustin Getz19:05:25

I don't fully understand your question/problem

Dustin Getz19:05:52

You can pass dbvals by lexical or dynamic scope, both are efficient, neither will needlessly reboot your components

Dustin Getz19:05:46

Dynamic scope is idiomatic in Electric (specifically moreso than in Clojure, where dynamic scope has too many gotchas)

braai engineer12:05:43

How do I pass DataScript DB values as signal fn arguments? E.g.

(e/defn MyThing [ds-db]
  (e/server
    (let [results (ds/q '[:find …] ds-db)]
      ...)))
It seems to want to transfer the DataScript DB over the wire. Or do I just need to define ds-db as nil in :cljs land?

Dustin Getz12:05:16

You need to "bias" any functions with server dependencies so that they are called from the server, then the database parameter won't transfer. This is a long standing issue that we can fix later this year

Dustin Getz12:05:35

the rule today is, Electric functions are "called" from one place, which means all of their parameters must be moved to the place where it is called. So for example in (e/client (MyThing. (e/server db)), db will transfer to the client even if it is not actually used on the client inside MyThing.

Dustin Getz12:05:55

This is wrong obviously and needs to be fixed.

braai engineer12:05:48

How do I “bias” the function with server dependencies to prevent the transfer of server parameters? Do I need to use a binding at the top of the function?

Dustin Getz12:05:13

by designing it to be called from the server, it is the call site that matters

braai engineer12:05:23

so e/for-by (if looping over stuff) needs to be in e/server? E.g.

(e/server
  (e/for-by :xt/id [row [1 2 3]]
    (MyThing. db id))
? Does that still work with e/client stuff in MyThing?

Dustin Getz12:05:55

> Does that still work with e/client stuff in MyThing yes

braai engineer13:05:12

What are the performance implications of running all my “row” loops on the server? Does the client still know the playbook? Any additional overheads?

Dustin Getz13:05:44

the compiler will make it fast

Dustin Getz13:05:28

i think this tutorial attempts to explain what you need to know - https://electric.hyperfiddle.net/user.demo-system-properties!SystemProperties

👍 1
Dustin Getz13:05:45

"Reactive for details" and "Reasoning about network transfer"

Dustin Getz13:05:05

also https://electric.hyperfiddle.net/user.demo-todos-simple!TodoList has close to this pattern (except the database is passed by dynamic scope rather than lexical, which we recommend you do but it's not mandatory)

braai engineer14:05:01

The db/latest-db> helper seems create a new listener for every signal, so each component would have a listener. How would I share this signal between components? Maybe a !db atom that gets updated and e/watch that? From Electric XTDB starter app:

(new (db/latest-db> user/!xtdb))
Source:
(defn latest-db>
  "return flow of latest XTDB tx, but only works for XTDB in-process mode. see
  "
  [!xtdb]
  (->> (m/observe (fn [!]
                    (let [listener (xt/listen !xtdb {::xt/event-type ::xt/indexed-tx :with-tx-ops? true} !)]
                      #(.close listener))))
    (m/reductions {} (xt/latest-completed-tx !xtdb)) ; initial value is the latest known tx, possibly nil
    (m/relieve {})
    (m/latest (fn [{:keys [:xt/tx-time] :as ?tx}]
                (if tx-time (xt/db !xtdb {::xt/tx-time tx-time})
                            (xt/db !xtdb))))))