datascript

Immutable database and Datalog query engine for Clojure, ClojureScript and JS
Tom H. 2021-04-02T13:50:26.023500Z

Athens Research uses posh but it looks like they’re having performance issues https://github.com/athensresearch/athens/pull/784

1🏛️
Saikyun 2021-04-06T08:58:18.064Z

ah, I see. that's too bad

Tom H. 2021-04-07T12:32:10.065Z

no worries 🙂

Tom H. 2021-04-07T12:34:06.065200Z

I’m on the hunt for a performant path from datascript/datahike -> reagent

lilactown 2021-04-07T15:32:13.065400Z

the bottleneck in these situations always seems to be datascript/datahike

lilactown 2021-04-07T15:33:17.065600Z

the power and flexibility that they give you is amazing, but when you have a 16ms budget on the UI thread, you don't always want to spend the bulk of it getting data out of the client side db

1💯
Saikyun 2021-04-08T07:21:23.065800Z

@tomisme I'm guessing one simply has to try with the dataset one is needing. that's what I'm gonna do at least 🙂

1👍
Saikyun 2021-04-08T07:22:42.066Z

@lilactown sounds reasonable. in my case, it seems that using datascript will lead to me doing a bit fewer recalculations. at least to my understanding, the athens solution above doesn't cause rerenders e.g. when filtering away results of transactions.

Saikyun 2021-04-08T07:23:04.066200Z

also, the datasets I'm using aren't that big. we'll see how it goes

Richie 2021-04-02T14:58:12.024200Z

(cljs.pprint/pprint
 (let [conn (ds/create-conn)]
   (ds/transact! conn [{:db/id -1
                        :name "Fred"
                        :friend {:db/id -2
                                 :name "Rich"
                                 }}])
   (ds/datoms @conn :eavt)))
