From 378ba01ba8a4b67fb5d01dd5adc57fb72e3c86a5 Mon Sep 17 00:00:00 2001 From: Scott Ostler Date: Fri, 11 Jun 2010 15:07:46 -0400 Subject: load testing code --- test/dumpfm/load_test.clj | 81 ++++++++++++++++++++++++++++++++++++++++ test/dumpfm/millstone.clj | 95 +++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 176 insertions(+) create mode 100644 test/dumpfm/load_test.clj create mode 100644 test/dumpfm/millstone.clj (limited to 'test/dumpfm') diff --git a/test/dumpfm/load_test.clj b/test/dumpfm/load_test.clj new file mode 100644 index 0000000..68eda68 --- /dev/null +++ b/test/dumpfm/load_test.clj @@ -0,0 +1,81 @@ +(ns dumpfm.load-test + (:import org.postgresql.ds.PGPoolingDataSource) + (:use clojure.contrib.json.read + clojure.contrib.json.write + clojure.contrib.seq-utils + clojure.contrib.sql + dumpfm.millstone)) + + +(let [db-host "localhost" + db-name "dumpfm" + db-user "postgres" + db-pass "root"] + (def *db* {:datasource (doto (new PGPoolingDataSource) + (.setServerName db-host) + (.setDatabaseName db-name) + (.setUser db-user) + (.setPassword db-pass) + (.setMaxConnections 3))})) + +(def userlist-query " +select u.nick, u.hash +from users u, messages m where u.user_id = m.user_id +group by u.nick, u.hash +having count(*) > 50 +order by count(*) desc +") + +(print "Fetching userlist: ") +(def userlist (time + (with-connection *db* + (with-query-results rs [userlist-query] + (doall rs))))) + + +(def sample-messages-query " +select content +from messages +order by random() +limit 100 +") + +(print "Fetching messages: ") +(def message-contents (time + (with-connection *db* + (with-query-results rs [sample-messages-query] + (doall (map :content rs)))))) + +(defn login-client [] + (let [user-info (rand-elt userlist) + params (select-keys user-info [:nick :hash])] + (do-setup-request! "/login" + :params params + :method "GET"))) + +(defn refresh [] + (let [params {:since (- (System/currentTimeMillis) 2000) + :room "test"}] + (do-request! "/refresh" + :params params + :method "GET") + (Thread/sleep 1000))) + +(defn post-msg [] + (let [params {:content (rand-elt message-contents) + :room "test"}] + (do-request! "/msg" + :params params + :method "POST"))) + +(def test-spec {:server "http://localhost:8080" + :clients 100 + :requests 10000 + :setup-func login-client + :funcs [[55 refresh] + [5 post-msg]] + :frequency 1 + }) + +(grind! test-spec) +(System/exit 0) \ No newline at end of file diff --git a/test/dumpfm/millstone.clj b/test/dumpfm/millstone.clj new file mode 100644 index 0000000..e59bb61 --- /dev/null +++ b/test/dumpfm/millstone.clj @@ -0,0 +1,95 @@ +(ns dumpfm.millstone + (:use clojure.contrib.def + clojure.contrib.seq-utils + clojure-http.client)) + +(def *spec*) +(def *cookies*) + +(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 [[time res] (with-timing + (do-base-request (:server *spec*) + path + method + *cookies* + params))] + (log (format "%s - %sms [%s %s]" path time (:code res) (:msg res))) + (if (:cookies res) + (log "should set cookies" (:cookies res))) + res)) + +(defn build-client! [spec client-id] + (binding [*cookies* {}] + (if (:setup-func spec) + ((:setup-func spec))) + {:client-id client-id + :cookies *cookies*})) + +(defn print-run-results [spec elapsed] + (log (format "\nFinished\n--------\n%s runs in %s s (%s r/s)\n%s clients" + (:requests spec) + (/ elapsed 1000) + (/ (:requests spec) elapsed 1000) + (:clients spec)))) + +(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 (atom []) + counter (atom (:requests spec)) + threads (doall + (for [c clients] + (Thread. + (fn [] + (log "starting thread" (:client-id c)) + (binding [*spec* spec + *cookies* (:cookies c)] + (loop [] + (if (> (swap! counter dec) 0) + (let [f (rand-elt funcs)] + (f) + (recur)))) + (log "finishing thread" (:client-id c)))))))] + (println "Finished setup") + (let [[elapsed _] (with-timing + (do + (doseq [t threads] + (.start t)) + (doseq [t threads] + (.join t))))] + (print-run-results spec elapsed))))) -- cgit v1.2.3-70-g09d2