(ns dumpfm.millstone (:use clojure.contrib.def clojure.contrib.seq-utils clojure-http.client)) (def *spec*) (def *cookies*) (def *results*) (def printer (agent nil)) (defn log [& args] (send printer (fn [_] (apply println args)))) (defmacro with-timing [e] `(let [s# (System/nanoTime) r# ~e f# (System/nanoTime)] [(float (/ (- f# s#) 1e6)) r#])) (defn do-base-request [server path method cookies params] (let [method (.toUpperCase method) url (str server path)] (if (= method "GET") (request (add-query-params url params) method nil cookies) (request url method nil cookies params)))) (defnk do-setup-request! [path :params nil :method "GET"] (let [res (do-base-request (:server *spec*) path method *cookies* params)] (if (:cookies res) (set! *cookies* (merge *cookies* (:cookies res)))) res)) (defnk do-request! [path :params nil :method :GET] (let [[ms resp] (with-timing (do-base-request (:server *spec*) path method *cookies* params)) result {:path path :ms ms}] (dosync (commute *results* conj result)) (if (:cookies resp) (log "should set cookies" (:cookies resp))) resp)) (defn build-client! [spec client-id] (binding [*cookies* {}] (if (:setup-func spec) ((:setup-func spec))) {:client-id client-id :cookies *cookies*})) (defn sum [nums] (reduce + nums)) (defn avg [nums] (float (/ (sum nums) (count nums)))) (defn print-run-results [spec results elapsed-ms] (log (format "\nFinished\n--------\n%s requests in %.02f s (%.02f r/s)\n%s clients" (:requests spec) (/ elapsed-ms 1000) (/ (:requests spec) (/ elapsed-ms 1000)) (:clients spec))) (doseq [[path rs] (group-by :path results)] (let [nums (map :ms rs)] (log (format "\n%s:\nmin: %.02f ms\navg: %.02f ms\nmax: %.02f ms" path (apply min nums) (avg nums) (apply max nums))))) (log "")) (defn build-func-list [routes] (flatten (for [[n f] routes] (repeat n f)))) (defn grind! [spec] (binding [*spec* spec] (let [clients (doall (for [id (range (:clients spec))] (build-client! spec id))) funcs (build-func-list (:funcs spec)) results (ref []) counter (atom (:requests spec)) threads (doall (for [c clients] (Thread. (fn [] (binding [*spec* spec *cookies* (:cookies c) *results* results] (loop [] (if (> (swap! counter dec) 0) (let [f (rand-elt funcs)] (f) (recur)))))))))] (println "Finished setup") (let [[elapsed _] (with-timing (do (doseq [t threads] (.start t)) (doseq [t threads] (.join t))))] (print-run-results spec @results elapsed)))))