Shouldn’t this create two datoms? I expect that it would resolve the temp id -2 to a new id as well as -1. Instead I get:
(#datascript/Datom [1 :friend {:db/id -2, :name "Rich"} 536870913 true]
 #datascript/Datom [1 :name "Fred" 536870913 true])

Richie 2021-04-02T15:00:25.024700Z

Here’s the return from ds/transact!

{:db-before {},
 :db-after {1 :friend, 1 :name},
 :tx-data
 [#datascript/Datom [1 :name "Fred" 536870913 true]
  #datascript/Datom [1 :friend {:db/id -2, :name "Rich"} 536870913 true]],
 :tempids {-1 1, :db/current-tx 536870913}, 
 :tx-meta nil}

Richie 2021-04-02T15:01:49.025200Z

From the docs on transact! :

; ref attributes can be specified as nested map, that will create nested entity as well
      (transact! conn [{:db/id  -1
                        :name   \"Oleg\"
                        :friend {:db/id -2
                                 :name \"Sergey\"}])

2021-04-02T15:09:21.025300Z

:friend must be declared as :db.type/ref

Richie 2021-04-02T15:11:09.025500Z

Ok, thanks.

Richie 2021-04-02T15:51:51.025800Z

Thanks for setting me on the right track. I didn’t know I needed a schema. I haven’t figured out how to pass it in though. I’ve tried passing it on creaton:

(ds/create-conn {:schema
                             [{:db/ident :friend
                                :db/valueType :db.type/ref
                               :db/cardinality :db.cardinality/one}]})
And I’ve tried passing it to transact!
(ds/transact! conn [ {:db/ident :friend
                         :db/valueType :db.type/ref
                         :db/cardinality :db.cardinality/one}])

Richie 2021-04-02T16:06:23.026Z

Oh.

(cljs.pprint/pprint
 (let [conn (ds/create-conn {:schema
                             [{:db/ident :friend
                               :db/valueType :db.type/ref
                               :db/cardinality :db.cardinality/one}]}
                            )]

   (ds/schema @conn)))
prints
{:schema
 [{:db/ident :friend,
   :db/valueType :db.type/ref, 
   :db/cardinality :db.cardinality/one}]}
… I still have not resolved my original problem though.

Richie 2021-04-02T16:09:29.026200Z

Oh.

(ds/create-conn {:friend
                             {:db/ident :friend
                              :db/valueType :db.type/ref
                              :db/cardinality :db.cardinality/one}})
Ok…

lilactown 2021-04-02T16:57:48.027200Z

it's one of the reasons why I wouldn't recommend it as a catch-all for UI app state

raspasov 2021-04-02T17:07:19.030Z

@lilactown It’s actually kinda slow even with very little data on React Native; I am not sure exactly why. Maybe I don’t fully understand how to use it. If you try to use the indices directly, it’s faster; but any use of (d/q ...) is often quite slow.

raspasov 2021-04-02T17:07:55.030800Z

But otherwise the functionality is very nice, so I’m sticking with… but have to resort to some hacks to make the UI snappy.

raspasov 2021-04-02T17:08:49.031800Z

One idea I’m about to experiment with is treating it as a remote database; basically assume it’s a remote database, and show loaders/spinners everywhere;

raspasov 2021-04-02T17:09:29.032600Z

That obvi. won’t make it faster, but maybe it will “feel” faster 🙂

raspasov 2021-04-02T17:13:27.035700Z

The “hacks” involve having an extra app-state the I render out of, rather than querying directly from Datascript; querying datascript multiple times per second is a big NO-GO

indy 2021-04-02T17:14:13.036200Z

Are you using it with re-frame? Re-frame will run all the queries for every transact, which is what the posh library tried to solve. I was planning to use datascript too but if there is such a perf problem, I'll have to think of something else. Has anyone tried running reframe + datascript in a web worker?

indy 2021-04-02T17:14:36.036800Z

Something like this https://github.com/yetanalytics/re-thread

raspasov 2021-04-02T17:14:51.037300Z

@kslvsunil no, I use React directly; no extra libs

raspasov 2021-04-02T17:15:28.038300Z

If I paint myself in the corner, at least I have a chance of understanding how I got there! haha 🙂

indy 2021-04-02T17:15:35.038400Z

Oh dang, then it's going to much much slower with re-frame.

raspasov 2021-04-02T17:15:50.039Z

No idea, never used re-frame/reagent.

indy 2021-04-02T17:15:52.039200Z

I wonder how roam research uses datascript

raspasov 2021-04-02T17:15:56.039300Z

Only om.next back in the day.

raspasov 2021-04-02T17:17:32.040800Z

And while I haven’t used them, it’s hard to say whether they are faster or slower; but it will most likely be more difficult for me to analyze the whole thing.

raspasov 2021-04-02T17:18:07.041100Z

Web worker is something I’ve thought about;

raspasov 2021-04-02T17:18:38.042Z

It will make it “faster” in the sense that it won’t block your JS UI thread, but the results will still often take 100+ ms to arrive;

raspasov 2021-04-02T17:19:23.042900Z

So you’re definitely in the realm of what I wrote just above: you’d have to treat DataScript as a fully remote database and never even pretend that it’s local (it will definitely be all async)

1😔
raspasov 2021-04-02T17:20:36.043900Z

Basically, anytime things take more than 16ms in JavaScript, you run into UI smoothness trouble (1000 / 60fps = 16 ms)

indy 2021-04-02T17:26:54.048Z

Yup agree completely to "It will make it 'faster' in the sense that it won’t block your JS UI thread, but the results will still often take 100+ ms to arrive;". There is also lilactown's https://github.com/lilactown/autonormal which I was recommended in the asami channel. Guess I'll start spiking out with autonormal. All I want is a well structured normalized app state, which I could probably tame by having some discipline. But the problem is every time I want a new state slice, I'll have to think about the structure of it, it's nesting level, yada yada, which is time consuming and burns a lot of cognitive energy unnecessarily

2021-04-05T10:11:22.063300Z

FYI there is also this: https://github.com/wotbrew/idx/ Not sure how they compare. cc @lilactown

1👍
lilactown 2021-04-05T17:07:40.063600Z

My 15 min review: on the surface, idx implements custom data structures which implicitly handle indexing and have a bunch of additional helper functions for doing a bunch of additional operations on those data structures, like sorting, matching, and others. autonormal uses clojure.core datastructures (maps/vectors/sets) to store your data, so the normalization and indexing is transparent to the developer, and does not give you any additional tools for sorting or matching this. autonormal also has an EQL engine for querying maps (including following references created by autonormal). idx does not provide a way of declaratively, recursively querying arbitrary EDN

1👍
lilactown 2021-04-02T18:31:40.049700Z

autonormal won't give you the full power of datalog, but on the other hand because it's not tuple-based it should be much faster

lilactown 2021-04-02T18:32:20.050500Z

I benchmarked it early on against datascript and mapgraph and it was orders of magnitude faster than DS. I've added some features after that that may have slowed it down a bit. I should redo those benchmarks and publish them

lilactown 2021-04-02T18:34:05.051800Z

React Native could probably get away with treating datascript as a "remote" database since JS runs on a separate thread from the actual UI, but the browser cannot; if transacting takes 250ms then that's 250ms that the UI thread isn't doing anything else (like responding to clicks or key presses)

lilactown 2021-04-02T18:34:39.052200Z

oh I see you mentioned webworker @raspasov. yes. then it's measuring the cost of serialization

lilactown 2021-04-02T18:37:26.053400Z

I think that the idea of shoving all app state into a single store is naive anyway. DS in a webworker for domain state might be good enough and open up other novel things like sharing that state across browser windows

1