TS-4099: add automatic metrics namespace management Rather than hardcoding the set of metrics namespace prefixes, poll it at runtime. We keep polling until the namespace converges and periodically thereafter to account for metrics created by plugins.
Project: http://git-wip-us.apache.org/repos/asf/trafficserver/repo Commit: http://git-wip-us.apache.org/repos/asf/trafficserver/commit/b547b651 Tree: http://git-wip-us.apache.org/repos/asf/trafficserver/tree/b547b651 Diff: http://git-wip-us.apache.org/repos/asf/trafficserver/diff/b547b651 Branch: refs/heads/master Commit: b547b651b662af16f6866d524b7661176e2b2e92 Parents: 4e44756 Author: James Peach <[email protected]> Authored: Wed Dec 30 16:36:24 2015 -0800 Committer: James Peach <[email protected]> Committed: Thu Jan 21 19:21:30 2016 -0800 ---------------------------------------------------------------------- cmd/traffic_manager/metrics.cc | 77 +++++++++++++++---------------------- lib/bindings/bindings.cc | 73 +++++++++++++++++++++++++++-------- lib/bindings/bindings.h | 8 ++-- lib/bindings/metrics.cc | 42 ++++++++++++++++++++ lib/bindings/metrics.h | 7 ++++ 5 files changed, 142 insertions(+), 65 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/trafficserver/blob/b547b651/cmd/traffic_manager/metrics.cc ---------------------------------------------------------------------- diff --git a/cmd/traffic_manager/metrics.cc b/cmd/traffic_manager/metrics.cc index 7bc5bec..9608a60 100644 --- a/cmd/traffic_manager/metrics.cc +++ b/cmd/traffic_manager/metrics.cc @@ -127,7 +127,7 @@ private: }; struct EvaluatorList { - EvaluatorList() : passes(0) {} + EvaluatorList() : update(true), passes(0) {} ~EvaluatorList() { forv_Vec(Evaluator, e, this->evaluators) { delete e; } @@ -147,15 +147,27 @@ struct EvaluatorList { forv_Vec(Evaluator, e, this->evaluators) { e->eval(L); } - elapsed = ink_hrtime_diff(ink_get_hrtime_internal(), start); Debug("lua", "evaluated %u metrics in %fmsec", evaluators.length(), ink_hrtime_to_usec(elapsed) / 1000.0); } + bool update; int64_t passes; Vec<Evaluator *> evaluators; }; +static int +update_metrics_namespace(lua_State *L) +{ + lua_Integer count; + + lua_metrics_install(L); + count = lua_tointeger(L, 1); + lua_pop(L, 1); + + return count; +} + static int64_t timestamp_now_msec() { @@ -305,46 +317,6 @@ metrics_cluster_sum(lua_State *L) return 1; } -static void -register_metrics_namespace(BindingInstance &binding) -{ - // XXX Currently known metrics namespace prefixes. Figure out a way to - // add new metrics nodes as new prefixes are added. - const char *prefixes[] = { - "proxy.cluster", "proxy.cluster.cache", "proxy.cluster.cache.contents", "proxy.cluster.dns", "proxy.cluster.hostdb", - "proxy.cluster.http", "proxy.cluster.log", "proxy.node", "proxy.node.cache", "proxy.node.cache.contents", "proxy.node.cluster", - "proxy.node.config", "proxy.node.config.restart_required", "proxy.node.dns", "proxy.node.hostdb", "proxy.node.http", - "proxy.node.http.transaction_counts_avg_10s", "proxy.node.http.transaction_counts_avg_10s.errors", - "proxy.node.http.transaction_counts_avg_10s.other", "proxy.node.http.transaction_frac_avg_10s", - "proxy.node.http.transaction_frac_avg_10s.errors", "proxy.node.http.transaction_frac_avg_10s.other", - "proxy.node.http.transaction_msec_avg_10s", "proxy.node.http.transaction_msec_avg_10s.errors", - "proxy.node.http.transaction_msec_avg_10s.other", "proxy.node.log", "proxy.node.restarts.manager", "proxy.node.restarts.proxy", - "proxy.node.version.manager", "proxy.process.cache", "proxy.process.cache.direntries", "proxy.process.cache.evacuate", - "proxy.process.cache.frags_per_doc", "proxy.process.cache.frags_per_doc.3+", "proxy.process.cache.lookup", - "proxy.process.cache.ram_cache", "proxy.process.cache.read", "proxy.process.cache.read_busy", "proxy.process.cache.remove", - "proxy.process.cache.scan", "proxy.process.cache.sync", "proxy.process.cache.update", "proxy.process.cache.write", - "proxy.process.cache.write.backlog", "proxy.process.cluster", "proxy.process.congestion", "proxy.process.dns", - "proxy.process.hostdb", "proxy.process.http", "proxy.process.http.milestone", "proxy.process.http.transaction_counts", - "proxy.process.http.transaction_counts.errors", "proxy.process.http.transaction_counts.hit_fresh", - "proxy.process.http.transaction_counts.other", "proxy.process.http.transaction_totaltime", - "proxy.process.http.transaction_totaltime.errors", "proxy.process.http.transaction_totaltime.hit_fresh", - "proxy.process.http.transaction_totaltime.other", "proxy.process.http.websocket", "proxy.process.http2", "proxy.process.https", - "proxy.process.log", "proxy.process.net", "proxy.process.socks", "proxy.process.ssl", "proxy.process.ssl.cipher.user_agent", - "proxy.process.version.server", - }; - - // Register the metrics userdata type. - lua_metrics_register(binding.lua); - - // Bind metric nodes to all the metrics namespace prefixes. - for (unsigned i = 0; i < countof(prefixes); ++i) { - if (lua_metrics_new(prefixes[i], binding.lua) == 1) { - binding.bind_value(prefixes[i], -1); - lua_pop(binding.lua, 1); - } - } -} - bool metrics_binding_initialize(BindingInstance &binding) { @@ -355,8 +327,9 @@ metrics_binding_initialize(BindingInstance &binding) mgmt_fatal(stderr, 0, "failed to initialize Lua runtime\n"); } - // Register the metrics bindings. - register_metrics_namespace(binding); + // Register the metrics userdata type. + lua_metrics_register(binding.lua); + update_metrics_namespace(binding.lua); // Register our own API. binding.bind_function("integer", metrics_create_integer); @@ -371,7 +344,11 @@ metrics_binding_initialize(BindingInstance &binding) binding.attach_ptr("evaluators", new EvaluatorList()); // Finally, execute the config file. - return binding.require(config.get()); + if (binding.require(config.get())) { + return true; + } + + return false; } void @@ -392,7 +369,17 @@ metrics_binding_evaluate(BindingInstance &binding) evaluators = (EvaluatorList *)binding.retrieve_ptr("evaluators"); ink_release_assert(evaluators != NULL); + // Keep updating the namespace until it settles (ie. we make 0 updates). + if (evaluators->update) { + evaluators->update = update_metrics_namespace(binding.lua) ? true : false; + } + binding.bind_constant("metrics.now.msec", timestamp_now_msec()); binding.bind_constant("metrics.update.pass", ++evaluators->passes); evaluators->evaluate(binding.lua); + + // Periodically refresh the namespace to catch newly added metrics. + if (evaluators->passes % 10 == 0) { + evaluators->update = true; + } } http://git-wip-us.apache.org/repos/asf/trafficserver/blob/b547b651/lib/bindings/bindings.cc ---------------------------------------------------------------------- diff --git a/lib/bindings/bindings.cc b/lib/bindings/bindings.cc index 9cd01b8..d189e52 100644 --- a/lib/bindings/bindings.cc +++ b/lib/bindings/bindings.cc @@ -56,36 +56,49 @@ BindingInstance::retrieve_ptr(const char *name) return (ptr == this->attachments.end()) ? NULL : ptr->second; } -void +bool BindingInstance::bind_constant(const char *name, lua_Integer value) { + bool bound; + lua_pushinteger(this->lua, value); - this->bind_value(name, -1); + bound = this->bind_value(name, -1); lua_pop(this->lua, 1); + + return bound; } -void +bool BindingInstance::bind_constant(const char *name, const char *value) { + bool bound; + lua_pushlstring(this->lua, value, strlen(value)); - this->bind_value(name, -1); + bound = this->bind_value(name, -1); lua_pop(this->lua, 1); + + return bound; } -void +bool BindingInstance::bind_function(const char *name, int (*value)(lua_State *)) { + bool bound; + lua_pushcfunction(this->lua, value); - this->bind_value(name, -1); + bound = this->bind_value(name, -1); lua_pop(this->lua, 1); + + return bound; } // Bind an arbitrary Lua value from the give stack position. -void +bool BindingInstance::bind_value(const char *name, int value) { - const char * start = name; - const char * end = name; + const char *start = name; + const char *end = name; + bool bound = false; int depth = 0; @@ -161,18 +174,48 @@ BindingInstance::bind_value(const char *name, int value) // If we pushed a series of tables onto the stack, bind the name to a table // entry. otherwise bind it as a global name. if (depth) { + bool isnil; + + // At this point the top of stack should be something indexable. + ink_assert(is_indexable(this->lua, -1)); + + Debug("lua", "stack depth is %d (expected %d)\n", lua_gettop(this->lua), depth); + // Push the index name. lua_pushstring(this->lua, start); - lua_pushvalue(this->lua, value); - ink_assert(is_indexable(this->lua, -3)); + Debug("lua", "stack depth is %d (expected %d)\n", lua_gettop(this->lua), depth); + // Fetch the index (without metamethods); + lua_gettable(this->lua, -2); + + // Only push the value if it is currently nil. + isnil = lua_isnil(this->lua, -1); + lua_pop(this->lua, 1); + Debug("lua", "isnil? %s", isnil ? "yes" : "no"); + + if (isnil) { + lua_pushstring(this->lua, start); + lua_pushvalue(this->lua, value); + lua_settable(this->lua, -3); + bound = true; + } - lua_settable(this->lua, -3); Debug("lua", "stack depth is %d (expected %d)\n", lua_gettop(this->lua), depth); lua_pop(this->lua, depth); } else { - lua_pushvalue(this->lua, value); - lua_setglobal(this->lua, start); + bool isnil; + + lua_getglobal(this->lua, start); + isnil = lua_isnil(this->lua, -1); + lua_pop(this->lua, 1); + + if (isnil) { + lua_pushvalue(this->lua, value); + lua_setglobal(this->lua, start); + bound = true; + } } + + return bound; } bool @@ -271,5 +314,3 @@ BindingInstance::register_metatable(lua_State *lua, const char *name, const luaL ink_assert(lua_gettop(lua) == 0); } - -/* vim: set sw=4 ts=4 tw=79 et: */ http://git-wip-us.apache.org/repos/asf/trafficserver/blob/b547b651/lib/bindings/bindings.h ---------------------------------------------------------------------- diff --git a/lib/bindings/bindings.h b/lib/bindings/bindings.h index 525cb60..55f78a0 100644 --- a/lib/bindings/bindings.h +++ b/lib/bindings/bindings.h @@ -41,10 +41,10 @@ struct BindingInstance { // Bind values to the specified global name. If the name contains '.' // separators, intermediate tables are constucted and the value is bound // to the final path component. - void bind_constant(const char *name, lua_Integer value); - void bind_constant(const char *name, const char *value); - void bind_function(const char *name, int (*value)(lua_State *)); - void bind_value(const char *name, int value); + bool bind_constant(const char *name, lua_Integer value); + bool bind_constant(const char *name, const char *value); + bool bind_function(const char *name, int (*value)(lua_State *)); + bool bind_value(const char *name, int value); // Attach a named pointer that we can later fish out from a Lua state. void attach_ptr(const char *, void *); http://git-wip-us.apache.org/repos/asf/trafficserver/blob/b547b651/lib/bindings/metrics.cc ---------------------------------------------------------------------- diff --git a/lib/bindings/metrics.cc b/lib/bindings/metrics.cc index 66dbacf..ae148ee 100644 --- a/lib/bindings/metrics.cc +++ b/lib/bindings/metrics.cc @@ -26,6 +26,7 @@ #include "P_RecCore.h" #include "ts/ink_memory.h" #include <map> +#include <set> #define BINDING "lua.metrics" @@ -199,3 +200,44 @@ lua_metrics_register(lua_State *L) BindingInstance::register_metatable(L, BINDING, metatable); } + +static void +install_metrics_object(RecT rec_type, void *edata, int registered, const char *name, int data_type, RecData *datum) +{ + std::set<std::string> *prefixes = (std::set<std::string> *)edata; + + if (likely(registered)) { + const char *end = strrchr(name, '.'); + ptrdiff_t len = end - name; + prefixes->insert(std::string(name, len)); + } +} + +int +lua_metrics_install(lua_State *L) +{ + int count = 0; + int metrics_type = RECT_NODE | RECT_PROCESS | RECT_CLUSTER | RECT_PLUGIN; + BindingInstance *binding = BindingInstance::self(L); + std::set<std::string> prefixes; + + // Gather all the metrics namespace prefixes into a sorted set. We want to install + // metrics objects as the last branch of the namespace so that leaf metrics lookup + // end up indexing metrics objects. + RecDumpRecords((RecT)metrics_type, install_metrics_object, &prefixes); + + for (std::set<std::string>::const_iterator p = prefixes.cbegin(); p != prefixes.cend(); ++p) { + if (lua_metrics_new(p->c_str(), binding->lua) == 1) { + if (binding->bind_value(p->c_str(), -1)) { + Debug("lua", "installed metrics object at prefix %s", p->c_str()); + ++count; + } + + lua_pop(binding->lua, 1); + } + } + + // Return the number of metrics we installed; + lua_pushinteger(L, count); + return 1; +} http://git-wip-us.apache.org/repos/asf/trafficserver/blob/b547b651/lib/bindings/metrics.h ---------------------------------------------------------------------- diff --git a/lib/bindings/metrics.h b/lib/bindings/metrics.h index e0d39ac..9e42c16 100644 --- a/lib/bindings/metrics.h +++ b/lib/bindings/metrics.h @@ -30,4 +30,11 @@ int lua_metrics_new(const char *prefix, lua_State *L); // Register metrics binding type metatable. void lua_metrics_register(lua_State *L); +// Install new metrics objects into the global namespace. This function +// iterates over all the registered metrics and installs a metrics +// object at the global name given by the metric's prefix. For example, +// if the metric is named "proxy.my.great.counter", it would install +// a metrics object at the global name "proxy.my.great". +int lua_metrics_install(lua_State *L); + #endif /* METRICS_H_FED1F5EA_9EDE_48E6_B05A_5DCAFD8DC319 */
