From bcdc75dfd6d2733fc70d936b2bc561192b520000 Mon Sep 17 00:00:00 2001 From: Andriy Zavada Date: Mon, 6 Apr 2026 21:18:16 +0300 Subject: [PATCH 1/3] functions to support riak_control API requests specifically: node_info_for_riak_control/0, system_info/0, collect_all_app_env/0, apply_app_env/1, get_advanced_config/0, write_advanced_config/1 --- src/riak_kv_util.erl | 126 ++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 125 insertions(+), 1 deletion(-) diff --git a/src/riak_kv_util.erl b/src/riak_kv_util.erl index 3d6d48a4..6d25ddd6 100644 --- a/src/riak_kv_util.erl +++ b/src/riak_kv_util.erl @@ -52,7 +52,13 @@ shuffle_list/1, kv_ready/0, ngr_initial_timeout/0, - sys_monitor_count/0 + sys_monitor_count/0, + node_info_for_riak_control/0, + system_info/0, + collect_all_app_env/0, + apply_app_env/1, + get_advanced_config/0, + write_advanced_config/1 ]). -export([report_hashtree_tokens/0, reset_hashtree_tokens/2]). -export([reset_aae_key_filter/0]). @@ -752,6 +758,124 @@ sys_monitor_count() -> ). + + +%% @doc Return current nodes information, to be sent to riak_control +%% over http (see riak_kv_wm_cluster) +-spec node_info_for_riak_control() -> proplists:proplist(). +node_info_for_riak_control() -> + {Total, Used} = node_memory_usage(), + Handoffs = node_handoff_status(), + VNodes = riak_core_vnode_manager:all_vnodes(), + ErlangMemory = proplists:get_value(total,erlang:memory()), + [{reachable, true}, + {mem_total, Total}, + {mem_used, Used}, + {mem_erlang, ErlangMemory}, + {vnodes, VNodes}, + {handoffs, Handoffs}, + {system_info, system_info()} + ]. + +node_memory_usage() -> + Mem = memsup:get_system_memory_data(), + Total = proplists:get_value(total_memory, Mem), + Free = proplists:get_value(free_memory, Mem), + Buffered = + case lists:keyfind(buffered_memory, 1, Mem) of + {_, BufferedMem} -> BufferedMem; + false -> 0 + end, + Cached = + case lists:keyfind(cached_memory, 1, Mem) of + {_, CachedMem} -> CachedMem; + false -> 0 + end, + {Total, Total - (Free + Cached + Buffered)}. + +format_transfer({status_v2, Handoff}) -> + Mod = proplists:get_value(mod, Handoff), + SrcPartition = proplists:get_value(src_partition, Handoff), + SrcNode = proplists:get_value(src_node, Handoff), + {Mod, SrcPartition, SrcNode}. + +node_handoff_status() -> + Transfers = riak_core_handoff_manager:status({direction, outbound}), + [format_transfer(T) || T <- lists:flatten(Transfers)]. + +system_info() -> + {MS, _} = erlang:statistics(wall_clock), + St = MS div 1000, + S = St rem 60, + Mt = St div 60, + M = Mt rem 60, + Ht = Mt div 60, + H = Ht rem 24, + Dt = Ht div 24, + D = Dt, + Str = case {D, H, M} of + {A, _, _} when A > 0 -> io_lib:format("~b day~s, ~b hour~s, ~b minute~s, ~b sec", [D, s(D), H, s(H), M, s(M), S]); + {_, A, _} when A > 0 -> io_lib:format("~b hour~s, ~b minute~s, ~b sec", [H, s(H), M, s(M), S]); + {_, _, A} when A > 0 -> io_lib:format("~b minute~s, ~b sec", [M, s(M), S]); + _ -> io_lib:format("~b sec", [S]) + end, + #{riak_version => list_to_binary(riak_version()), + system_version => list_to_binary(lists:droplast(erlang:system_info(system_version))), + nodename => node(), + uptime => St, + uptime_str => iolist_to_binary(Str) + }. + +s(1) -> ""; +s(_) -> "s". + +riak_version() -> + element(2, lists:keyfind("riak", 1, release_handler:which_releases())). + +-spec collect_all_app_env() -> proplists:proplist(). +collect_all_app_env() -> + Apps = [A || {A, _, _} <- application:which_applications()], + lists:filter( + fun({_, E}) -> E /= [] end, + [{App, application:get_all_env(App)} || App <- Apps] + ). + +-spec apply_app_env(proplists:proplist()) -> ok. +apply_app_env(AppEE) -> + lists:map( + fun({App, EE}) -> + [application:set_env(App, K, V) || {K, V} <- EE] + end, + AppEE), + ok. + +-spec get_advanced_config() -> + {ok, proplists:proplist()} | {error, bad_config | file:posix() | badarg | terminated | system_limit}. +get_advanced_config() -> + case file:consult(which_advanced_config()) of + {error, {_Line, _Mod, _Term} = FE} -> + ?LOG_WARNING("advanced.config is not consultable: ~s", [file:format_error(FE)]), + {error, bad_config}; + {ok, [Config]} -> + {ok, Config}; + Other -> + Other + end. + +-spec write_advanced_config(iolist()|binary()) -> + ok | {error, file:posix() | badarg | terminated | system_limit}. +write_advanced_config(Blob) -> + file:write_file(which_advanced_config(), Blob). + +which_advanced_config() -> + case os:getenv("USER") of + "riak" -> + "/etc/riak/advanced.config"; + _ -> + "etc/advanced.config" + end. + + %% =================================================================== %% EUnit tests %% =================================================================== From ffaca45c17f56e0b326b7db60298df721bd4ea03 Mon Sep 17 00:00:00 2001 From: Andriy Zavada Date: Sat, 11 Apr 2026 03:15:55 +0300 Subject: [PATCH 2/3] riak:deadmanshand_restart/0, helper for admin_api call ClusterNodeRestart --- src/riak.erl | 22 +++++++++++++++++++++- 1 file changed, 21 insertions(+), 1 deletion(-) diff --git a/src/riak.erl b/src/riak.erl index 58086077..5dd2ad92 100644 --- a/src/riak.erl +++ b/src/riak.erl @@ -26,7 +26,8 @@ -export([client_connect/1,client_connect/2, client_test/1, local_client/0,local_client/1, - join/1]). + join/1, + deadmanshand_restart/0]). -export([code_hash/0]). -include_lib("kernel/include/logger.hrl"). @@ -164,6 +165,25 @@ code_hash() -> riak_core_util:integer_to_list(MD5Sum, 62). +%% Helper function called from riak_admin_api_wm_ctl_cluster, as an +%% action to initiate riak restart. Actual restart (strictly, `riak +%% stop` followed by `riak start`) is performed by an external script, +%% run as a systemd service alongside riak. See +%% rel/files/riak-deadmanshand. +-spec deadmanshand_restart() -> ok. +deadmanshand_restart() -> + P = + case lists:keyfind("RELEASE_PROG", 1, os:env()) of + {_, "/usr" ++ _} -> + "/run/riak/"; + _ -> + "" + end, + _ = file:write_file(P ++ "RESTART_RIAK", <<>>), + ok. + + + %% %% Internal functions for testing a Riak node through single read/write cycle %% From f3e977c1c90ca9922ae66c626fe389477c8e703c Mon Sep 17 00:00:00 2001 From: Andriy Zavada Date: Wed, 13 May 2026 16:15:38 +0100 Subject: [PATCH 3/3] move code shared between cli and ag handlers to a module of its own tictacaae_report:produce/0 --- src/riak_kv_tictacaae_cli.erl | 40 +------------------- src/riak_kv_tictacaae_report.erl | 65 ++++++++++++++++++++++++++++++++ 2 files changed, 66 insertions(+), 39 deletions(-) create mode 100644 src/riak_kv_tictacaae_report.erl diff --git a/src/riak_kv_tictacaae_cli.erl b/src/riak_kv_tictacaae_cli.erl index 73358a63..710f9642 100644 --- a/src/riak_kv_tictacaae_cli.erl +++ b/src/riak_kv_tictacaae_cli.erl @@ -493,47 +493,9 @@ treestatus_usage() -> ]. treestatus_cmd([_, _, _], _, Options) -> - Report = get_aae_progress_report(), + Report = riak_kv_tictacaae_report:produce(), print_aae_progress_report(Report, Options). -get_aae_progress_report() -> - VVSS = - lists:append( - [case sys:get_state(P) of - {active, _CoreVnodeState = - {state, Idx, riak_kv_vnode, VSx, _, _, _, _, _, _, _, _}} -> - [{Idx, VSx}]; - _ -> - [] - end || {_, P, _, _} <- supervisor:which_children(riak_core_vnode_sup)]), - [begin - AAECntrl = riak_kv_vnode:aae_controller(VNState), - TictacRebuilding = riak_kv_vnode:aae_rebuilding(VNState), - InProgress = TictacRebuilding /= false, - AAEReport = aae_controller:aae_produce_progress_report(AAECntrl), - IsEmpty = proplists:get_value(is_empty, AAEReport), - LastRebuild = proplists:get_value(last_rebuild, AAEReport), - NextRebuild = proplists:get_value(next_rebuild, AAEReport), - Status = - case {IsEmpty, LastRebuild, InProgress, NextRebuild} of - {true, _, _, _} -> - empty; - {_, never, false, Scheduled} when Scheduled /= undefined -> - partial; - {_, Built, false, _} when Built /= never -> - built; - {_, Built, true, _} when Built /= never -> - rebuilding; - {_, never, true, _} -> - building - end, - Extra = [{status, Status}, - {partition, Idx}, - {controller_pid, list_to_binary(pid_to_list(AAECntrl))} - ], - AAEReport ++ Extra - end || {Idx, VNState} <- VVSS]. - print_aae_progress_report(Report, Options) -> Show_ = case proplists:get_all_values(show, Options) of diff --git a/src/riak_kv_tictacaae_report.erl b/src/riak_kv_tictacaae_report.erl new file mode 100644 index 00000000..f4330d95 --- /dev/null +++ b/src/riak_kv_tictacaae_report.erl @@ -0,0 +1,65 @@ +%% ------------------------------------------------------------------- +%% +%% Copyright (c) 2026 TI Tokyo. All Rights Reserved. +%% +%% This file is provided to you under the Apache License, +%% Version 2.0 (the "License"); you may not use this file +%% except in compliance with the License. You may obtain +%% a copy of the License at +%% +%% http://www.apache.org/licenses/LICENSE-2.0 +%% +%% Unless required by applicable law or agreed to in writing, +%% software distributed under the License is distributed on an +%% "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +%% KIND, either express or implied. See the License for the +%% specific language governing permissions and limitations +%% under the License. +%% +%% ------------------------------------------------------------------- + +-module(riak_kv_tictacaae_report). + +-export([produce/0]). + +%% The report is collected in both cli and ag (Ag, argentum, silver, +%% SilverMachine, aha!) handlers, so let's isolate it into a module of +%% its own +-spec produce() -> proplists:proplist(). +produce() -> + VVSS = + lists:append( + [case sys:get_state(P) of + {active, _CoreVnodeState = + {state, Idx, riak_kv_vnode, VSx, _, _, _, _, _, _, _, _}} -> + [{Idx, VSx}]; + _ -> + [] + end || {_, P, _, _} <- supervisor:which_children(riak_core_vnode_sup)]), + [begin + AAECntrl = riak_kv_vnode:aae_controller(VNState), + TictacRebuilding = riak_kv_vnode:aae_rebuilding(VNState), + InProgress = TictacRebuilding /= false, + AAEReport = aae_controller:aae_produce_progress_report(AAECntrl), + IsEmpty = proplists:get_value(is_empty, AAEReport), + LastRebuild = proplists:get_value(last_rebuild, AAEReport), + NextRebuild = proplists:get_value(next_rebuild, AAEReport), + Status = + case {IsEmpty, LastRebuild, InProgress, NextRebuild} of + {true, _, _, _} -> + empty; + {_, never, false, Scheduled} when Scheduled /= undefined -> + partial; + {_, Built, false, _} when Built /= never -> + built; + {_, Built, true, _} when Built /= never -> + rebuilding; + {_, never, true, _} -> + building + end, + Extra = [{status, Status}, + {partition, Idx}, + {controller_pid, list_to_binary(pid_to_list(AAECntrl))} + ], + lists:keydelete(is_empty, 1, AAEReport ++ Extra) + end || {Idx, VNState} <- VVSS].