Simplify MongoDB code by just storing whole state

- Every time it receives a state, it blows away the existing one and
  puts in the new one.
- There's a non-zero probability of losing the entire state if the
  server fails after blowing away the previous state and before
  inserting the new.
This commit is contained in:
Nicholas Kariniemi 2014-10-04 23:20:55 +03:00
parent 48ba2c5449
commit 86bfdf80e4
4 changed files with 21 additions and 51 deletions

View file

@ -73,14 +73,14 @@
(reset! index-page prod-index-page) (reset! index-page prod-index-page)
(let [to-db (chan)] (let [to-db (chan)]
(db/connect-production-database to-db mongo-url) (db/connect-production-database to-db mongo-url)
(state/init-server to-db (db/get-current-grubs) (db/get-current-recipes)) (state/init-server to-db (db/get-current-state))
(println "Starting production server on localhost:" port) (println "Starting production server on localhost:" port)
(start-server port))) (start-server port)))
(defn start-development-server [{:keys [port]}] (defn start-development-server [{:keys [port]}]
(let [to-db (chan)] (let [to-db (chan)]
(db/connect-development-database to-db) (db/connect-development-database to-db)
(state/init-server to-db (db/get-current-grubs) (db/get-current-recipes)) (state/init-server to-db (db/get-current-state))
(println "Starting development server on localhost:" port) (println "Starting development server on localhost:" port)
(start-server port))) (start-server port)))

View file

@ -1,5 +1,6 @@
(ns grub.db (ns grub.db
(:require [grub.util :as util] (:require [grub.util :as util]
[grub.sync :as sync]
[monger.core :as m] [monger.core :as m]
[monger.collection :as mc] [monger.collection :as mc]
[monger.operators :as mo] [monger.operators :as mo]
@ -7,54 +8,22 @@
(def conn (atom nil)) (def conn (atom nil))
(def db (atom nil)) (def db (atom nil))
(def grub-collection "grubs") (def collection "grub-lists")
(def recipe-collection "recipes")
(def production-db "grub") (def production-db "grub")
(def development-db "grub-dev") (def development-db "grub-dev")
(defn clear-grubs []
(mc/drop @db grub-collection))
(defn clear-recipes []
(mc/drop @db recipe-collection))
(defn clear-all [] (defn clear-all []
(clear-grubs) (mc/drop @db collection))
(clear-recipes))
(defn update-db! [{:keys [grubs recipes]}] (defn update-db! [state]
(let [deleted-grubs (:deleted grubs) (mc/drop @db collection)
updated-grubs (->> (:updated grubs) (mc/insert @db collection state))
(seq)
(map (fn [[k v]]
(-> v
(dissoc :id)
(assoc :_id k)))))
deleted-recipes (:deleted recipes)
updated-recipes (->> (:updated recipes)
(seq)
(map (fn [[k v]]
(-> v
(dissoc :id)
(assoc :_id k)))))]
(doseq [g deleted-grubs]
(mc/remove-by-id @db grub-collection g))
(doseq [g updated-grubs]
(mc/update-by-id @db grub-collection (:_id g) g {:upsert true}))
(doseq [r deleted-recipes]
(mc/remove-by-id @db recipe-collection r))
(doseq [r updated-recipes]
(mc/update-by-id @db recipe-collection (:_id r) r {:upsert true}))))
(defn get-current-grubs [] (defn get-current-state []
(->> (mc/find-maps @db grub-collection) (let [state (first (mc/find-maps @db collection))]
(sort-by :_id) (if state
(map #(clojure.set/rename-keys % {:_id :id})))) (dissoc state :_id)
sync/empty-state)))
(defn get-current-recipes []
(->> (mc/find-maps @db recipe-collection)
(sort-by :_id)
(map #(clojure.set/rename-keys % {:_id :id}))))
(defn connect! [db-name mongo-url] (defn connect! [db-name mongo-url]
(if mongo-url (if mongo-url
@ -65,8 +34,9 @@
(defn connect-and-handle-events [to-db db-name & [mongo-url]] (defn connect-and-handle-events [to-db db-name & [mongo-url]]
(a/go-loop [] (a/go-loop []
(if-let [diff (<! to-db)] (if-let [state (<! to-db)]
(do (update-db! diff) (do (println "DB got new state")
(update-db! state)
(recur)) (recur))
(println "Database disconnected"))) (println "Database disconnected")))
(let [_conn (connect! db-name mongo-url)] (let [_conn (connect! db-name mongo-url)]

View file

@ -65,7 +65,6 @@
(def states (atom [])) (def states (atom []))
(def empty-state sync/empty-state) (def empty-state sync/empty-state)
;; TODO: Remove watch, close up channels properly
#+clj #+clj
(defn sync-new-client! [>client <client] (defn sync-new-client! [>client <client]
(let [client-id (java.util.UUID/randomUUID) (let [client-id (java.util.UUID/randomUUID)
@ -85,8 +84,9 @@
(a/close! state-change-events))))) (a/close! state-change-events)))))
(make-server-agent client-events >client states))) (make-server-agent client-events >client states)))
(defn init-server [to-db grubs recipes] #+clj
(reset! states (sync/initial-state grubs recipes)) (defn init-server [to-db initial-state]
(reset! states (sync/new-state initial-state))
(add-watch states :to-db (fn [_ _ old-states new-states] (add-watch states :to-db (fn [_ _ old-states new-states]
(a/put! to-db (sync/get-current-state new-states))))) (a/put! to-db (sync/get-current-state new-states)))))

View file

@ -35,7 +35,7 @@
(doseq [grub grubs] (doseq [grub grubs]
(test/is (taxi/find-element driver {:value grub}) (test/is (taxi/find-element driver {:value grub})
"Previously added grubs should be loaded on refresh"))) "Previously added grubs should be loaded on refresh")))
(db/clear-grubs)) (db/clear-all))
(defn test-added-grubs-sync [url driver1 driver2] (defn test-added-grubs-sync [url driver1 driver2]
(taxi/to driver1 url) (taxi/to driver1 url)
@ -75,7 +75,7 @@
(defn start-db-and-websocket-server! [] (defn start-db-and-websocket-server! []
(let [to-db (chan)] (let [to-db (chan)]
(db/connect-and-handle-events to-db "grub-integration-test") (db/connect-and-handle-events to-db "grub-integration-test")
(state/init-server to-db (db/get-current-grubs) (db/get-current-recipes)))) (state/init-server to-db (db/get-current-state))))
(defn run [] (defn run []
(println "Starting integration test") (println "Starting integration test")