         (* (dec current-page) per-page)
         per-page))))
 
+(def write-executor (memoize #(Executors/newSingleThreadExecutor)))
+
+(def ^:private ^:dynamic *in-executor* nil)
+
+(defn serialize-task* [task-name task]
+  (if *in-executor*
+    (task)
+    (binding [*in-executor* true]
+      (let [bound-f (bound-fn []
+                      (try
+                        (task)
+                        (catch Throwable e
+                          e)))
+            response (deref
+                        (.submit (write-executor) bound-f)
+                        10000 ::timeout)]
+        (cond
+          (= response ::timeout) (throw
+                                   (ex-info
+                                     "Timed out waiting for serialized task to run"
+                                     {:name task-name}))
+          (instance? Throwable response) (throw
+                                           (ex-info "Serialized task failed"
+                                             {:name task-name}
+                                             response))
+          :default response)))))
+
+(defmacro serialize-task [name & body]
+  `(serialize-task* ~name
+     (fn [] ~@body)))
+
 (defn add-user [email username password ssh-key pgp-key]
   (let [record {:email email, :user username, :password (bcrypt password)
                 :ssh_key ssh-key, :pgp_key pgp-key}
         group (str "org.clojars." username)]
-    (insert users (values (assoc record
-                            :created (get-time)
-                            ;;TODO: remove salt field
-                            :salt "")))
-    (insert groups (values {:name group :user username}))
+    (serialize-task :add-user
+      (insert users (values (assoc record
+                              :created (get-time)
+                              ;;TODO: remove salt field
+                              :salt "")))
+      (insert groups (values {:name group :user username})))
     (ev/record :user (clojure.set/rename-keys record {:user :username
                                                       :ssh_key :ssh-key
                                                       :pgp_key :pgp-key}))

            [cemerick.friend.credentials :as creds])
  (:import java.security.MessageDigest
           java.util.Date
           java.io.File))

(def ^{:private true} ssh-options
  "no-agent-forwarding,no-port-forwarding,no-pty,no-X11-forwarding")

(defn add-jar [account {:keys [group name version
                               description homepage authors]}]
  (check-and-add-group account group)
  (serialize-task :add-jar
    (insert jars
      (values {:group_name group
               :jar_name   name
               :version    version
               :user       account
               :created    (get-time)
               :description description
               :homepage   homepage
               :authors    (str/join ", " (map #(.replace % "," "")
                                            authors))}))))
