-module(ibrowse_load_test). -export([go/3]). -define(counters, ibrowse_load_test_counters). go(URL, N_workers, N_reqs) -> spawn(fun() -> go_1(URL, N_workers, N_reqs) end). go_1(URL, N_workers, N_reqs) -> ets:new(?counters, [named_table, public]), try ets:insert(?counters, [{success, 0}, {failed, 0}, {timeout, 0}, {retry_later, 0}]), Start_time = now(), Pids = spawn_workers(N_workers, N_reqs, URL, self(), []), wait_for_pids(Pids), End_time = now(), Time_taken = trunc(round(timer:now_diff(End_time, Start_time) / 1000000)), [{_, Success_reqs}] = ets:lookup(?counters, success), Total_reqs = N_workers*N_reqs, Req_rate = case Time_taken > 0 of true -> trunc(Success_reqs / Time_taken); false when Success_reqs == Total_reqs -> withabix; false -> without_a_bix end, io:format("Stats : ~p~n", [ets:tab2list(?counters)]), io:format("Total reqs : ~p~n", [Total_reqs]), io:format("Time taken : ~p seconds~n", [Time_taken]), io:format("Reqs / sec : ~p~n", [Req_rate]) catch Class:Reason -> io:format("Load test crashed. Reason: ~p~n" "Stacktrace : ~p~n", [{Class, Reason}, erlang:get_stacktrace()]) after ets:delete(?counters) end. spawn_workers(0, _, _, _, Acc) -> Acc; spawn_workers(N_workers, N_reqs, URL, Parent, Acc) -> Pid = spawn(fun() -> worker(N_reqs, URL, Parent) end), spawn_workers(N_workers - 1, N_reqs, URL, Parent, [Pid | Acc]). wait_for_pids([Pid | T]) -> receive {done, Pid} -> wait_for_pids(T); {done, Some_pid} -> wait_for_pids([Pid | (T -- [Some_pid])]) end; wait_for_pids([]) -> ok. worker(0, _, Parent) -> Parent ! {done, self()}; worker(N, URL, Parent) -> case ibrowse:send_req(URL, [], get) of {ok, "200", _, _} -> ets:update_counter(?counters, success, 1); {error, req_timedout} -> ets:update_counter(?counters, timeout, 1); {error, retry_later} -> ets:update_counter(?counters, retry_later, 1); _ -> ets:update_counter(?counters, failed, 1) end, worker(N - 1, URL, Parent).