From 43b1ff2a8a8eabfe7bbe1289c437aa21cb9316c1 Mon Sep 17 00:00:00 2001 From: zvoykish Date: Thu, 16 Jun 2016 14:36:22 +0300 Subject: [PATCH 1/5] Update to enable tracking over reconnection loops per connection --- src/eredis_client.erl | 28 ++++++++++++++++++++++++++-- 1 file changed, 26 insertions(+), 2 deletions(-) diff --git a/src/eredis_client.erl b/src/eredis_client.erl index fb1cdc72..568881ee 100644 --- a/src/eredis_client.erl +++ b/src/eredis_client.erl @@ -40,6 +40,7 @@ password :: binary() | undefined, database :: binary() | undefined, reconnect_sleep :: reconnect_sleep() | undefined, + id :: string(), socket :: port() | undefined, parser_state :: #pstate{} | undefined, @@ -68,15 +69,24 @@ stop(Pid) -> %%==================================================================== init([Host, Port, Database, Password, ReconnectSleep]) -> + random:seed(erlang:now()), + Creation_Time = now_for_timestamp_millisecs(), + Client_Id = Creation_Time ++ "-" ++ integer_to_list(random:uniform(16)), + State = #state{host = Host, port = Port, database = list_to_binary(integer_to_list(Database)), password = list_to_binary(Password), reconnect_sleep = ReconnectSleep, - + id = Client_Id, parser_state = eredis_parser:init(), queue = queue:new()}, + case ets:info(eredis_client_stats) of + undefined -> ets:new(eredis_client_stats, [ordered_set, public, named_table]); + _ -> ok + end, + case connect(State) of {ok, NewState} -> {ok, NewState}; @@ -84,6 +94,15 @@ init([Host, Port, Database, Password, ReconnectSleep]) -> {stop, {connection_error, Reason}} end. +now_for_timestamp_millisecs() -> + {A, B, C} = os:timestamp(), + Milliseconds = C div 1000, + Total_Seconds = (A * 1000000 + B), + Gregorian_Seconds = 62167219200 + Total_Seconds, + {{Year, Month, Day}, {Hour, Minute, Second}} = calendar:gregorian_seconds_to_datetime(Gregorian_Seconds), + lists:flatten(io_lib:format("~4.10.0B-~2.10.0B-~2.10.0BT~2.10.0B:~2.10.0B:~2.10.0B.~3.10.0BZ", + [Year, Month, Day, Hour, Minute, Second, Milliseconds])). + handle_call({request, Req}, From, State) -> do_request(Req, From, State); @@ -297,7 +316,12 @@ do_sync_command(Socket, Command) -> %% @doc: Loop until a connection can be established, this includes %% successfully issuing the auth and select calls. When we have a %% connection, give the socket to the redis client. -reconnect_loop(Client, #state{reconnect_sleep = ReconnectSleep} = State) -> +reconnect_loop(Client, #state{reconnect_sleep = ReconnectSleep, id = Id} = State) -> + case ets:member(eredis_client_stats, Id) of + true -> ets:update_counter (eredis_client_stats, Id, 1); + false -> ets:insert (eredis_client_stats, {Id, 1}) + end, + case catch(connect(State)) of {ok, #state{socket = Socket}} -> gen_tcp:controlling_process(Socket, Client), From 9fedb2f254de96cb95dcf529df37faa84ead5a95 Mon Sep 17 00:00:00 2001 From: zvoykish Date: Thu, 16 Jun 2016 15:56:35 +0300 Subject: [PATCH 2/5] Update to enable tracking over reconnection loops per connection --- src/eredis_client.erl | 20 +++++++++++++------- 1 file changed, 13 insertions(+), 7 deletions(-) diff --git a/src/eredis_client.erl b/src/eredis_client.erl index 568881ee..bf8680ab 100644 --- a/src/eredis_client.erl +++ b/src/eredis_client.erl @@ -71,7 +71,7 @@ stop(Pid) -> init([Host, Port, Database, Password, ReconnectSleep]) -> random:seed(erlang:now()), Creation_Time = now_for_timestamp_millisecs(), - Client_Id = Creation_Time ++ "-" ++ integer_to_list(random:uniform(16)), + Client_Id = Creation_Time ++ "-" ++ integer_to_list(random:uniform(9999999999999999)), State = #state{host = Host, port = Port, @@ -270,12 +270,23 @@ safe_reply(From, Value) -> %% returns something we don't expect, we crash. Returns {ok, State} or %% {SomeError, Reason}. connect(State) -> + Id = State#state.id, + case ets:member(eredis_client_stats, Id) of + true -> + ets:update_counter(eredis_client_stats, Id, 1), + ets:update_element(eredis_client_stats, Id, {3, now_for_timestamp_millisecs()}); + false -> + ets:insert(eredis_client_stats, {Id, 1, now_for_timestamp_millisecs(), 0, never}) + end, + case gen_tcp:connect(State#state.host, State#state.port, ?SOCKET_OPTS) of {ok, Socket} -> case authenticate(Socket, State#state.password) of ok -> case select_database(Socket, State#state.database) of ok -> + ets:update_counter(eredis_client_stats, Id, {4, 1}), + ets:update_element(eredis_client_stats, Id, {5, now_for_timestamp_millisecs()}), {ok, State#state{socket = Socket}}; {error, Reason} -> {error, {select_error, Reason}} @@ -316,12 +327,7 @@ do_sync_command(Socket, Command) -> %% @doc: Loop until a connection can be established, this includes %% successfully issuing the auth and select calls. When we have a %% connection, give the socket to the redis client. -reconnect_loop(Client, #state{reconnect_sleep = ReconnectSleep, id = Id} = State) -> - case ets:member(eredis_client_stats, Id) of - true -> ets:update_counter (eredis_client_stats, Id, 1); - false -> ets:insert (eredis_client_stats, {Id, 1}) - end, - +reconnect_loop(Client, #state{reconnect_sleep = ReconnectSleep} = State) -> case catch(connect(State)) of {ok, #state{socket = Socket}} -> gen_tcp:controlling_process(Socket, Client), From f80751b28d4764d6432583b4d9b94c915b346930 Mon Sep 17 00:00:00 2001 From: zvoykish Date: Thu, 16 Jun 2016 15:59:06 +0300 Subject: [PATCH 3/5] Update to enable tracking over reconnection loops per connection --- src/eredis_client.erl | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/src/eredis_client.erl b/src/eredis_client.erl index bf8680ab..01d31e00 100644 --- a/src/eredis_client.erl +++ b/src/eredis_client.erl @@ -47,6 +47,8 @@ queue :: queue() | undefined }). +-define(STATS_TABLE, eredis_client_stats). + %% %% API %% @@ -82,8 +84,8 @@ init([Host, Port, Database, Password, ReconnectSleep]) -> parser_state = eredis_parser:init(), queue = queue:new()}, - case ets:info(eredis_client_stats) of - undefined -> ets:new(eredis_client_stats, [ordered_set, public, named_table]); + case ets:info(?STATS_TABLE) of + undefined -> ets:new(?STATS_TABLE, [ordered_set, public, named_table]); _ -> ok end, @@ -271,12 +273,12 @@ safe_reply(From, Value) -> %% {SomeError, Reason}. connect(State) -> Id = State#state.id, - case ets:member(eredis_client_stats, Id) of + case ets:member(?STATS_TABLE, Id) of true -> - ets:update_counter(eredis_client_stats, Id, 1), - ets:update_element(eredis_client_stats, Id, {3, now_for_timestamp_millisecs()}); + ets:update_counter(?STATS_TABLE, Id, 1), + ets:update_element(?STATS_TABLE, Id, {3, now_for_timestamp_millisecs()}); false -> - ets:insert(eredis_client_stats, {Id, 1, now_for_timestamp_millisecs(), 0, never}) + ets:insert(?STATS_TABLE, {Id, 1, now_for_timestamp_millisecs(), 0, never}) end, case gen_tcp:connect(State#state.host, State#state.port, ?SOCKET_OPTS) of @@ -285,8 +287,8 @@ connect(State) -> ok -> case select_database(Socket, State#state.database) of ok -> - ets:update_counter(eredis_client_stats, Id, {4, 1}), - ets:update_element(eredis_client_stats, Id, {5, now_for_timestamp_millisecs()}), + ets:update_counter(?STATS_TABLE, Id, {4, 1}), + ets:update_element(?STATS_TABLE, Id, {5, now_for_timestamp_millisecs()}), {ok, State#state{socket = Socket}}; {error, Reason} -> {error, {select_error, Reason}} From 82eea949ffe58be3829363c772f9cc044aea278c Mon Sep 17 00:00:00 2001 From: zvoykish Date: Thu, 16 Jun 2016 16:13:37 +0300 Subject: [PATCH 4/5] Update to enable tracking over reconnection loops per connection --- src/eredis_client.erl | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/eredis_client.erl b/src/eredis_client.erl index 01d31e00..77dffe4a 100644 --- a/src/eredis_client.erl +++ b/src/eredis_client.erl @@ -73,7 +73,7 @@ stop(Pid) -> init([Host, Port, Database, Password, ReconnectSleep]) -> random:seed(erlang:now()), Creation_Time = now_for_timestamp_millisecs(), - Client_Id = Creation_Time ++ "-" ++ integer_to_list(random:uniform(9999999999999999)), + Client_Id = pid_to_list(self()) ++ "-" ++ Creation_Time, State = #state{host = Host, port = Port, @@ -85,8 +85,8 @@ init([Host, Port, Database, Password, ReconnectSleep]) -> queue = queue:new()}, case ets:info(?STATS_TABLE) of - undefined -> ets:new(?STATS_TABLE, [ordered_set, public, named_table]); - _ -> ok + undefined -> try ets:new(?STATS_TABLE, [ordered_set, public, named_table]) catch _:_ -> saved_from_race_condition end; + _ -> already_started end, case connect(State) of From 94c63f95fa6c41445b67550e5a52d4fcd45b93cc Mon Sep 17 00:00:00 2001 From: zvoykish Date: Thu, 16 Jun 2016 16:16:51 +0300 Subject: [PATCH 5/5] Update to enable tracking over reconnection loops per connection --- src/eredis_client.erl | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/eredis_client.erl b/src/eredis_client.erl index 77dffe4a..489a35ff 100644 --- a/src/eredis_client.erl +++ b/src/eredis_client.erl @@ -84,6 +84,7 @@ init([Host, Port, Database, Password, ReconnectSleep]) -> parser_state = eredis_parser:init(), queue = queue:new()}, + % ETS table structure: {Id, connection attempts (counter), last connection attempt timestamp (string), successful connections (counter), last successful connection timestamp (string)} case ets:info(?STATS_TABLE) of undefined -> try ets:new(?STATS_TABLE, [ordered_set, public, named_table]) catch _:_ -> saved_from_race_condition end; _ -> already_started @@ -275,9 +276,12 @@ connect(State) -> Id = State#state.id, case ets:member(?STATS_TABLE, Id) of true -> + % Increase connection attempts counter ets:update_counter(?STATS_TABLE, Id, 1), + % Update last connection attempt time ets:update_element(?STATS_TABLE, Id, {3, now_for_timestamp_millisecs()}); false -> + % First time connecting with client ets:insert(?STATS_TABLE, {Id, 1, now_for_timestamp_millisecs(), 0, never}) end, @@ -287,7 +291,9 @@ connect(State) -> ok -> case select_database(Socket, State#state.database) of ok -> + % Increase successful connections counter ets:update_counter(?STATS_TABLE, Id, {4, 1}), + % Update last successful connection time ets:update_element(?STATS_TABLE, Id, {5, now_for_timestamp_millisecs()}), {ok, State#state{socket = Socket}}; {error, Reason} ->