Fork me on GitHub
#datomic
<
2017-05-16
>
wistb03:05:05

is datomic a good fit for managing trees of data (predominently). There are relations between the data at different levels too.

val_waeselynck05:05:37

@wistb so DAGs actually :)

val_waeselynck05:05:10

I'd sat definitely yes for reading

val_waeselynck05:05:49

To be seen for writing

val_waeselynck05:05:37

Would be easier if you gave us examples of data, reads and writes

wistb06:05:45

have you seen anyone replacing a hibernate layer with datomic ? Our situation is like that. we use spring/jpa/hibernate/postgress/oracle. In the interest of keeping the business logic in tact, we are wondering if we can replace the ORM with datomic ....

val_waeselynck07:05:09

@wistb AFAIK there are no Datomic ORMs (Datomic is not 'R', and its community is not too fond of 'O' 😉 ). Reimplementing all of JPA on top of Datomic would probably be a huge endeavour IMO. Out of curiosity, what leverage do you expect from Datomic if you're planning on 'hiding' it behind a JPA-like interface? Application logic is where the specifics of Datomic usually shine (datalog, rules, pull, entity API, non-remote querying etc.)

val_waeselynck07:05:44

Although I do understand the desire to make a smooth transition

augustl07:05:01

facts map to objects better than tables I suppose, so it should definitely be doable

val_waeselynck07:05:42

the read interface shouldn't be too hard, I guess you can have each Entity class have a private datomic.Entity field and implement the getters on top of that

val_waeselynck07:05:56

writing is probably more problematic

Petrus Theron10:05:07

(Datalog beginner) Is there a reason why Datomic/Datomic does not support hash-map return values, e.g. [:find {:id: ?e :title ?title} :where [?e :product/title ?title]] => [{:id 1789... :title "Title 1"} ...] My first guess would be to do with subquerying and set uniqueness

karol.adamiec10:05:30

use [:find [(pull ?e [:id :title]) …]

Petrus Theron12:05:10

Date/instant attribute naming best practice: :thing/arrival-date vs :thing/arrived-on vs :thing/arrived-at?

souenzzo13:05:14

#subscribe this doubt

val_waeselynck14:05:20

I have a slight preference for :thing/arrival-date which is more informative re: type.

val_waeselynck14:05:42

The noun vs verb debate is not a big deal IMHO - clarity and searchability are the important concepts

devth16:05:53

I intermittently get:

Exception in thread "main" clojure.lang.ExceptionInfo: Error communicating with HOST 0.0.0.0 or ALT_HOST datomic on PORT 4334
when a peer / kubernetes replica starts up and attempts to connect, using SQL storage with pro license. sometimes kubernetes will restart it up to 4 times after it crashes, but it always eventually connects. is this typically thrown if it can't establish network or are there other reasons? should my peer retry a few times before crashing?

favila16:05:25

That means peer connected to storage, but could not connect to transactor via the provided "host=" or "alt-host=" config values set in the transactor's transactor.properties file @devth

devth16:05:48

got it. strange, since i know the transactor is up.

favila16:05:50

the host=0.0.0.0 sounds just bad

favila17:05:11

alt-host=datomic relies on "datomic" resolving correctly

favila17:05:19

(from the peer's perspective)

devth17:05:31

i think 0.0.0.0 is because i don't know the IP it will get

devth17:05:37

and datomic does resolve from the peer

devth17:05:42

(kubernetes dns)

favila17:05:46

0.0.0.0 is only for peers

favila17:05:53

so it's not really useful

favila17:05:58

unless the peer is on the same machien

devth17:05:24

so i guess it's not used at all

favila17:05:39

nm, I take that back

favila17:05:47

it might be used for the bind address

devth17:05:01

i thought i needed it to be 0.0.0.0 in order to work, but it's been awhile since i first set it up on k8s

devth17:05:36

might try wrapping it in a retry

devth17:05:42

don't understand why it intermittently fails

favila17:05:04

do you know that datomic is resolveable right away?

favila17:05:10

the name "datomic" I mean

favila17:05:18

resolveable and routable

devth17:05:26

it should be but i should verify that

favila17:05:38

how is storage resolved? also a kubernetes dns name?

devth17:05:39

kubernetes pods are configured to use an internal dns server

devth17:05:52

storage (sql) goes through a proxy (google cloud sql proxy) on localhost to the cloud mysql instance

devth17:05:27

dns is configured automatically on google container engine. not something i touch

favila17:05:38

so the connection string is always something like datomic:?

favila17:05:23

and the proxy has an IP address, or a name?

devth17:05:10

datomic:
that's what i'm using

devth17:05:17

MYSQL_HOST is always localhost:3306

devth17:05:33

which is a process running inside the pod using a docker image provided by google

devth17:05:50

it uses credentials to find and access the cloud sql instance

devth17:05:56

given a resource name

favila17:05:58

what I mean is, how does the proxy get the destination address? I am seeing if these are determined via different systems, which would make it possible that one system is up and the other is not yet

devth17:05:21

yeah two separate systems

favila17:05:25

e.g. if proxy forwarded to hardcoded IP (likely for google cloud mysql), then maybe dns is just not up yet

devth17:05:47

i don't know how google's cloud sql proxy works internally

devth17:05:28

if anything i would suspect kubernetes dns resolution

devth17:05:43

the proxy has worked flawlessly for other apps

devth17:05:54

i can ping datomic on startup to see if it resolves to an IP

devth17:05:06

i wish there was an http endpoint on the transactor i could curl though

favila17:05:14

yes the proxy is definitely working

favila17:05:32

that's how the peer got the "datomic" name in the first place

favila17:05:47

interesting I didn't know about this proxy

favila17:05:02

we use ssl auth directly with could mysql

favila17:05:46

yeah I looked it up

favila17:05:13

I suspect it uses the gcloud http apis

favila17:05:23

but no idea how those resolve

devth17:05:38

right. might be internal dns inside google network

favila17:05:54

magic *.internal names even

wistb18:05:30

@val_waeselynck I liked this : "Application logic is where the specifics of Datomic usually shine (datalog, rules, pull, entity API, non-remote querying etc.)" . It is nice take-away point .

eoliphant19:05:27

Hi, I have an architecture question. I’ve a Datomic/Clojure based microservice. Some of its transactions are ‘domain events’ that I want to shoot off to kafka. I identify them with an attribute on the relevant transaction entity I’d been playing around with Onyx, it’s cool, but I’m starting think it might be overkill for this use case which is really just grabbing relevant transactions, mapping their attributes to the event structure and sending them on their way to kafka. I’ve been looking at datomic’s TX-report-queue, as it looks like a listener on that guy would pretty much meet my needs. But i’m not clear on some of its semantics. it seems like each peer gets it’s own queue? if so then I’d potentially be processing (num peers) copies of the same transaction/event.

kenny20:05:03

If every entity needs a globally unique identifier, do you guys prefer to add it under an entity namespaced attribute (e.g. for your product entity :product/id or organization entity :organization/id) or globally understood name (e.g. every entity have the attribute`:entity/id`)?

augustl20:05:07

I prefer to have separate ID attributes per "type" of entity, because it allows me to query for them more easily

augustl20:05:33

I don't have to "duck type", I can just look specifically for :product/id 123. Otherwise I'd have to query for :generic-id 123 and then later figure out if facts for that entity is of the type I expect

kenny20:05:56

@augustl Wouldn't your query effectively be the same either way?

'[:find ?name
  :in $ ?id
  :where
  [?e :product/id ?id]
  [?e :product/name ?name]]

'[:find ?name
  :in $ ?id
  :where
  [?e :entity/id ?id]
  [?e :product/name ?name]]

