]> git.apps.os.sepia.ceph.com Git - ceph-ci.git/commitdiff
common/config: extract ObserverMgr from md_config_impl<>
authorKefu Chai <kchai@redhat.com>
Sun, 15 Jul 2018 10:14:43 +0000 (18:14 +0800)
committerKefu Chai <kchai@redhat.com>
Wed, 25 Jul 2018 04:59:30 +0000 (12:59 +0800)
crimson will have sharded<PGShard> (name may vary in future), and
underlying ObjectStore instances. they will be consumers of crimson
ConfigProxy, and will register them as an observer of config changes.
but as always, in crimson osd, world is sharded, each ConfigProxy will
only take care of the sharded services living on the shard which it is
in charge of. in other words, unlike alien's ConfigProxy, the
"observers" data structure is not shared, they are sharded as well. so
we need to extract the observers out of md_config_impl<>, and move it
into ConfigProxy.

Signed-off-by: Kefu Chai <kchai@redhat.com>
src/common/config.cc
src/common/config.h
src/common/config_obs_mgr.h [new file with mode: 0644]
src/common/config_proxy.h
src/common/config_tracker.h [new file with mode: 0644]

index fb151dbbb96d58817954185f09dfebbf3012e3d7..5b1fdce0b3ace45bd9fa72ab1285430e467ccbe1 100644 (file)
@@ -101,6 +101,7 @@ namespace ceph::internal {
 
 template<LockPolicy lp>
 md_config_impl<lp>::md_config_impl(ConfigValues& values,
+                                  const ConfigTracker& tracker,
                                   bool is_daemon)
   : is_daemon(is_daemon)
 {
@@ -205,7 +206,7 @@ md_config_impl<lp>::md_config_impl(ConfigValues& values,
       if (val != *def_str) {
        // if the validator normalizes the string into a different form than
        // what was compiled in, use that.
-       set_val_default(values, opt.name, val);
+       set_val_default(values, tracker, opt.name, val);
       }
     }
   }
@@ -258,18 +259,20 @@ const Option *md_config_impl<lp>::find_option(const string& name) const
 
 template<LockPolicy lp>
 void md_config_impl<lp>::set_val_default(ConfigValues& values,
+                                        const ConfigTracker& tracker,
                                         const string& name, const std::string& val)
 {
   const Option *o = find_option(name);
   assert(o);
   string err;
-  int r = _set_val(values, val, *o, CONF_DEFAULT, &err);
+  int r = _set_val(values, tracker, val, *o, CONF_DEFAULT, &err);
   assert(r >= 0);
 }
 
 template<LockPolicy lp>
 int md_config_impl<lp>::set_mon_vals(CephContext *cct,
     ConfigValues& values,
+    const ConfigTracker& tracker,
     const map<string,string>& kv,
     config_callback config_cb)
 {
@@ -297,7 +300,7 @@ int md_config_impl<lp>::set_mon_vals(CephContext *cct,
       continue;
     }
     std::string err;
-    int r = _set_val(values, i.second, *o, CONF_MON, &err);
+    int r = _set_val(values, tracker, i.second, *o, CONF_MON, &err);
     if (r < 0) {
       lderr(cct) << __func__ << " failed to set " << i.first << " = "
                 << i.second << ": " << err << dendl;
@@ -329,33 +332,9 @@ int md_config_impl<lp>::set_mon_vals(CephContext *cct,
   return 0;
 }
 
-template<LockPolicy lp>
-void md_config_impl<lp>::add_observer(md_config_obs_impl<lp>* observer_)
-{
-  const char **keys = observer_->get_tracked_conf_keys();
-  for (const char ** k = keys; *k; ++k) {
-    observers.emplace(*k, observer_);
-  }
-}
-
-template<LockPolicy lp>
-void md_config_impl<lp>::remove_observer(md_config_obs_impl<lp>* observer_)
-{
-  bool found_obs = false;
-  for (auto o = observers.begin(); o != observers.end(); ) {
-    if (o->second == observer_) {
-      observers.erase(o++);
-      found_obs = true;
-    }
-    else {
-      ++o;
-    }
-  }
-  assert(found_obs);
-}
-
 template<LockPolicy lp>
 int md_config_impl<lp>::parse_config_files(ConfigValues& values,
+                                   const ConfigTracker& tracker,
                                    const char *conf_files_str,
                                    std::ostream *warnings,
                                    int flags)
@@ -442,7 +421,7 @@ int md_config_impl<lp>::parse_config_files(ConfigValues& values,
     int ret = _get_val_from_conf_file(my_sections, opt.name, val);
     if (ret == 0) {
       std::string error_message;
-      int r = _set_val(values, val, opt, CONF_FILE, &error_message);
+      int r = _set_val(values, tracker, val, opt, CONF_FILE, &error_message);
       if (warnings != nullptr && (r < 0 || !error_message.empty())) {
         *warnings << "parse error setting '" << opt.name << "' to '" << val
                   << "'";
@@ -482,7 +461,9 @@ int md_config_impl<lp>::parse_config_files(ConfigValues& values,
 }
 
 template<LockPolicy lp>
-void md_config_impl<lp>::parse_env(ConfigValues& values, const char *args_var)
+void md_config_impl<lp>::parse_env(ConfigValues& values,
+                                  const ConfigTracker& tracker,
+                                  const char *args_var)
 {
   if (safe_to_start_threads)
     return;
@@ -490,7 +471,7 @@ void md_config_impl<lp>::parse_env(ConfigValues& values, const char *args_var)
     args_var = "CEPH_ARGS";
   }
   if (getenv("CEPH_KEYRING")) {
-    _set_val(values, getenv("CEPH_KEYRING"), *find_option("keyring"),
+    _set_val(values, tracker, getenv("CEPH_KEYRING"), *find_option("keyring"),
             CONF_ENV, nullptr);
   }
   if (const char *dir = getenv("CEPH_LIB")) {
@@ -498,13 +479,13 @@ void md_config_impl<lp>::parse_env(ConfigValues& values, const char *args_var)
     std::string err;
       const Option *o = find_option(name);
       assert(o);
-      _set_val(values, dir, *o, CONF_ENV, &err);
+      _set_val(values, tracker, dir, *o, CONF_ENV, &err);
     }
   }
   if (getenv(args_var)) {
     vector<const char *> env_args;
     env_to_vec(env_args, args_var);
-    parse_argv(values, env_args, CONF_ENV);
+    parse_argv(values, tracker, env_args, CONF_ENV);
   }
 }
 
@@ -557,6 +538,7 @@ void md_config_impl<lp>::_show_config(const ConfigValues& values,
 
 template<LockPolicy lp>
 int md_config_impl<lp>::parse_argv(ConfigValues& values,
+                                  const ConfigTracker& tracker,
                                   std::vector<const char*>& args, int level)
 {
   if (safe_to_start_threads) {
@@ -591,26 +573,26 @@ int md_config_impl<lp>::parse_argv(ConfigValues& values,
       values.no_mon_config = false;
     }
     else if (ceph_argparse_flag(args, i, "--foreground", "-f", (char*)NULL)) {
-      set_val_or_die(values, "daemonize", "false");
+      set_val_or_die(values, tracker, "daemonize", "false");
     }
     else if (ceph_argparse_flag(args, i, "-d", (char*)NULL)) {
-      set_val_or_die(values, "daemonize", "false");
-      set_val_or_die(values, "log_file", "");
-      set_val_or_die(values, "log_to_stderr", "true");
-      set_val_or_die(values, "err_to_stderr", "true");
-      set_val_or_die(values, "log_to_syslog", "false");
+      set_val_or_die(values, tracker, "daemonize", "false");
+      set_val_or_die(values, tracker, "log_file", "");
+      set_val_or_die(values, tracker, "log_to_stderr", "true");
+      set_val_or_die(values, tracker, "err_to_stderr", "true");
+      set_val_or_die(values, tracker, "log_to_syslog", "false");
     }
     // Some stuff that we wanted to give universal single-character options for
     // Careful: you can burn through the alphabet pretty quickly by adding
     // to this list.
     else if (ceph_argparse_witharg(args, i, &val, "--monmap", "-M", (char*)NULL)) {
-      set_val_or_die(values, "monmap", val.c_str());
+      set_val_or_die(values, tracker, "monmap", val.c_str());
     }
     else if (ceph_argparse_witharg(args, i, &val, "--mon_host", "-m", (char*)NULL)) {
-      set_val_or_die(values, "mon_host", val.c_str());
+      set_val_or_die(values, tracker, "mon_host", val.c_str());
     }
     else if (ceph_argparse_witharg(args, i, &val, "--bind", (char*)NULL)) {
-      set_val_or_die(values, "public_addr", val.c_str());
+      set_val_or_die(values, tracker, "public_addr", val.c_str());
     }
     else if (ceph_argparse_witharg(args, i, &val, "--keyfile", "-K", (char*)NULL)) {
       bufferlist bl;
@@ -623,17 +605,17 @@ int md_config_impl<lp>::parse_argv(ConfigValues& values,
       }
       if (r >= 0) {
        string k(bl.c_str(), bl.length());
-       set_val_or_die(values, "key", k.c_str());
+       set_val_or_die(values, tracker, "key", k.c_str());
       }
     }
     else if (ceph_argparse_witharg(args, i, &val, "--keyring", "-k", (char*)NULL)) {
-      set_val_or_die(values, "keyring", val.c_str());
+      set_val_or_die(values, tracker, "keyring", val.c_str());
     }
     else if (ceph_argparse_witharg(args, i, &val, "--client_mountpoint", "-r", (char*)NULL)) {
-      set_val_or_die(values, "client_mountpoint", val.c_str());
+      set_val_or_die(values, tracker, "client_mountpoint", val.c_str());
     }
     else {
-      int r = parse_option(values, args, i, NULL, level);
+      int r = parse_option(values, tracker, args, i, NULL, level);
       if (r < 0) {
         return r;
       }
@@ -672,6 +654,7 @@ void md_config_impl<lp>::do_argv_commands(const ConfigValues& values)
 
 template<LockPolicy lp>
 int md_config_impl<lp>::parse_option(ConfigValues& values,
+                                    const ConfigTracker& tracker,
                                     std::vector<const char*>& args,
                                     std::vector<const char*>::iterator& i,
                                     ostream *oss,
@@ -695,9 +678,9 @@ int md_config_impl<lp>::parse_option(ConfigValues& values,
       if (ceph_argparse_binary_flag(args, i, &res, oss, as_option.c_str(),
                                    (char*)NULL)) {
        if (res == 0)
-         ret = _set_val(values, "false", opt, level, &error_message);
+         ret = _set_val(values, tracker, "false", opt, level, &error_message);
        else if (res == 1)
-         ret = _set_val(values, "true", opt, level, &error_message);
+         ret = _set_val(values, tracker, "true", opt, level, &error_message);
        else
          ret = res;
        break;
@@ -705,7 +688,7 @@ int md_config_impl<lp>::parse_option(ConfigValues& values,
        std::string no("--no-");
        no += opt.name;
        if (ceph_argparse_flag(args, i, no.c_str(), (char*)NULL)) {
-         ret = _set_val(values, "false", opt, level, &error_message);
+         ret = _set_val(values, tracker, "false", opt, level, &error_message);
          break;
        }
       }
@@ -716,7 +699,7 @@ int md_config_impl<lp>::parse_option(ConfigValues& values,
        ret = -EINVAL;
        break;
       }
-      ret = _set_val(values, val, opt, level, &error_message);
+      ret = _set_val(values, tracker,  val, opt, level, &error_message);
       break;
     }
     ++o;
@@ -750,98 +733,19 @@ int md_config_impl<lp>::parse_option(ConfigValues& values,
 
 template<LockPolicy lp>
 int md_config_impl<lp>::parse_injectargs(ConfigValues& values,
+                                 const ConfigTracker& tracker,
                                  std::vector<const char*>& args,
                                  std::ostream *oss)
 {
   int ret = 0;
   for (std::vector<const char*>::iterator i = args.begin(); i != args.end(); ) {
-    int r = parse_option(values, args, i, oss, CONF_OVERRIDE);
+    int r = parse_option(values, tracker, args, i, oss, CONF_OVERRIDE);
     if (r < 0)
       ret = r;
   }
   return ret;
 }
 
-template<LockPolicy lp>
-void md_config_impl<lp>::apply_changes(ConfigValues& values,
-                                      const ConfigProxy& proxy,
-                                      std::ostream *oss)
-{
-  /*
-   * apply changes until the cluster name is assigned
-   */
-  if (values.cluster.size()) {
-    // meta expands could have modified anything.  Copy it all out again.
-    update_legacy_vals(values);
-    _apply_changes(values, proxy, oss);
-  }
-}
-
-template<LockPolicy lp>
-void md_config_impl<lp>::_apply_changes(ConfigValues& values,
-                                       const ConfigProxy& proxy,
-                                       std::ostream *oss)
-{
-  /* Maps observers to the configuration options that they care about which
-   * have changed. */
-  typedef std::map < md_config_obs_t*, std::set <std::string> > rev_obs_map_t;
-
-  // create the reverse observer mapping, mapping observers to the set of
-  // changed keys that they'll get.
-  rev_obs_map_t robs;
-  std::set <std::string> empty_set;
-  string val;
-  for (changed_set_t::const_iterator c = changed.begin();
-       c != changed.end(); ++c) {
-    const std::string &key(*c);
-    auto [first, last] = observers.equal_range(key);
-    if ((oss) && !conf_stringify(_get_val(values, key), &val)) {
-      (*oss) << key << " = '" << val << "' ";
-      if (first == last) {
-       (*oss) << "(not observed, change may require restart) ";
-      }
-    }
-    for (auto r = first; r != last; ++r) {
-      rev_obs_map_t::value_type robs_val(r->second, empty_set);
-      pair < rev_obs_map_t::iterator, bool > robs_ret(robs.insert(robs_val));
-      std::set <std::string> &keys(robs_ret.first->second);
-      keys.insert(key);
-    }
-  }
-
-  changed.clear();
-
-  // Make any pending observer callbacks
-  for (rev_obs_map_t::const_iterator r = robs.begin(); r != robs.end(); ++r) {
-    md_config_obs_t *obs = r->first;
-    obs->handle_conf_change(proxy, r->second);
-  }
-}
-
-template<LockPolicy lp>
-void md_config_impl<lp>::call_all_observers(const ConfigProxy& proxy)
-{
-  std::map<md_config_obs_t*,std::set<std::string> > obs;
-  // Have the scope of the lock extend to the scope of
-  // handle_conf_change since that function expects to be called with
-  // the lock held. (And the comment in config.h says that is the
-  // expected behavior.)
-  //
-  // An alternative might be to pass a std::unique_lock to
-  // handle_conf_change and have a version of get_var that can take it
-  // by reference and lock as appropriate.
-  {
-    for (auto r = observers.begin(); r != observers.end(); ++r) {
-      obs[r->second].insert(r->first);
-    }
-  }
-  for (auto p = obs.begin();
-       p != obs.end();
-       ++p) {
-    p->first->handle_conf_change(proxy, p->second);
-  }
-}
-
 template<LockPolicy lp>
 void md_config_impl<lp>::set_safe_to_start_threads()
 {
@@ -856,6 +760,7 @@ void md_config_impl<lp>::_clear_safe_to_start_threads()
 
 template<LockPolicy lp>
 int md_config_impl<lp>::injectargs(ConfigValues& values,
+                                  const ConfigTracker& tracker,
                                   const std::string& s, std::ostream *oss)
 {
   int ret;
@@ -871,7 +776,7 @@ int md_config_impl<lp>::injectargs(ConfigValues& values,
     *p++ = 0;
     while (*p && *p == ' ') p++;
   }
-  ret = parse_injectargs(values, nargs, oss);
+  ret = parse_injectargs(values, tracker, nargs, oss);
   if (!nargs.empty()) {
     *oss << " failed to parse arguments: ";
     std::string prefix;
@@ -889,11 +794,12 @@ int md_config_impl<lp>::injectargs(ConfigValues& values,
 
 template<LockPolicy lp>
 void md_config_impl<lp>::set_val_or_die(ConfigValues& values,
+                                       const ConfigTracker& tracker,
                                        const std::string &key,
                                        const std::string &val)
 {
   std::stringstream err;
-  int ret = set_val(values, key, val, &err);
+  int ret = set_val(values, tracker, key, val, &err);
   if (ret != 0) {
     std::cerr << "set_val_or_die(" << key << "): " << err.str();
   }
@@ -902,6 +808,7 @@ void md_config_impl<lp>::set_val_or_die(ConfigValues& values,
 
 template<LockPolicy lp>
 int md_config_impl<lp>::set_val(ConfigValues& values,
+                               const ConfigTracker& tracker,
                                const std::string &key, const char *val,
                                std::stringstream *err_ss)
 {
@@ -921,7 +828,7 @@ int md_config_impl<lp>::set_val(ConfigValues& values,
   if (opt_iter != schema.end()) {
     const Option &opt = opt_iter->second;
     std::string error_message;
-    int r = _set_val(values, v, opt, CONF_OVERRIDE, &error_message);
+    int r = _set_val(values, tracker, v, opt, CONF_OVERRIDE, &error_message);
     if (r >= 0) {
       if (err_ss) *err_ss << "Set " << opt.name << " to " << v;
       r = 0;
@@ -1122,18 +1029,19 @@ void md_config_impl<lp>::early_expand_meta(
 }
 
 template<LockPolicy lp>
-void md_config_impl<lp>::finalize_reexpand_meta(ConfigValues& values,
-                                               const ConfigProxy& proxy)
+bool md_config_t::finalize_reexpand_meta(ConfigValues& values,
+                                        const ConfigTracker& tracker)
 {
-  for (auto &i : may_reexpand_meta) {
-    set_val(values, i.first, i.second);
+  for (auto& [name, value] : may_reexpand_meta) {
+    set_val(values, tracker, name, value);
   }
   
-  if (may_reexpand_meta.size()) {
+  if (!may_reexpand_meta.empty()) {
     // meta expands could have modified anything.  Copy it all out again.
     update_legacy_vals(values);
-    _apply_changes(values, proxy, NULL);
-    values.changed.clear();
+    return true;
+  } else {
+    return false;
   }
 }
 
@@ -1391,6 +1299,7 @@ int md_config_impl<lp>::_get_val_from_conf_file(
 template<LockPolicy lp>
 int md_config_impl<lp>::_set_val(
   ConfigValues& values,
+  const ConfigTracker& observers,
   const std::string &raw_val,
   const Option &opt,
   int level,
@@ -1405,7 +1314,7 @@ int md_config_impl<lp>::_set_val(
   // unsafe runtime change?
   if (!opt.can_update_at_runtime() &&
       safe_to_start_threads &&
-      observers.count(opt.name) == 0) {
+      !observers.is_tracking(opt.name)) {
     // accept value if it is not actually a change
     if (new_value != _get_val_nometa(values, opt)) {
       *error_message = string("Configuration option '") + opt.name +
index d3f54e83838e7bebc1b5a871348a1de66436cd8d..9fb577cd5b55583a17fe19cfdbb55bcf7dbfaeb5 100644 (file)
@@ -24,6 +24,7 @@
 #include "common/options.h"
 #include "common/subsys_types.h"
 #include "common/config_fwd.h"
+#include "common/config_tracker.h"
 #include "common/config_values.h"
 
 class CephContext;
@@ -122,47 +123,28 @@ public:
   } opt_type_t;
 
   // Create a new md_config_t structure.
-  explicit md_config_impl(ConfigValues& values, bool is_daemon=false);
+  explicit md_config_impl(ConfigValues& values,
+                         const ConfigTracker& tracker,
+                         bool is_daemon=false);
   ~md_config_impl();
 
-  // Adds a new observer to this configuration. You can do this at any time,
-  // but it will only receive notifications for the changes that happen after
-  // you attach it, obviously.
-  //
-  // Most developers will probably attach their observers after global_init,
-  // but before anyone can call injectargs.
-  //
-  // The caller is responsible for allocating observers.
-  void add_observer(md_config_obs_impl<lock_policy>* observer_);
-
-  // Remove an observer from this configuration.
-  // This doesn't delete the observer! If you allocated it with new(),
-  // you need to delete it yourself.
-  // This function will assert if you try to delete an observer that isn't
-  // there.
-  void remove_observer(md_config_obs_impl<lock_policy>* observer_);
-
   // Parse a config file
-  int parse_config_files(ConfigValues& values, const char *conf_files,
+  int parse_config_files(ConfigValues& values, const ConfigTracker& tracker,
+                        const char *conf_files,
                         std::ostream *warnings, int flags);
 
   // Absorb config settings from the environment
-  void parse_env(ConfigValues& values, const char *env_var = "CEPH_ARGS");
+  void parse_env(ConfigValues& values, const ConfigTracker& tracker,
+                const char *env_var = "CEPH_ARGS");
 
   // Absorb config settings from argv
-  int parse_argv(ConfigValues& values,
+  int parse_argv(ConfigValues& values, const ConfigTracker& tracker,
                 std::vector<const char*>& args, int level=CONF_CMDLINE);
 
   // do any commands we got from argv (--show-config, --show-config-val)
   void do_argv_commands(const ConfigValues& values);
 
-  // Expand all metavariables. Make any pending observer callbacks.
-  void apply_changes(ConfigValues& values, const ConfigProxy& proxy,
-                    std::ostream *oss);
-  void _apply_changes(ConfigValues& values, const ConfigProxy& proxy,
-                     std::ostream *oss);
   bool _internal_field(const string& k);
-  void call_all_observers(const ConfigProxy& proxy);
 
   void set_safe_to_start_threads();
   void _clear_safe_to_start_threads();  // this is only used by the unit test
@@ -172,30 +154,36 @@ public:
 
   /// Set a default value
   void set_val_default(ConfigValues& values,
+                      const ConfigTracker& tracker,
                       const std::string& key, const std::string &val);
 
   /// Set a values from mon
   int set_mon_vals(CephContext *cct,
       ConfigValues& values,
+      const ConfigTracker& tracker,
       const map<std::string,std::string>& kv,
       config_callback config_cb);
 
   // Called by the Ceph daemons to make configuration changes at runtime
   int injectargs(ConfigValues& values,
+                const ConfigTracker& tracker,
                 const std::string &s,
                 std::ostream *oss);
 
   // Set a configuration value, or crash
   // Metavariables will be expanded.
-  void set_val_or_die(ConfigValues& values, const std::string &key, const std::string &val);
+  void set_val_or_die(ConfigValues& values, const ConfigTracker& tracker,
+                     const std::string &key, const std::string &val);
 
   // Set a configuration value.
   // Metavariables will be expanded.
-  int set_val(ConfigValues& values, const std::string &key, const char *val,
+  int set_val(ConfigValues& values, const ConfigTracker& tracker,
+             const std::string &key, const char *val,
               std::stringstream *err_ss=nullptr);
-  int set_val(ConfigValues& values, const std::string &key, const string& s,
+  int set_val(ConfigValues& values, const ConfigTracker& tracker,
+             const std::string &key, const string& s,
               std::stringstream *err_ss=nullptr) {
-    return set_val(values, key, s.c_str(), err_ss);
+    return set_val(values, tracker, key, s.c_str(), err_ss);
   }
 
   /// clear override value
@@ -293,16 +281,19 @@ private:
                              const std::string &key, std::string &out) const;
 
   int parse_option(ConfigValues& values,
+                  const ConfigTracker& tracker,
                   std::vector<const char*>& args,
                   std::vector<const char*>::iterator& i,
                   std::ostream *oss,
                   int level);
   int parse_injectargs(ConfigValues& values,
+                      const ConfigTracker& tracker,
                       std::vector<const char*>& args,
                       std::ostream *oss);
 
   int _set_val(
     ConfigValues& values,
+    const ConfigTracker& tracker,
     const std::string &val,
     const Option &opt,
     int level,  // CONF_*
@@ -330,7 +321,8 @@ public:  // for global_init
                         std::ostream *oss) const;
 
   // for those want to reexpand special meta, e.g, $pid
-  void finalize_reexpand_meta(ConfigValues& values, const ConfigProxy& proxy);
+  bool finalize_reexpand_meta(ConfigValues& values,
+                             const ConfigTracker& tracker);
 private:
 
   /// expand all metavariables in config structure.
@@ -349,8 +341,6 @@ private:
   bool do_show_config = false;
   string do_show_config_value;
 
-  obs_map_t observers;
-
   vector<Option> subsys_options;
 
 public:
diff --git a/src/common/config_obs_mgr.h b/src/common/config_obs_mgr.h
new file mode 100644 (file)
index 0000000..2da4cb0
--- /dev/null
@@ -0,0 +1,128 @@
+// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
+
+#pragma once
+
+#include <map>
+#include <set>
+#include <string>
+
+#include "common/config_tracker.h"
+
+class ConfigValues;
+
+// @c ObserverMgr manages a set of config observers which are interested in
+// the changes of settings at runtime.
+template<class ConfigObs>
+class ObserverMgr : public ConfigTracker {
+  // Maps configuration options to the observer listening for them.
+  using obs_map_t = std::multimap<std::string, ConfigObs*>;
+  obs_map_t observers;
+  /* Maps observers to the configuration options that they care about which
+   * have changed. */
+  using rev_obs_map_t = std::map<ConfigObs*, std::set<std::string>>;
+
+public:
+  // Adds a new observer to this configuration. You can do this at any time,
+  // but it will only receive notifications for the changes that happen after
+  // you attach it, obviously.
+  //
+  // Most developers will probably attach their observers after global_init,
+  // but before anyone can call injectargs.
+  //
+  // The caller is responsible for allocating observers.
+  void add_observer(ConfigObs* observer);
+
+  // Remove an observer from this configuration.
+  // This doesn't delete the observer! If you allocated it with new(),
+  // you need to delete it yourself.
+  // This function will assert if you try to delete an observer that isn't
+  // there.
+  void remove_observer(ConfigObs* observer);
+  template<class ConfigProxyT>
+  void call_all_observers(const ConfigProxyT& proxy);
+  template<class ConfigProxyT>
+  void apply_changes(const std::set<std::string>& changes,
+                    const ConfigProxyT& proxy,
+                    std::ostream *oss);
+  bool is_tracking(const std::string& name) const override;
+};
+
+// we could put the implementations in a .cc file, and only instantiate the
+// used template specializations explicitly, but that forces us to involve
+// unused headers and libraries at compile-time. for instance, for instantiate,
+// to instantiate ObserverMgr for seastar, we will need to include seastar
+// headers to get the necessary types in place, but that would force us to link
+// the non-seastar binaries against seastar libraries. so, to avoid pulling
+// in unused dependencies at the expense of increasing compiling time, we put
+// the implementation in the header file.
+template<class ConfigObs>
+void ObserverMgr<ConfigObs>::add_observer(ConfigObs* observer)
+{
+  const char **keys = observer->get_tracked_conf_keys();
+  for (const char ** k = keys; *k; ++k) {
+    observers.emplace(*k, observer);
+  }
+}
+
+template<class ConfigObs>
+void ObserverMgr<ConfigObs>::remove_observer(ConfigObs* observer)
+{
+  bool found_obs = false;
+  for (auto o = observers.begin(); o != observers.end(); ) {
+    if (o->second == observer) {
+      observers.erase(o++);
+      found_obs = true;
+    } else {
+      ++o;
+    }
+  }
+  assert(found_obs);
+}
+
+template<class ConfigObs>
+template<class ConfigProxyT>
+void ObserverMgr<ConfigObs>::call_all_observers(const ConfigProxyT& proxy)
+{
+  rev_obs_map_t rev_obs;
+  for (const auto& [key, obs] : observers) {
+    rev_obs[obs].insert(key);
+  }
+  for (auto& [obs, keys] : rev_obs) {
+    obs->handle_conf_change(proxy, keys);
+  }
+}
+
+template<class ConfigObs>
+template<class ConfigProxy>
+void ObserverMgr<ConfigObs>::apply_changes(const std::set<std::string>& changes,
+                                           const ConfigProxy& proxy,
+                                           std::ostream *oss)
+{
+  // create the reverse observer mapping, mapping observers to the set of
+  // changed keys that they'll get.
+  rev_obs_map_t robs;
+  string val;
+  for (auto& key : changes) {
+    auto [first, last] = observers.equal_range(key);
+    if ((oss) && !proxy.get_val(key, &val)) {
+      (*oss) << key << " = '" << val << "' ";
+      if (first == last) {
+        (*oss) << "(not observed, change may require restart) ";
+      }
+    }
+    for (auto r = first; r != last; ++r) {
+      auto obs = r->second;
+      robs[obs].insert(key);
+    }
+  }
+  // Make any pending observer callbacks
+  for (auto& [obs, keys] : robs) {
+    obs->handle_conf_change(proxy, keys);
+  }
+}
+
+template<class ConfigObs>
+bool ObserverMgr<ConfigObs>::is_tracking(const std::string& name) const
+{
+  return observers.count(name) > 0;
+}
index 057cf4d17633df7652a8fb77c0a00be104014a49..fe38c3bc98f6022390805733f425cd72a72ae47c 100644 (file)
@@ -5,6 +5,7 @@
 #include <type_traits>
 #include "common/config.h"
 #include "common/config_fwd.h"
+#include "common/config_obs_mgr.h"
 #include "common/Mutex.h"
 
 class ConfigProxy {
@@ -12,6 +13,7 @@ class ConfigProxy {
    * The current values of all settings described by the schema
    */
   ConfigValues values;
+  ObserverMgr<md_config_obs_t> obs_mgr;
   md_config_t config;
   /** A lock that protects the md_config_t internals. It is
    * recursive, for simplicity.
@@ -21,7 +23,7 @@ class ConfigProxy {
 
 public:
   explicit ConfigProxy(bool is_daemon)
-    : config{values, is_daemon},
+    : config{values, obs_mgr, is_daemon},
       lock{"ConfigProxy", true, false}
   {}
   const ConfigValues* operator->() const noexcept {
@@ -91,21 +93,24 @@ public:
     Mutex::Locker l{lock};
     return config.early_expand_meta(values, val, oss);
   }
-  // change `values` in-place
+  // for those want to reexpand special meta, e.g, $pid
   void finalize_reexpand_meta() {
-    Mutex::Locker l{lock};
-    config.finalize_reexpand_meta(values, *this);
+    Mutex::Locker l(lock);
+    if (config.finalize_reexpand_meta(values, obs_mgr)) {
+      obs_mgr.apply_changes(values.changed, *this, nullptr);
+      values.changed.clear();
+    }
   }
   void add_observer(md_config_obs_t* obs) {
-    Mutex::Locker l{lock};
-    config.add_observer(obs);
+    Mutex::Locker l(lock);
+    obs_mgr.add_observer(obs);
   }
   void remove_observer(md_config_obs_t* obs) {
-    Mutex::Locker l{lock};
-    config.remove_observer(obs);
+    Mutex::Locker l(lock);
+    obs_mgr.remove_observer(obs);
   }
   void call_all_observers() {
-    Mutex::Locker l{lock};
+    Mutex::Locker l(lock);
     // Have the scope of the lock extend to the scope of
     // handle_conf_change since that function expects to be called with
     // the lock held. (And the comment in config.h says that is the
@@ -114,7 +119,7 @@ public:
     // An alternative might be to pass a std::unique_lock to
     // handle_conf_change and have a version of get_var that can take it
     // by reference and lock as appropriate.
-    config.call_all_observers(*this);
+    obs_mgr.call_all_observers(*this);
   }
   void set_safe_to_start_threads() {
     config.set_safe_to_start_threads();
@@ -138,51 +143,58 @@ public:
     Mutex::Locker l{lock};
     return config.rm_val(values, key);
   }
+  // Expand all metavariables. Make any pending observer callbacks.
   void apply_changes(std::ostream* oss) {
     Mutex::Locker l{lock};
-    config.apply_changes(values, *this, oss);
+    // apply changes until the cluster name is assigned
+    if (!values.cluster.empty()) {
+      // meta expands could have modified anything.  Copy it all out again.
+      obs_mgr.apply_changes(values.changed, *this, oss);
+      values.changed.clear();
+    }
   }
   int set_val(const std::string& key, const std::string& s,
               std::stringstream* err_ss=nullptr) {
     Mutex::Locker l{lock};
-    return config.set_val(values, key, s);
+    return config.set_val(values, obs_mgr, key, s);
   }
   void set_val_default(const std::string& key, const std::string& val) {
     Mutex::Locker l{lock};
-    config.set_val_default(values, key, val);
+    config.set_val_default(values, obs_mgr, key, val);
   }
   void set_val_or_die(const std::string& key, const std::string& val) {
     Mutex::Locker l{lock};
-    config.set_val_or_die(values, key, val);
+    config.set_val_or_die(values, obs_mgr, key, val);
   }
   int set_mon_vals(CephContext *cct,
                   const map<std::string,std::string>& kv,
                   md_config_t::config_callback config_cb) {
     Mutex::Locker l{lock};
-    int ret = config.set_mon_vals(cct, values, kv, config_cb);
-    config._apply_changes(values, *this, nullptr);
+    int ret = config.set_mon_vals(cct, values, obs_mgr, kv, config_cb);
+    obs_mgr.apply_changes(values.changed, *this, nullptr);
     values.changed.clear();
     return ret;
   }
   int injectargs(const std::string &s, std::ostream *oss) {
     Mutex::Locker l{lock};
-    int ret = config.injectargs(values, s, oss);
-    config._apply_changes(values, *this, oss);
+    int ret = config.injectargs(values, obs_mgr, s, oss);
+    obs_mgr.apply_changes(values.changed, *this, oss);
     values.changed.clear();
     return ret;
   }
   void parse_env(const char *env_var = "CEPH_ARGS") {
     Mutex::Locker l{lock};
-    config.parse_env(values, env_var);
+    config.parse_env(values, obs_mgr, env_var);
   }
   int parse_argv(std::vector<const char*>& args, int level=CONF_CMDLINE) {
     Mutex::Locker l{lock};
-    return config.parse_argv(values, args, level);
+    return config.parse_argv(values, obs_mgr, args, level);
   }
   int parse_config_files(const char *conf_files,
                         std::ostream *warnings, int flags) {
     Mutex::Locker l{lock};
-    return config.parse_config_files(values, conf_files, warnings, flags);
+    return config.parse_config_files(values, obs_mgr,
+                                    conf_files, warnings, flags);
   }
   size_t num_parse_errors() const {
     return config.parse_errors.size();
diff --git a/src/common/config_tracker.h b/src/common/config_tracker.h
new file mode 100644 (file)
index 0000000..783e6f2
--- /dev/null
@@ -0,0 +1,18 @@
+// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
+
+#pragma once
+
+#include <string>
+
+// @ConfigTracker is queried to see if any added observers is tracking one or
+// more changed settings.
+//
+// this class is introduced in hope to decouple @c md_config_t from any instantiated
+// class of @c ObserverMgr, as what the former wants is but @c is_tracking(), and to
+// make ObserverMgr a template parameter of md_config_t's methods just complicates
+// the dependencies between header files, and slows down the compiling.
+class ConfigTracker {
+public:
+  virtual ~ConfigTracker() = default;
+  virtual bool is_tracking(const std::string& name) const = 0;
+};