Bläddra i källkod

perf(config): read zone conf once

William Yang 2 år sedan
förälder
incheckning
88f9782db0
1 ändrade filer med 84 tillägg och 16 borttagningar
  1. 84 16
      apps/emqx/src/emqx_config.erl

+ 84 - 16
apps/emqx/src/emqx_config.erl

@@ -189,23 +189,11 @@ find_raw(KeyPath) ->
 
 
 -spec get_zone_conf(atom(), emqx_utils_maps:config_key_path()) -> term().
 -spec get_zone_conf(atom(), emqx_utils_maps:config_key_path()) -> term().
 get_zone_conf(Zone, KeyPath) ->
 get_zone_conf(Zone, KeyPath) ->
-    case find(?ZONE_CONF_PATH(Zone, KeyPath)) of
-        %% not found in zones, try to find the global config
-        {not_found, _, _} ->
-            ?MODULE:get(KeyPath);
-        {ok, Value} ->
-            Value
-    end.
+    ?MODULE:get(?ZONE_CONF_PATH(Zone, KeyPath)).
 
 
 -spec get_zone_conf(atom(), emqx_utils_maps:config_key_path(), term()) -> term().
 -spec get_zone_conf(atom(), emqx_utils_maps:config_key_path(), term()) -> term().
 get_zone_conf(Zone, KeyPath, Default) ->
 get_zone_conf(Zone, KeyPath, Default) ->
-    case find(?ZONE_CONF_PATH(Zone, KeyPath)) of
-        %% not found in zones, try to find the global config
-        {not_found, _, _} ->
-            ?MODULE:get(KeyPath, Default);
-        {ok, Value} ->
-            Value
-    end.
+    ?MODULE:get(?ZONE_CONF_PATH(Zone, KeyPath), Default).
 
 
 -spec put_zone_conf(atom(), emqx_utils_maps:config_key_path(), term()) -> ok.
 -spec put_zone_conf(atom(), emqx_utils_maps:config_key_path(), term()) -> ok.
 put_zone_conf(Zone, KeyPath, Conf) ->
 put_zone_conf(Zone, KeyPath, Conf) ->
@@ -245,8 +233,10 @@ erase(RootName) ->
 
 
 -spec put(emqx_utils_maps:config_key_path(), term()) -> ok.
 -spec put(emqx_utils_maps:config_key_path(), term()) -> ok.
 put(KeyPath, Config) ->
 put(KeyPath, Config) ->
-    Putter = fun(Path, Map, Value) ->
-        emqx_utils_maps:deep_put(Path, Map, Value)
+    Putter = fun(Path, Map, Value0) ->
+        Value = emqx_utils_maps:deep_put(Path, Map, Value0),
+        maybe_update_zone(KeyPath, Value0),
+        Value
     end,
     end,
     do_put(?CONF, Putter, KeyPath, Config).
     do_put(?CONF, Putter, KeyPath, Config).
 
 
@@ -596,6 +586,15 @@ save_to_app_env(AppEnvs0) ->
 -spec save_to_config_map(config(), raw_config()) -> ok.
 -spec save_to_config_map(config(), raw_config()) -> ok.
 save_to_config_map(Conf, RawConf) ->
 save_to_config_map(Conf, RawConf) ->
     ?MODULE:put(Conf),
     ?MODULE:put(Conf),
+    try emqx_config:get([zones]) of
+        Zones when is_map(Zones) ->
+            init_default_zone()
+    catch
+        error:{config_not_found, [zones]} ->
+            %% emqx schema is not loaded.
+            %% note, don't trust get_root_names/0
+            skip
+    end,
     ?MODULE:put_raw(RawConf).
     ?MODULE:put_raw(RawConf).
 
 
 -spec save_to_override_conf(boolean(), raw_config(), update_opts()) -> ok | {error, term()}.
 -spec save_to_override_conf(boolean(), raw_config(), update_opts()) -> ok | {error, term()}.
@@ -788,3 +787,72 @@ to_atom_conf_path(Path, OnFail) ->
                     V
                     V
             end
             end
     end.
     end.
+
+%% @doc Init zones under root `zones'
+%% 1. ensure one `default' zone as it is referenced by listeners.
+%%   if default zone is unset, clone all default values from `GlobalDefaults'
+%%   if default zone is set, values are merged with `GlobalDefaults'
+%% 2. For any user defined zones, merge with `GlobalDefaults'
+%%
+%% note1, this should be called as post action after emqx_config terms (zones, and GlobalDefaults)
+%%       are written in the PV storage during emqx config loading/initialization.
+-spec init_default_zone() -> ok.
+init_default_zone() ->
+    Zones =
+        case ?MODULE:get([zones], #{}) of
+            #{default := _DefaultZone} = Z1 ->
+                Z1;
+            Z2 ->
+                Z2#{default => #{}}
+        end,
+    GlobalDefaults = maps:from_list([{K, ?MODULE:get([K])} || K <- zone_roots()]),
+    NewZones = maps:map(
+        fun(_ZoneName, ZoneVal) ->
+            merge_with_global_defaults(ZoneVal, GlobalDefaults)
+        end,
+        Zones
+    ),
+    ?MODULE:put([zones], NewZones).
+
+%% @TODO just use deep merge?
+-spec merge_with_global_defaults(map(), map()) -> map().
+merge_with_global_defaults(Val, Defaults) ->
+    maps:fold(
+        fun(K, V, Acc) ->
+            case maps:get(K, Acc, ?CONFIG_NOT_FOUND_MAGIC) of
+                ?CONFIG_NOT_FOUND_MAGIC ->
+                    %% Use the value of global default
+                    Acc#{K => V};
+                Override ->
+                    %% Merge with overrides
+                    Acc#{K => emqx_utils_maps:deep_merge(V, Override)}
+            end
+        end,
+        Val,
+        Defaults
+    ).
+
+%% @doc Update zones in case global defaults are changed.
+-spec maybe_update_zone(runtime_config_key_path(), Val :: term()) -> skip | ok.
+maybe_update_zone([], _Value) ->
+    skip;
+maybe_update_zone([RootName | _T] = Path, Value) ->
+    case lists:member(RootName, zone_roots()) of
+        false ->
+            skip;
+        true ->
+            Zones = ?MODULE:get([zones], #{}),
+            NewZones = maps:map(
+                fun(_ZoneName, ZoneVal) ->
+                    %% @TODO we should not overwrite if it is a user defined value
+                    emqx_utils_maps:deep_put(Path, ZoneVal, Value)
+                end,
+                Zones
+            ),
+            ?MODULE:put([zones], NewZones),
+            ok
+    end.
+
+-spec zone_roots() -> [atom()].
+zone_roots() ->
+    lists:map(fun atom/1, emqx_zone_schema:roots()).