a | b | |
---|
| 0 | + | %%%------------------------------------------------------------------- |
---|
| 0 | + | %% Reproduces CLOSE-WAIT conditions in CouchBeam. |
---|
| 0 | + | %% Observe in a terminal |
---|
| 0 | + | %% > watch -n 1 "ss -p -o state close-wait '( dport = 5984 )' | wc -l " |
---|
| 0 | + | %% |
---|
| 0 | + | %% Run couchbed:run() to reproduce the CLOSE-WAIT condition. |
---|
| 0 | + | %% The condition appears after about 60 secondes, one CLOSE-WAIT socket |
---|
| 0 | + | %% per killed couchbeam_view_stream process. |
---|
| 0 | + | %%%------------------------------------------------------------------- |
---|
| 0 | + | |
---|
| 0 | + | -module(couchbed). |
---|
| 0 | + | -export([run/0, start_client/0, start/0, get/0, generate_and_store_docs/2, create_view/1, query_view/2]). |
---|
| 0 | + | |
---|
| 0 | + | |
---|
| 0 | + | -define(DB_NAME, "close_wait"). |
---|
| 0 | + | -define(VIEW, {"design", "view"}). |
---|
| 0 | + | -define(COUCH_URL, "http://localhost:5984"). |
---|
| 0 | + | -define(OPTIONS, [{basic_auth, {"admin", "admin"}}]). |
---|
| 0 | + | |
---|
| 0 | + | -define(MAX, 50). |
---|
| 0 | + | -spec run() -> ok. |
---|
| 0 | + | run() -> |
---|
| 0 | + | start(), |
---|
| 0 | + | Db = ?MODULE:get(), |
---|
| 0 | + | io:format("~p~n", [Db]), |
---|
| 0 | + | %generate_and_store_docs(Db, 50), |
---|
| 0 | + | Pid = start_client(), |
---|
| 0 | + | create_view(Db), |
---|
| 0 | + | query_view(Db, Pid), |
---|
| 0 | + | io:format("~p~n", [hackney_pool:get_stats(default)]), |
---|
| 0 | + | ok. |
---|
| 0 | + | |
---|
| 0 | + | |
---|
| 0 | + | -spec query_view(couchbeam:db(), pid()) -> ok. |
---|
| 0 | + | query_view(Db, Pid) -> |
---|
| 0 | + | lists:foreach(fun(_N) -> |
---|
| 0 | + | {ok, _Ref } = couchbeam_view:stream(Db, ?VIEW, [{stream_to, Pid}]), |
---|
| 0 | + | ok |
---|
| 0 | + | end, lists:seq(1, ?MAX)), |
---|
| 0 | + | kill_view_stream_process(), |
---|
| 0 | + | ok. |
---|
| 0 | + | |
---|
| 0 | + | -spec start_client() -> pid(). |
---|
| 0 | + | start_client() -> |
---|
| 0 | + | Pid = spawn(fun() -> loop() end), |
---|
| 0 | + | Pid. |
---|
| 0 | + | |
---|
| 0 | + | -spec loop() -> no_return(). |
---|
| 0 | + | loop() -> |
---|
| 0 | + | receive |
---|
| 0 | + | _Message -> |
---|
| 0 | + | case rand:uniform(400) of |
---|
| 0 | + | 1 -> |
---|
| 0 | + | io:format("~p~n", [hackney_pool:get_stats(default)]), |
---|
| 0 | + | loop(); |
---|
| 0 | + | _ -> |
---|
| 0 | + | loop() |
---|
| 0 | + | end |
---|
| 0 | + | end. |
---|
| 0 | + | |
---|
| 0 | + | -spec get() -> couchbeam:db(). |
---|
| 0 | + | get() -> |
---|
| 0 | + | S = couchbeam:server_connection(?COUCH_URL, ?OPTIONS), |
---|
| 0 | + | DbName = uri_string:quote(?DB_NAME), |
---|
| 0 | + | {ok, Db} = case couchbeam:db_exists(S,DbName) of |
---|
| 0 | + | true -> couchbeam:open_db(S, DbName, []); |
---|
| 0 | + | false -> couchbeam:create_db(S,DbName), |
---|
| 0 | + | couchbeam:open_db(S, DbName, []) |
---|
| 0 | + | end, |
---|
| 0 | + | |
---|
| 0 | + | Db. |
---|
| 0 | + | |
---|
| 0 | + | |
---|
| 0 | + | -spec kill_view_stream_process() -> ok. |
---|
| 0 | + | kill_view_stream_process() -> |
---|
| 0 | + | killall(ets:tab2list(couchbeam_view_streams)). |
---|
| 0 | + | |
---|
| 0 | + | -spec killall(list()) -> ok. |
---|
| 0 | + | killall([]) -> ok; |
---|
| 0 | + | killall([{_Ref, Pid} | Pids]) -> |
---|
| 0 | + | % kill 10% of |
---|
| 0 | + | case rand:uniform(10) of |
---|
| 0 | + | 1 -> |
---|
| 0 | + | io:format("killing ~p~n", [Pid]), |
---|
| 0 | + | exit(Pid, kill); |
---|
| 0 | + | _ -> |
---|
| 0 | + | ok |
---|
| 0 | + | end, |
---|
| 0 | + | killall(Pids). |
---|
| 0 | + | |
---|
| 0 | + | -spec generate_and_store_docs(couchbeam:db(), non_neg_integer()) -> ok. |
---|
| 0 | + | generate_and_store_docs(Db, N) -> |
---|
| 0 | + | io:format("creating ~p docs~n", [N]), |
---|
| 0 | + | lists:foreach(fun(_) -> |
---|
| 0 | + | Doc = {[{<<"type">>,<<"random_doc">>}, {<<"value">>, rand:uniform(1000)}]}, |
---|
| 0 | + | io:format("."), |
---|
| 0 | + | {ok, _Doc} = couchbeam:save_doc(Db, Doc) |
---|
| 0 | + | end, lists:seq(1, N)), |
---|
| 0 | + | io:format("~n"), |
---|
| 0 | + | ok. |
---|
| 0 | + | |
---|
| 0 | + | -spec create_view(couchbeam:db()) -> ok. |
---|
| 0 | + | create_view(Db) -> |
---|
| 0 | + | {Design, View} = ?VIEW, |
---|
| 0 | + | create_view(Db, list_to_binary(Design), list_to_binary(View)). |
---|
| 0 | + | |
---|
| 0 | + | -spec create_view(couchbeam:db(), binary(), binary()) -> couchbeam:document(). |
---|
| 0 | + | create_view(Db, Design, View) -> |
---|
| 0 | + | DesignDoc = {[ |
---|
| 0 | + | {<<"_id">>, <<"_design/",Design/binary>>}, |
---|
| 0 | + | {<<"language">>,<<"javascript">>}, |
---|
| 0 | + | {<<"views">>, |
---|
| 0 | + | {[{View, |
---|
| 0 | + | {[{<<"map">>, |
---|
| 0 | + | <<"function (doc) {\n emit(doc._id, doc); \n}">> |
---|
| 0 | + | }]} |
---|
| 0 | + | }]} |
---|
| 0 | + | } |
---|
| 0 | + | ]}, |
---|
| 0 | + | case couchbeam:save_doc(Db, DesignDoc) of |
---|
| 0 | + | {ok, DesignDoc1} -> |
---|
| 0 | + | io:format("Created view"), |
---|
| 0 | + | DesignDoc1; |
---|
| 0 | + | {error, conflict} -> |
---|
| 0 | + | io:format("View already exists"), |
---|
| 0 | + | ok; |
---|
| 0 | + | {error, Reason} -> |
---|
| 0 | + | io:format("Error creating view: ~p~n", [Reason]), |
---|
| 0 | + | error |
---|
| 0 | + | end. |
---|
| 0 | + | |
---|
| 0 | + | -spec start() -> ok. |
---|
| 0 | + | start() -> |
---|
| 0 | + | application:start(inets), |
---|
| 0 | + | application:ensure_all_started(couchbeam), |
---|
| 0 | + | io:format("Everything started~n"). |
---|
... | |
---|