Finally implement the "sticky" chain node

This commit is contained in:
Craig Everett 2025-10-25 13:45:40 +09:00
parent 4c09490f8a
commit e8febcf8d5
2 changed files with 68 additions and 18 deletions

View File

@ -582,7 +582,7 @@ next_nonce(AccountID) ->
% {ok, #{"reason" := Reason}} -> {error, Reason};
% Error -> Error
% end.
case request(["/v3/accounts/", AccountID]) of
case request_sticky(["/v3/accounts/", AccountID]) of
{ok, #{"nonce" := Nonce}} -> {ok, Nonce + 1};
{ok, #{"reason" := "Account not found"}} -> {ok, 1};
{ok, #{"reason" := Reason}} -> {error, Reason};
@ -729,7 +729,7 @@ tx_info(ID) ->
post_tx(Data) when is_binary(Data) ->
JSON = zj:binary_encode(#{tx => Data}),
request("/v3/transactions", JSON);
request_sticky("/v3/transactions", JSON);
post_tx(Data) when is_list(Data) ->
post_tx(list_to_binary(Data)).
@ -841,6 +841,14 @@ status_chainends() ->
request("/v3/status/chain-ends").
request_sticky(Path) ->
hz_man:request_sticky(unicode:characters_to_list(Path)).
request_sticky(Path, Payload) ->
hz_man:request_sticky(unicode:characters_to_list(Path), Payload).
request(Path) ->
hz_man:request(unicode:characters_to_list(Path)).

View File

@ -21,7 +21,7 @@
timeout/0, timeout/1]).
%% The whole point of this module:
-export([request/1, request/2]).
-export([request_sticky/1, request_sticky/2,request/1, request/2]).
%% gen_server goo
-export([start_link/0]).
@ -94,6 +94,25 @@ timeout(Value) when 0 < Value, Value =< 120000 ->
gen_server:cast(?MODULE, {timeout, Value}).
-spec request_sticky(Path) -> {ok, Value} | {error, Reason}
when Path :: unicode:charlist(),
Value :: map(),
Reason :: hz:chain_error().
request_sticky(Path) ->
gen_server:call(?MODULE, {request_sticky, {get, Path}}, infinity).
-spec request_sticky(Path, Data) -> {ok, Value} | {error, Reason}
when Path :: unicode:charlist(),
Data :: unicode:charlist(),
Value :: map(),
Reason :: hz:chain_error().
request_sticky(Path, Data) ->
gen_server:call(?MODULE, {request_sticky, {post, Path, Data}}, infinity).
-spec request(Path) -> {ok, Value} | {error, Reason}
when Path :: unicode:charlist(),
Value :: map(),
@ -145,6 +164,9 @@ init(none) ->
handle_call({request, Request}, From, State) ->
NewState = do_request(Request, From, State),
{noreply, NewState};
handle_call({request_sticky, Request}, From, State) ->
NewState = do_request_sticky(Request, From, State),
{noreply, NewState};
handle_call(tls, _, State = #s{tls = TLS}) ->
{reply, TLS, State};
handle_call(chain_nodes, _, State = #s{chain_nodes = {Wait, Used}}) ->
@ -160,10 +182,9 @@ handle_call(Unexpected, From, State) ->
handle_cast({tls, Boolean}, State) ->
NewState = do_tls(Boolean, State),
{noreply, NewState};
handle_cast({chain_nodes, []}, State) ->
{noreply, State#s{chain_nodes = {[], []}}};
handle_cast({chain_nodes, ToUse}, State) ->
{noreply, State#s{chain_nodes = {ToUse, []}}};
handle_cast({chain_nodes, List}, State) ->
NewState = do_chain_nodes(List, State),
{noreply, NewState};
handle_cast({timeout, Value}, State) ->
{noreply, State#s{timeout = Value}};
handle_cast(Unexpected, State) ->
@ -218,6 +239,14 @@ terminate(_, _) ->
%%% Doer Functions
do_chain_nodes([], State) ->
State#s{sticky = none, chain_nodes = {[], []}};
do_chain_nodes(List = [Sticky], State) ->
State#s{sticky = Sticky, chain_nodes = {List, []}};
do_chain_nodes([Sticky | List], State) ->
State#s{sticky = Sticky, chain_nodes = {List, []}}.
do_tls(true, State) ->
ok = ssl:start(),
State#s{tls = true};
@ -227,17 +256,21 @@ do_tls(_, State) ->
State.
do_request(_, From, State = #s{chain_nodes = {[], []}}) ->
do_request_sticky(_, From, State = #s{sticky = none}) ->
ok = gen_server:reply(From, {error, no_nodes}),
State;
do_request(Request,
do_request_sticky(Request,
From,
State = #s{tls = false,
State = #s{tls = TLS,
fetchers = Fetchers,
chain_nodes = {[Node | Rest], Used},
sticky = Node,
timeout = Timeout}) ->
Now = erlang:system_time(nanosecond),
Fetcher = fun() -> hz_fetcher:connect(Node, Request, From, Timeout) end,
Fetcher =
case TLS of
true -> fun() -> hz_fetcher:slowly_connect(Node, Request, From, Timeout) end;
false -> fun() -> hz_fetcher:connect(Node, Request, From, Timeout) end
end,
{PID, Mon} = spawn_monitor(Fetcher),
New = #fetcher{pid = PID,
mon = Mon,
@ -245,15 +278,24 @@ do_request(Request,
node = Node,
from = From,
req = Request},
State#s{fetchers = [New | Fetchers], chain_nodes = {Rest, [Node | Used]}};
State#s{fetchers = [New | Fetchers]}.
do_request(_, From, State = #s{chain_nodes = {[], []}}) ->
ok = gen_server:reply(From, {error, no_nodes}),
State;
do_request(Request,
From,
State = #s{tls = true,
State = #s{tls = TLS,
fetchers = Fetchers,
chain_nodes = {[Node | Rest], Used},
timeout = Timeout}) ->
Now = erlang:system_time(nanosecond),
Fetcher = fun() -> hz_fetcher:slowly_connect(Node, Request, From, Timeout) end,
Fetcher =
case TLS of
true -> fun() -> hz_fetcher:slowly_connect(Node, Request, From, Timeout) end;
false -> fun() -> hz_fetcher:connect(Node, Request, From, Timeout) end
end,
{PID, Mon} = spawn_monitor(Fetcher),
New = #fetcher{pid = PID,
mon = Mon,