2626-export ([set /3 , set /4 , set /5 ]).
2727-export ([delete /2 , delete /3 , delete /4 ]).
2828
29- -export ([get_integer /3 , set_integer /3 , set_integer /4 ]).
29+ -export ([get_integer /3 , set_integer /3 , set_integer /4 , get_integer_or_infinity / 3 ]).
3030-export ([get_float /3 , set_float /3 , set_float /4 ]).
3131-export ([get_boolean /3 , set_boolean /3 , set_boolean /4 ]).
3232
3838-export ([subscribe_for_changes /1 ]).
3939
4040-export ([init /1 ]).
41- -export ([handle_call /3 , handle_cast /2 ]).
41+ -export ([handle_call /3 , handle_cast /2 , handle_info / 2 ]).
4242-export ([terminate /2 ]).
4343
4444-export ([is_sensitive /2 ]).
6464 % Just *.ini files: original *.ini + expanded *.d dirs
6565 ini_files ,
6666 % The file we write config values to
67- write_filename
67+ write_filename ,
68+ % Reload timer ref
69+ reload_tref
6870}).
6971
7072start_link (IniFilesDirs ) ->
@@ -80,6 +82,17 @@ all() ->
8082 lists :sort (lists :filtermap (fun settings_fmap_fun /1 , persistent_term :get ())).
8183
8284get_integer (Section , Key , Default ) when is_integer (Default ) ->
85+ get_integer_int (Section , Key , Default ).
86+
87+ get_integer_or_infinity (Section , Key , Default ) when is_integer (Default ); Default == infinity ->
88+ case get_value (Section , Key , Default ) of
89+ infinity ->
90+ infinity ;
91+ _Value ->
92+ get_integer_int (Section , Key , Default )
93+ end .
94+
95+ get_integer_int (Section , Key , Default ) ->
8396 try
8497 to_integer (get_value (Section , Key , Default ))
8598 catch
@@ -294,7 +307,7 @@ init(IniFilesDirs) ->
294307 % correct node name and distribution mode.
295308 case check_distribution_mode () of
296309 ok ->
297- {ok , Config };
310+ {ok , schedule_reload ( Config ) };
298311 {error , Msg } ->
299312 erlang :display (Msg ),
300313 timer :sleep (500 ),
@@ -331,9 +344,9 @@ handle_call({set, Sec, Key, Val, Opts}, _From, Config) ->
331344 ok ->
332345 Event = {config_change , Sec , Key , Val , Persist },
333346 gen_event :sync_notify (config_event , Event ),
334- {reply , ok , Config };
347+ {reply , ok , schedule_reload ( Config ) };
335348 {error , Else } ->
336- {reply , {error , Else }, Config }
349+ {reply , {error , Else }, schedule_reload ( Config ) }
337350 end
338351 end ;
339352handle_call ({delete , Sec , Key , Persist , Reason }, _From , Config ) ->
@@ -357,55 +370,21 @@ handle_call({delete, Sec, Key, Persist, Reason}, _From, Config) ->
357370 ok ->
358371 Event = {config_change , Sec , Key , deleted , Persist },
359372 gen_event :sync_notify (config_event , Event ),
360- {reply , ok , Config };
373+ {reply , ok , schedule_reload ( Config ) };
361374 Else ->
362- {reply , Else , Config }
375+ {reply , Else , schedule_reload ( Config ) }
363376 end ;
364377handle_call (reload , _From , # config {} = Config ) ->
365- # config {ini_files_dirs = IniFilesDirs } = Config ,
366- IniFiles = expand_dirs (IniFilesDirs ),
367- % Update persistent term with ini values.
368- IniMap = ini_map (IniFiles ),
369- maps :foreach (
370- fun ({Sec , Key }, V ) ->
371- VExisting = get_value (Sec , Key , undefined ),
372- case V =:= VExisting of
373- true ->
374- ok ;
375- false ->
376- put_value (Sec , Key , V ),
377- Msg = " Reload detected config change ~s .~s = ~p " ,
378- Args = [Sec , Key , maybe_conceal (V , is_sensitive (Sec , Key ))],
379- couch_log :notice (Msg , Args ),
380- Event = {config_change , Sec , Key , V , true },
381- gen_event :sync_notify (config_event , Event )
382- end
383- end ,
384- IniMap
385- ),
386- % And remove anything in persistent terms that wasn't on disk.
387- lists :foreach (
388- fun
389- ({{Sec , Key }, _ }) when not is_map_key ({Sec , Key }, IniMap ) ->
390- NoticeMsg = " Reload deleting in-memory config ~s .~s " ,
391- couch_log :notice (NoticeMsg , [Sec , Key ]),
392- erase_value (Sec , Key ),
393- Event = {config_change , Sec , Key , deleted , true },
394- gen_event :sync_notify (config_event , Event );
395- (_ ) ->
396- ok
397- end ,
398- all ()
399- ),
400- Config1 = Config # config {
401- ini_files = IniFiles ,
402- write_filename = get_write_file (IniFiles )
403- },
404- {reply , ok , Config1 }.
378+ {reply , ok , reload (Config )}.
405379
406380handle_cast (_Msg , State ) ->
407381 {noreply , State }.
408382
383+ handle_info (reload , # config {} = Config ) ->
384+ {noreply , schedule_reload (reload (Config ))};
385+ handle_info (_Msg , State ) ->
386+ {noreply , State }.
387+
409388terminate (_Reason , _State ) ->
410389 erase_all ().
411390
@@ -615,6 +594,58 @@ settings_fmap_fun({{?MODULE, ?SETTINGS, Sec, Key}, Val}) ->
615594settings_fmap_fun (_ ) ->
616595 false .
617596
597+ reload (Config ) ->
598+ # config {ini_files_dirs = IniFilesDirs } = Config ,
599+ IniFiles = expand_dirs (IniFilesDirs ),
600+ % Update ets with ini values.
601+ IniMap = ini_map (IniFiles ),
602+ maps :foreach (
603+ fun ({Sec , Key }, V ) ->
604+ VExisting = get_value (Sec , Key , undefined ),
605+ case V =:= VExisting of
606+ true ->
607+ ok ;
608+ false ->
609+ put_value (Sec , Key , V ),
610+ Msg = " Reload detected config change ~s .~s = ~p " ,
611+ Args = [Sec , Key , maybe_conceal (V , is_sensitive (Sec , Key ))],
612+ couch_log :notice (Msg , Args ),
613+ Event = {config_change , Sec , Key , V , true },
614+ gen_event :sync_notify (config_event , Event )
615+ end
616+ end ,
617+ IniMap
618+ ),
619+ % And remove anything in ets that wasn't on disk.
620+ lists :foreach (
621+ fun
622+ ({{Sec , Key }, _ }) when not is_map_key ({Sec , Key }, IniMap ) ->
623+ NoticeMsg = " Reload deleting in-memory config ~s .~s " ,
624+ couch_log :notice (NoticeMsg , [Sec , Key ]),
625+ erase_value (Sec , Key ),
626+ Event = {config_change , Sec , Key , deleted , true },
627+ gen_event :sync_notify (config_event , Event );
628+ (_ ) ->
629+ ok
630+ end ,
631+ all ()
632+ ),
633+ Config # config {
634+ ini_files = IniFiles ,
635+ write_filename = get_write_file (IniFiles )
636+ }.
637+
638+ schedule_reload (# config {} = Config ) ->
639+ timer :cancel (Config # config .reload_tref ),
640+ case get_integer_or_infinity (" config" , " auto_reload_secs" , infinity ) of
641+ infinity ->
642+ Config ;
643+ AutoReloadSecs when AutoReloadSecs > 0 ->
644+ AutoReloadMS = erlang :convert_time_unit (AutoReloadSecs , second , millisecond ),
645+ TRef = erlang :send_after (AutoReloadMS , ? MODULE , reload ),
646+ Config # config {reload_tref = TRef }
647+ end .
648+
618649- ifdef (TEST ).
619650- include_lib (" couch/include/couch_eunit.hrl" ).
620651
0 commit comments