augustl21:05:11

I guess it depends on how you like to structure your queries, yeah

augustl21:05:21

I like to just query for the id and then build entity objects, not pull out attributes in the query

kenny21:05:09

Still seems pretty similar 🙂

(:product/name (d/entity db [:product/id my-id]))
(:product/name (d/entity db [:entity/id my-id]))

augustl21:05:36

with the latter, you wouldn't know what "type" of entity you would get though

augustl21:05:03

so your URL could be /people/5 and that could be an ID of a product, not a person, and you'd still get data from the query

kenny21:05:33

Isn't the type implicit with the entity attributes? i.e. Because the entity has the :product/name attribute it is therefore a Product entity.

kenny21:05:00

It wouldn't make much sense if a Person had a :product/name attribute.

augustl21:05:43

in that case, where you only ask for the product name, that makes sense

augustl21:05:57

but if you want a full collection of attributes you'd just end up with a sloppy system, I'd say

augustl21:05:09

where you would get a person with the id 5 (even though 5 is a product) and all the attributes would be nil

augustl21:05:15

makes more sense to 404 in that case

kenny21:05:18

I think you'd still be covered tho':

(let [{:product/keys [id] :as e} (d/pull db '[*] [:product/id my-id])]
  (if id
    {:status 200 :body e}
    {:status 404}))

(let [{:product/keys [name] :as e} (d/pull db '[*] [:entity/id my-id])]
  (if name
    {:status 200 :body e}
    {:status 404}))

augustl21:05:52

if all your "product" entities has a name attribute, that would work, yeah

kenny21:05:39

And you wouldn't end up with a map of nils. From the docs: > attribute specifications that do not match an entity are omitted from that entity's result map

augustl21:05:14

I guess it's just a matter of taste

augustl21:05:34

checking for a "name" feels like duck typing, and I'd prefer an "actual" type

kenny21:05:38

I think I see what you're getting at: you just want a globally consistent key to check for an entity's type?

augustl21:05:34

and you can use lookup refs more easily

augustl21:05:03

(d/entity db [:product/id "123"])

kenny21:05:32

How's that different than (d/entity db [:entity/id my-id])?

augustl21:05:56

the built in type check 🙂

augustl21:05:17

no additional check needed to verify that it's actually a product

kenny21:05:17

IIRC, d/entity will always return an entity even if the entity does not exist. So, you'd still need the if.

kenny21:05:11

Yeah, you'd still need to check if pulling :product/id off the entity was nil before proceeding.

souenzzo21:05:26

(transact conn (into #{[:db/add (d/tempid :db.part/tx) :audit/user x] [:db/add (d/tempid :db.part/tx) :audit/ns y]} other-tx)) resolve both tempid to the same "transact-id". It's a feature? Should I use it??

favila21:05:49

@souenzzo there can only be one tx tempid id per tx. It's handled specially

favila21:05:13

@souenzzo you can also use the string "datomic.tx" in recent datomics

souenzzo21:05:12

"datomic.tx" is a special string that generates :db.part/tx tempid?

favila21:05:54

you can now use a string as a tempid, normally the tempid is in the :db.part/user partition, except "datomic.tx", which is in the :db.part/tx partition

favila21:05:25

it is a tempid which always resolves to the currently executing transaction entity