feat(telemetry): save uuids to files

In order for the node and cluster UUIDs used by telemetry to survive
database purges, we'll save the generated UUIDs to files in EMQX's
data directory as well.
This commit is contained in:
Thales Macedo Garitezi 2022-05-23 13:26:57 -03:00
parent 866810cea6
commit 4afb4d9dd8
No known key found for this signature in database
GPG Key ID: DD279F8152A9B6DD
2 changed files with 127 additions and 4 deletions

View File

@ -93,6 +93,9 @@
-define(TELEMETRY_SHARD, emqx_telemetry_shard).
-define(NODE_UUID_FILENAME, "node.uuid").
-define(CLUSTER_UUID_FILENAME, "cluster.uuid").
%%--------------------------------------------------------------------
%% API
%%--------------------------------------------------------------------
@ -535,7 +538,11 @@ ensure_uuids() ->
NodeUUID =
case mnesia:wread({?TELEMETRY, node()}) of
[] ->
NodeUUID0 = generate_uuid(),
NodeUUID0 =
case get_uuid_from_file(node) of
{ok, NUUID} -> NUUID;
undefined -> generate_uuid()
end,
mnesia:write(
?TELEMETRY,
#telemetry{
@ -551,7 +558,11 @@ ensure_uuids() ->
ClusterUUID =
case mnesia:wread({?TELEMETRY, ?CLUSTER_UUID_KEY}) of
[] ->
ClusterUUID0 = generate_uuid(),
ClusterUUID0 =
case get_uuid_from_file(cluster) of
{ok, CUUID} -> CUUID;
undefined -> generate_uuid()
end,
mnesia:write(
?TELEMETRY,
#telemetry{
@ -566,8 +577,35 @@ ensure_uuids() ->
end,
{NodeUUID, ClusterUUID}
end,
{atomic, UUIDs} = mria:transaction(?TELEMETRY_SHARD, Txn),
UUIDs.
{atomic, {NodeUUID, ClusterUUID}} = mria:transaction(?TELEMETRY_SHARD, Txn),
save_uuid_to_file(NodeUUID, node),
save_uuid_to_file(ClusterUUID, cluster),
{NodeUUID, ClusterUUID}.
get_uuid_from_file(Type) ->
Path = uuid_file_path(Type),
case file:read_file(Path) of
{ok,
UUID =
<<_:8/binary, "-", _:4/binary, "-", _:4/binary, "-", _:4/binary, "-", _:12/binary>>} ->
{ok, UUID};
_ ->
undefined
end.
save_uuid_to_file(UUID, Type) when is_binary(UUID) ->
Path = uuid_file_path(Type),
ok = filelib:ensure_dir(Path),
ok = file:write_file(Path, UUID).
uuid_file_path(Type) ->
DataDir = emqx:data_dir(),
Filename =
case Type of
node -> ?NODE_UUID_FILENAME;
cluster -> ?CLUSTER_UUID_FILENAME
end,
filename:join(DataDir, Filename).
empty_state() ->
#state{

View File

@ -151,6 +151,41 @@ init_per_testcase(t_cluster_uuid, Config) ->
Node = start_slave(n1),
ok = setup_slave(Node),
[{n1, Node} | Config];
init_per_testcase(t_uuid_restored_from_file, Config) ->
mock_httpc(),
NodeUUID = <<"AAAAAAAA-BBBB-CCCC-DDDD-EEEEEEEEEEEE">>,
ClusterUUID = <<"FFFFFFFF-GGGG-HHHH-IIII-JJJJJJJJJJJJ">>,
DataDir = emqx:data_dir(),
NodeUUIDFile = filename:join(DataDir, "node.uuid"),
ClusterUUIDFile = filename:join(DataDir, "cluster.uuid"),
file:delete(NodeUUIDFile),
file:delete(ClusterUUIDFile),
ok = file:write_file(NodeUUIDFile, NodeUUID),
ok = file:write_file(ClusterUUIDFile, ClusterUUID),
%% clear the UUIDs in the DB
{atomic, ok} = mria:clear_table(emqx_telemetry),
emqx_common_test_helpers:stop_apps([emqx_conf, emqx_authn, emqx_authz, emqx_modules]),
emqx_common_test_helpers:start_apps(
[emqx_conf, emqx_authn, emqx_authz, emqx_modules],
fun set_special_configs/1
),
Node = start_slave(n1),
ok = setup_slave(Node),
[
{n1, Node},
{node_uuid, NodeUUID},
{cluster_uuid, ClusterUUID}
| Config
];
init_per_testcase(t_uuid_saved_to_file, Config) ->
mock_httpc(),
DataDir = emqx:data_dir(),
NodeUUIDFile = filename:join(DataDir, "node.uuid"),
ClusterUUIDFile = filename:join(DataDir, "cluster.uuid"),
file:delete(NodeUUIDFile),
file:delete(ClusterUUIDFile),
Config;
init_per_testcase(t_num_clients, Config) ->
mock_httpc(),
ok = snabbkaffe:start_trace(),
@ -213,6 +248,14 @@ end_per_testcase(t_num_clients, Config) ->
meck:unload([httpc]),
ok = snabbkaffe:stop(),
Config;
end_per_testcase(t_uuid_restored_from_file, _Config) ->
DataDir = emqx:data_dir(),
NodeUUIDFile = filename:join(DataDir, "node.uuid"),
ClusterUUIDFile = filename:join(DataDir, "cluster.uuid"),
ok = file:delete(NodeUUIDFile),
ok = file:delete(ClusterUUIDFile),
meck:unload([httpc]),
ok;
end_per_testcase(_Testcase, _Config) ->
meck:unload([httpc]),
ok.
@ -248,6 +291,48 @@ t_cluster_uuid(Config) ->
?assertNotEqual(NodeUUID0, NodeUUID1),
ok.
%% should attempt read UUID from file in data dir to keep UUIDs
%% unique, in the event of a database purge.
t_uuid_restored_from_file(Config) ->
ExpectedNodeUUID = ?config(node_uuid, Config),
ExpectedClusterUUID = ?config(cluster_uuid, Config),
?assertEqual(
{ok, ExpectedNodeUUID},
emqx_telemetry:get_node_uuid()
),
?assertEqual(
{ok, ExpectedClusterUUID},
emqx_telemetry:get_cluster_uuid()
),
ok.
t_uuid_saved_to_file(_Config) ->
DataDir = emqx:data_dir(),
NodeUUIDFile = filename:join(DataDir, "node.uuid"),
ClusterUUIDFile = filename:join(DataDir, "cluster.uuid"),
%% preconditions
?assertEqual({error, enoent}, file:read_file(NodeUUIDFile)),
?assertEqual({error, enoent}, file:read_file(ClusterUUIDFile)),
%% clear the UUIDs in the DB
{atomic, ok} = mria:clear_table(emqx_telemetry),
emqx_common_test_helpers:stop_apps([emqx_conf, emqx_authn, emqx_authz, emqx_modules]),
emqx_common_test_helpers:start_apps(
[emqx_conf, emqx_authn, emqx_authz, emqx_modules],
fun set_special_configs/1
),
{ok, NodeUUID} = emqx_telemetry:get_node_uuid(),
{ok, ClusterUUID} = emqx_telemetry:get_cluster_uuid(),
?assertEqual(
{ok, NodeUUID},
file:read_file(NodeUUIDFile)
),
?assertEqual(
{ok, ClusterUUID},
file:read_file(ClusterUUIDFile)
),
ok.
t_official_version(_) ->
true = emqx_telemetry:official_version("0.0.0"),
true = emqx_telemetry:official_version("1.1.1"),