summaryrefslogtreecommitdiff
path: root/src/weepinbell/storage/worker.clj
blob: 1ce367582ce0e5b54eb1c32137e630714528c111 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
(ns weepinbell.storage.worker
  "Storage worker is a background loop that handles write operations 
   to the storage, which makes write/delete/update thread-safe."
  (:require [clojure.core.async :refer [go-loop chan put! <! <!! close!]]
            [taoensso.timbre :as timbre]))

(defn- start-worker
  "Asynchrnously start worker loop that takes from writer-chan 
   and writes to storage when operation is received.
   Returns a function that stops the worker gracefully."
  [storage writer-chan]
  (let [queue-closed? (chan)
        close-queue (fn []
                      (close! writer-chan)
                      (close! queue-closed?))]
    (go-loop [operation (<! writer-chan)]
      (let [{:keys [op args]} operation]
        (if (not= op :stop)
          (do ;; perform operation and recur
            (try
              (apply (op storage) args)
              (catch Throwable e
                (timbre/error "Something unexpected occrus while writing to storage.")
                (timbre/error e)))
            (recur (<! writer-chan)))
          (do
            (close-queue)
            (timbre/info "worker queue is cleared. storage worker shuts down")))))
    ;; return a function that stops the loop
    (fn []
      (put! writer-chan {:op :stop})
      (timbre/info "waiting for storage worker to shut down")
      (<!! queue-closed?))))

(defn- put-op!
  "Asynchronously put an operation map in a channel."
  [channel op args]
  (put! channel {:op op :args args}))

(defn start
  "Start worker. Wraps a base storage and returns a vector of 
  a stopper function and a asynchronous storage vtable."
  [storage]
  (let [writer-chan (chan)
        put-op (partial put-op! writer-chan)
        stop-worker (start-worker storage writer-chan)
        vtable {:append #(put-op :append [%])
                :update (fn [& args] (put-op :update args))
                :delete (fn [& args] (put-op :delete args))
                :get (:get storage)
                :get-by-domain (:get-by-domain storage)}]
    [stop-worker vtable]))