samedhi / firemore

Firebase + Clojure -> Firemore
https://firemore.org/
MIT License
19 stars 4 forks source link

Better transactions #43

Closed samedhi closed 4 years ago

samedhi commented 4 years ago

I am looking at the code that you have written for doing transactions. It currently looks like the following:

(transact-db!
  [{anakin-midichlorians :midichlorian} [:characters "anakin"] ;; 27700
   {padme-midichlorians  :midichlorian} [:characters "padme"]] ;;  4700
     (let [midichlorians-average (/ (+ padme-midichlorians anakin-midichlorians) 2)]
       (sut/set-db! reference {:midichlorian midichlorians-average})
       (str "midichlorians count is " midichlorians-average)))

I am suggesting that it be moved to the following form.

(transact-db!
  (async/go
    (let [{anakin-midichlorians :midichlorian} (async/<! (sut/get [:characters "anakin"])) ;; 27700
            {padme-midichlorians  :midichlorian} (async/<! (sut/get [:characters "padme"])) ;; 4700
            midichlorians-average (/ (+ padme-midichlorians anakin-midichlorians) 2)]
       (sut/set-db! reference {:midichlorian midichlorians-average})
       (str "midichlorians count is " midichlorians-average)))))

And that we then further override transact-db! to really just wrap a go block and to tree walk the entire body, replacing every (sut/get ...) with (async/<! (sut/get ...)).

PROBLEM: I was thinking that transact-db would also set the bindings, but now that I think about it that is problematic as async is run in (conceptually) a thread pool. Bindings don't necessarily "survive" park events, etc.

SOLUTION: Ok, so I guess I need to make :transaction a key in the third argument we are talking about passing to all the functions (get, set!, add!, and update!). Not only will it asyng/<! all the get but will also merge in the third {:transaction<TRX_OBJ} argument to all of them. I think we are also going to need a :transaction-reads and :transaction-writes passed to each function so that the closing function can know which transactions it has not actually written to the server and write them itself.

POSSIBILITY: Maybe there is a way to query the transaction itself so that we don't need to keep track of reads and writes ourselves.

(transact-db!
  (let [{anakin-midichlorians :midichlorian} (sut/get [:characters "anakin"]) ;; 27700
          {padme-midichlorians  :midichlorian} (sut/get [:characters "padme"]) ;; 4700
          midichlorians-average (/ (+ padme-midichlorians anakin-midichlorians) 2)]
       (sut/set-db! reference {:midichlorian midichlorians-average})
       (str "midichlorians count is " midichlorians-average)))