Fork me on GitHub
#datomic
<
2017-07-21
>
matan17:07:48

What are the procedure and "costs" of migrating from one storage backend to another? e.g. between postgres and Cassandra or vice versa?

hmaurer17:07:51

@matan you can restore from a backup on a new storage backend afaik

matan17:07:29

Yep, I guess so. I assume all functionality will be maintained, except that moving from a consistent (postgress) storage to an eventually consistent one (cassandra default configuration) will introduce application bugs if the application assumed strict consistency before.

favila17:07:11

@matan What is your reasoning? datomic hides the inconsistency

favila17:07:29

@matan application should be completely unchanged by the choice of storage for datomic

favila17:07:13

@matan Datomic only mutates a tiny number of records. everything else is immutable, so no opportunity for inconsistency

matan17:07:56

@favila last I asked on the mailing list, it was my understanding of the response I got, that query results will be different based on which cassandra node answered to datomic ― and as I recall the default setup scenario with cassandra is to "commit" a change before all cassandra nodes have updated (maybe I am wrong there) ― I think that with high throughput/activity, I might get different query results depending on which cassandra node answered to datomic

favila17:07:14

I see that mailing list thread

favila17:07:26

the results will not be different

matan17:07:30

To be honest I am not sure I got the correct bottom line on that thread

favila17:07:36

they may merely be not there yet

favila17:07:10

no quorum is needed

favila17:07:20

only one server with a record

favila17:07:54

what may happen is that NO server available to you has the record (in case of network partition)

favila17:07:06

(you = peer)

favila17:07:32

that would be some kind of failure or retry, but the application would not keep going silently with different results

matan18:07:02

So from an application point of view, say I query for all movies (borrowing from the tutorial minimalist scenario), and a movie was added, and it is not there yet when datomic runs the query, on the cassandra node that was used by datomic for this query.

matan18:07:22

Say the movie was added by a user on a front-end part of the overall application

matan18:07:00

If the application can't retreive the movie, this will reflect a wrong "world" from the user's point of view

favila18:07:37

it would not be an alternate world

matan18:07:41

Maybe I should revisit after finishing with the tutorial, which I'm halfway through

matan18:07:13

(agreed, not alternate, only stale in a confusing way for the user)

favila18:07:27

that's not a function of storage

favila18:07:37

that's just speed of light

favila18:07:31

if one peer writes while another peer queries, the querying peer may not know about the latest transaction

favila18:07:46

but its view of the world will not split

favila18:07:19

it will have an exact perfect snapshot of the world at the moment the query started

favila18:07:27

just a few ms behind or whatever

matan18:07:17

Okay, right, I easily flow with the timeline metaphor

matan18:07:28

As an aside, I should de-complect what the user tells the app, and what the app agrees to enter into the world

favila18:07:14

that's not really it

favila18:07:04

the key is there is only one transactor. the transactor writes and informs peers of the latest t

favila18:07:57

the latest t is like a pointer to a shapshot

matan18:07:04

oh, right, so a query coming after the data transacting, would not need to go all the way down to the storage layer, it will get the latest as long as the transactor already finished updating it about the transaction? is that it?

favila18:07:47

yes but that's an optimization (index-merging on the peer)

favila18:07:05

the point is once the peer knows about a T, that T is guaranteed to exist in storage

favila18:07:22

if the peer asks for it and it isn't there, it knows that there is a read failure

matan18:07:17

Though, if the peer forgets about T, because it has been taken out of its cache in order to satisfy some larger query?

matan18:07:00

T being what here?

favila18:07:55

the transaction id

favila18:07:12

the peer does not forget them

favila18:07:41

are you familiar with Clojure?

favila18:07:53

Imagine the entire database is an atom containing {:current-t T :transaction-log [...] :indexes {:eavt [...] :avet [...] :aevt [...] ...}

favila18:07:09

inside the atom is everything, including all history

favila18:07:29

the transactor swap!s into this atom, and shares the returned value with peers

favila18:07:21

so the result of every transaction is an immutable database value with access to all history too

favila18:07:29

inconsistency is impossible

favila18:07:00

the only thing that can happen is the peer doesn't know about the latest db value

favila18:07:17

that just means that its view of the world is behind, but it is still consistent

favila18:07:44

implementation-wise of course this is not how it's done, but operationally that is the experience you get

favila18:07:52

furthermore, that T never changes, so there is no chance that an eventually-consistent storage has different values for the same T

matan18:07:10

I should revisit after being done with the tutorial and having a complete grasp of datums, attributes etc.. the data model. Will be back at that point...

matan18:07:18

@favila many thanks so far

favila18:07:34

@matan this may interest you if you want to know more about internals: http://tonsky.me/blog/unofficial-guide-to-datomic-internals/

matan21:07:12

Well I think I get it. So reading the most current data boils down to chasing the latest time handle (if searching for an entity) or having the right transaction id at hand to begin with, or waiting for them to arrive courtesy of the transactor. Framed as such, the notion of "consistency" is reduced to an easy to satisfy definition, one that is closer to the ACID definition than the CAS one.

matan21:07:28

Which does not make an application using datomic behave consistently without some effort in the form of judicious use of datomic api. I can live with that, maybe, in exchange for not using SQL nor a lame NoSQL database.

hmaurer21:07:51

MongoDB scales though troll

matthavener21:07:52

matan: if you’re worried about a client reading their own writes, you can use d/sync to make sure you read the basis t of the last write from that user

hmaurer21:07:49

@matthavener with postgres as a backend this won’t be an issue, right?

hmaurer21:07:00

only with an eventually consistent store such as dynamo?

matan21:07:36

@matthavener yep, so I came to gather, thanks.

matthavener21:07:46

afaik, you can lag reads on any type of storage

matthavener21:07:17

if you write with peer B and read from peer A, there’s no guarantee that the transactor has updated A with the basis T that was just transacted from B

matthavener21:07:38

(hence, d/sync, which would allow you to force A to sync to some T)

hmaurer21:07:04

@matthavener but from within the same peer, there is guarantee, right?

matthavener21:07:24

I don’t know if that’s a guarantee datomic makes, but it would seem reasonable to me 🙂

matthavener21:07:02

if its the same peer, you can read the value of d/transact‘s future to get the db-after which has that guarantee

hmaurer21:07:35

By the way, quick unrelated question:

hmaurer21:07:58

If I were to write a service whose purpose is to follow the transaction log through the log API and update some internal state

hmaurer21:07:05

how can I make sure that it doesn’t miss a transaction?

hmaurer21:07:19

I don’t think I can rely on transaciton IDs being sequential or anything like that

hmaurer21:07:34

is it possible to get, at every point, the transaction ID that was immediatly preceding it

hmaurer21:07:23

so my service can keep track of the “latest transaction ID it processed”, and before processing a new transaction it can check that the “latest tx ID” it has recorded matches the “previous transaction ID” of the transaction he’s about to process

hmaurer21:07:47

and if not, it would request the log again from the “latest tx id” it has

hmaurer21:07:38

if that makes any sense

hmaurer21:07:09

TL;DR; how do I reliably write a service that follows the transaction log, ensuring that it doesn’t miss any transaction

favila21:07:32

read the tx-report-queue, it never skips a transaction

favila21:07:14

you can get all tx-ids in order by reading (d/datoms db :aevt :db/txInstant)

favila21:07:35

there is unfortunately no cheap way to get the previous tx, because there's no way to walk indexes backward

hmaurer21:07:48

@favila ok, thanks. I wish there was a way for me to manually check it though, e.g. when getting a new transaction to process be able to get a reference to the transaction that immediatly precedes it

hmaurer21:07:54

@favila oh wait; records from the report queue contain “db-before”. Would it be expensive to call basis-t on that, then t->tx ?

favila21:07:30

no, it's just pulling a long out and masking some bits

favila21:07:30

so if you have a log entry, you can do that

favila21:07:44

if you just have a t, you can't easily get previous t

hmaurer21:07:48

@favila masking some bits?

favila21:07:00

well adding some bits in

favila21:07:17

a tx is just a T with the transaction partition bits added

hmaurer21:07:17

what’s the distinction between t and tx id by the way?

favila21:07:30

notice that t->tx and tx->t don't need a database

favila21:07:01

tx->t masks partition bits to 0; t->tx does the opposite

favila21:07:29

it's a special case of d/entid-at

val_waeselynck23:07:33

@hmaurer you can also use the Log API, for an architecture where the consumer decides when it catches up instead of being notified by the tx-report-queue in real-time

matan21:07:51

@hmaurer have you looked at the docs e.g. http://docs.datomic.com/log.html ?

hmaurer21:07:15

I have; I am still unsure how to check that my process hasn’t missed a transaction

favila21:07:48

@hmaurer @matan Again, I don.t see how choice of storage affects anything

favila21:07:59

datomic acts acid even with eventually-consistent storage

matan21:07:05

Thanks, that's clear by now. The ACID definition of consistent is easy to accomplish in this architecture and the paradigm implied by the time-oriented API.

matan21:07:27

Going through the unofficial internals doc suggested above, my thoughts are twofold: 1. Most these things should have been on the official docs, right after the introductory parts. It gives a sense of what performance to expect in different scenarios, and thus whether or how to use datomic for a scenario. Maybe they are already mentioned there. 2. I think datomic has the upper hand on data modelling compared to what else we have out there, but possibly at a dire cost of being prohibitively slower than other options for some standard scenarios, due to all the translation taking place for weaving transactions into non-transactional storage, the unoptimized (?) nature of datalog v.s. SQL, and involving external storage layers which act as databases not just storage, thus incurring additional overhead.. I'd be really happy to see some intelligent benchmarks or refutations of the assumptions sprinkled in this quick note.

hmaurer22:07:08

Datomic’s terms and conditions prohibit the publishing of benchmarks

stuarthalloway22:07:47

“prohibitively slower” and “standard” both would need definition

stuarthalloway22:07:17

there is no doubt that SQL query engines have decades of clever performance optimizations

stuarthalloway22:07:10

and it almost feels like cheating to win some read scenarios via the architectural advantage of immutability + multi-tier caching

stuarthalloway22:07:07

@matan in Datomic, external storages act as block stores, not as databases

stuarthalloway22:07:02

@matan Datomic happily accepts the write overhead needed for ACID transactions, and the application fits and misfits this implies

stuarthalloway22:07:55

@matan I think the important thing missing from your note is the horizontal scaling and caching advantages enabled by peers + immutability

hmaurer22:07:19

@stuarthalloway from your experience, has there ever been cases where the single-writer process becomes a performance problem? And if yes, what would be the recommended way to deal with this?

hmaurer22:07:03

Over the top of my head I thought the application could then be split into multiple databases, which might allow the transactor to operate in parallel

hmaurer22:07:07

although I am not sure it does

stuarthalloway22:07:49

@hmaurer sure! Datomic is not a fit for write scale, as the FAQ states: http://www.datomic.com/faq.html

hmaurer22:07:43

@stuarthalloway Yep I saw that, and I wouldn’t hold Datomic guilty for not dealing with large volumes of write. I am just wondering if there are potential workarounds

stuarthalloway22:07:50

@hmaurer You can split the application into multiple databases with a separate transactor for each. Remember that peers can query (with join!) across databases, and peers do not care what transactor they come from

hmaurer22:07:14

Yep that was my feeling when I discovered peers can join across databases

hmaurer22:07:16

great 🙂

hmaurer22:07:52

Thanks for Datomic by the way, it’s making me enjoy databases again

matan14:07:04

Same here (only the scalability concerns)

hmaurer22:07:25

Also I might just be delusional but I feel I understand its internals much better than other databases I have used before, which is comforting

stuarthalloway22:07:44

If you are looking at that kind of scaling trick, please stay in touch here and/or on the mailing list. Happy to help vet and bench your ideas.

hmaurer22:07:53

Or that, even if I do not understand its internals, I won’t need 10 years of experience to understand them if explained properly

hmaurer22:07:27

Great, thanks! I don’t have any practical application for which I would need to scale writes though; it was just out of curiosity

hmaurer22:07:42

Ah, quick other question while you are here @stuarthalloway 🙂

hmaurer22:07:08

Is there a technical reason why Datomic isn’t accommodating for custom/community-built backup and storage drivers?