From 0d7fe9bd89d05759172986a15e587797eb148909 Mon Sep 17 00:00:00 2001 From: Amnon Heiman Date: Sun, 26 Jul 2015 13:29:13 +0300 Subject: [PATCH 1/3] Adding stats to column_family This adds the stats object to column_family. It set the write counter in the write path and support the pending_flush counter. The stats object contains information for switch_count, number of pending flushes, and counters for read, write, and range. Signed-off-by: Amnon Heiman --- database.hh | 27 +++++++++++++++++++++++++-- 1 file changed, 25 insertions(+), 2 deletions(-) diff --git a/database.hh b/database.hh index 3c2fa2c5b2..65fd3147ec 100644 --- a/database.hh +++ b/database.hh @@ -88,9 +88,19 @@ public: bool enable_commitlog = true; }; struct no_commitlog {}; + struct stats { + /** Number of times flush has resulted in the memtable being switched out. */ + int64_t memtable_switch_count = 0; + /** Estimated number of tasks pending for this column family */ + int64_t pending_flushes = 0; + int64_t reads = 0; + int64_t writes = 0; + }; + private: schema_ptr _schema; config _config; + stats _stats; lw_shared_ptr _memtables; // generation -> sstable. Ordered by key so we can easily get the most recent. lw_shared_ptr _sstables; @@ -107,7 +117,6 @@ private: private: void add_sstable(sstables::sstable&& sstable); void add_memtable(); - memtable& active_memtable() { return *_memtables->back(); } future<> update_cache(memtable&); struct merge_comparator; private: @@ -132,6 +141,7 @@ public: // FIXME: in case a query is satisfied from a single memtable, avoid a copy using const_mutation_partition_ptr = std::unique_ptr; using const_row_ptr = std::unique_ptr; + memtable& active_memtable() { return *_memtables->back(); } public: column_family(schema_ptr schema, config cfg, db::commitlog& cl); column_family(schema_ptr schema, config cfg, no_commitlog); @@ -156,7 +166,14 @@ public: future<> flush() { // FIXME: this will synchronously wait for this write to finish, but doesn't guarantee // anything about previous writes. - return seal_active_memtable(); + _stats.pending_flushes++; + return seal_active_memtable().finally([this]() mutable { + _stats.pending_flushes--; + // In origin memtable_switch_count is incremented inside + // ColumnFamilyMeetrics Flush.run + _stats.memtable_switch_count++; + return make_ready_future<>(); + }); } // FIXME: this is just an example, should be changed to something more // general. compact_all_sstables() starts a compaction of all sstables. @@ -172,6 +189,10 @@ public: void start_compaction(); void trigger_compaction(); void set_compaction_strategy(sstables::compaction_strategy_type strategy); + const stats& get_stats() const { + return _stats; + } + private: // One does not need to wait on this future if all we are interested in, is // initiating the write. The writes initiated here will eventually @@ -440,6 +461,7 @@ inline void column_family::apply(const mutation& m, const db::replay_position& rp) { active_memtable().apply(m, rp); + _stats.writes++; seal_on_overflow(); } @@ -466,6 +488,7 @@ void column_family::apply(const frozen_mutation& m, const db::replay_position& rp) { check_valid_rp(rp); active_memtable().apply(m, rp); + _stats.writes++; seal_on_overflow(); } From 8356b493a3e41621b9c6384ade6ab3435b0e996c Mon Sep 17 00:00:00 2001 From: Amnon Heiman Date: Sun, 26 Jul 2015 09:14:31 +0300 Subject: [PATCH 2/3] API: Adding read and write counters to column_family definition This adds the read and write counters to the column_family swagger definitions. It adds the following commands: get_read get_all_read get_write get_all_write Signed-off-by: Amnon Heiman --- api/api-doc/column_family.json | 86 ++++++++++++++++++++++++++++++++++ 1 file changed, 86 insertions(+) diff --git a/api/api-doc/column_family.json b/api/api-doc/column_family.json index d2dc68c451..2e4df149ff 100644 --- a/api/api-doc/column_family.json +++ b/api/api-doc/column_family.json @@ -915,6 +915,49 @@ } ] }, + { + "path":"/column_family/metrics/read/{name}", + "operations":[ + { + "method":"GET", + "summary":"Get number of reads", + "type":"long", + "nickname":"get_read", + "produces":[ + "application/json" + ], + "parameters":[ + { + "name":"name", + "description":"The column family name in keysspace:name format", + "required":true, + "allowMultiple":false, + "type":"string", + "paramType":"path" + } + ] + } + ] + }, + { + "path":"/column_family/metrics/read/", + "operations":[ + { + "method":"GET", + "summary":"Get number of reads from all column family, per shard", + "type":"array", + "items":{ + "type":"long" + }, + "nickname":"get_all_read", + "produces":[ + "application/json" + ], + "parameters":[ + ] + } + ] + }, { "path":"/column_family/metrics/read_latency", "operations":[ @@ -995,6 +1038,49 @@ } ] }, + { + "path":"/column_family/metrics/write/{name}", + "operations":[ + { + "method":"GET", + "summary":"Get number of writes", + "type":"long", + "nickname":"get_write", + "produces":[ + "application/json" + ], + "parameters":[ + { + "name":"name", + "description":"The column family name in keysspace:name format", + "required":true, + "allowMultiple":false, + "type":"string", + "paramType":"path" + } + ] + } + ] + }, + { + "path":"/column_family/metrics/write/", + "operations":[ + { + "method":"GET", + "summary":"Get number of writes from all column family, per shard", + "type":"array", + "items":{ + "type":"long" + }, + "nickname":"get_all_write", + "produces":[ + "application/json" + ], + "parameters":[ + ] + } + ] + }, { "path":"/column_family/metrics/write_latency", "operations":[ From cea73277ca8a6191ff7a45aef66846dfccc60f89 Mon Sep 17 00:00:00 2001 From: Amnon Heiman Date: Sun, 26 Jul 2015 09:20:56 +0300 Subject: [PATCH 3/3] API: Add read, write, and flush statistic to column_family This adds the API implementation for the read, write, number of panding flushes and memtable switch count. The implementation uses a helper function to perform map and map_reduce on column_family. The get_uuid helper method now supports both colon notations (i.e. either as a ":" or as %3A) Signed-off-by: Amnon Heiman --- api/column_family.cc | 153 +++++++++++++++++++++++++++++-------------- 1 file changed, 104 insertions(+), 49 deletions(-) diff --git a/api/column_family.cc b/api/column_family.cc index 0354675480..f416d50142 100644 --- a/api/column_family.cc +++ b/api/column_family.cc @@ -5,16 +5,95 @@ #include "column_family.hh" #include "api/api-doc/column_family.json.hh" #include +#include "http/exception.hh" namespace api { +using namespace httpd; using namespace std; +using namespace json; namespace cf = httpd::column_family_json; auto get_uuid(const sstring& name, const database& db) { - auto pos = name.find(':'); - return db.find_uuid(name.substr(0, pos), name.substr(pos + 1)); + auto pos = name.find("%3A"); + size_t end; + if (pos == sstring::npos) { + pos = name.find(":"); + if (pos == sstring::npos) { + throw bad_param_exception("Column family name should be in keyspace::column_family format"); + } + end = pos + 1; + } else { + end = pos + 3; + } + try { + return db.find_uuid(name.substr(0, pos), name.substr(end)); + } catch (std::out_of_range& e) { + throw bad_param_exception("Column family '" + name.substr(0, pos) + ":" + + name.substr(end) + "' not found"); + } } + +future<> foreach_column_family(http_context& ctx, const sstring& name, function f) { + auto uuid = get_uuid(name, ctx.db.local()); + + return ctx.db.invoke_on_all([f, uuid](database& db) { + f(db.find_column_family(uuid)); + }); +} + +template +future map_reduce_cf(http_context& ctx, const sstring& name, I init, + Mapper mapper, Reducer reducer) { + auto uuid = get_uuid(name, ctx.db.local()); + return ctx.db.map_reduce0([mapper, uuid](database& db) { + return mapper(db.find_column_family(uuid)); + }, init, reducer).then([](const I& res) { + return make_ready_future(res); + }); +} + +template +future map_reduce_cf(http_context& ctx, I init, + Mapper mapper, Reducer reducer) { + return ctx.db.map_reduce0([mapper, init, reducer](database& db) { + auto res = init; + for (auto i : db.get_column_families()) { + res = reducer(res, mapper(*i.second.get())); + } + return res; + }, init, reducer).then([](const I& res) { + return make_ready_future(res); + }); +} + +static future get_cf_stats(http_context& ctx, const sstring& name, + int64_t column_family::stats::*f) { + return map_reduce_cf(ctx, name, 0, [f](const column_family& cf) { + return cf.get_stats().*f; + }, std::plus()); +} + +static future get_cf_stats(http_context& ctx, + int64_t column_family::stats::*f) { + return map_reduce_cf(ctx, 0, [f](const column_family& cf) { + return cf.get_stats().*f; + }, std::plus()); +} + +static future map_cf_stats(http_context& ctx, + int64_t column_family::stats::*f) { + return ctx.db.map([f](const database& db) { + int64_t res = 0; + for (auto i : db.get_column_families()) { + res += i.second.get()->get_stats().*f; + } + return res; + }).then([](const std::vector& res) { + return make_ready_future(res); + }); +} + void set_column_family(http_context& ctx, routes& r) { cf::get_column_family_name.set(r, [&ctx] (const_req req){ vector res; @@ -45,14 +124,15 @@ void set_column_family(http_context& ctx, routes& r) { }); cf::get_memtable_columns_count.set(r, [&ctx] (std::unique_ptr req) { - //TBD - //auto id = get_uuid(req->param["name"], ctx.db.local()); - return make_ready_future(0); + return map_reduce_cf(ctx, req->param["name"], 0, [](column_family& cf) { + return cf.active_memtable().all_partitions().size(); + }, std::plus()); }); - cf::get_all_memtable_columns_count.set(r, [] (std::unique_ptr req) { - //TBD - return make_ready_future(0); + cf::get_all_memtable_columns_count.set(r, [&ctx] (std::unique_ptr req) { + return map_reduce_cf(ctx, 0, [](column_family& cf) { + return cf.active_memtable().all_partitions().size(); + }, std::plus()); }); cf::get_memtable_on_heap_size.set(r, [] (std::unique_ptr req) { @@ -121,15 +201,12 @@ void set_column_family(http_context& ctx, routes& r) { return make_ready_future(0); }); - cf::get_memtable_switch_count.set(r, [] (std::unique_ptr req) { - //TBD - //auto id = get_uuid(req->param["name"], ctx.db.local()); - return make_ready_future(0); + cf::get_memtable_switch_count.set(r, [&ctx] (std::unique_ptr req) { + return get_cf_stats(ctx,req->param["name"] ,&column_family::stats::memtable_switch_count); }); - cf::get_all_memtable_switch_count.set(r, [] (std::unique_ptr req) { - //TBD - return make_ready_future(0); + cf::get_all_memtable_switch_count.set(r, [&ctx] (std::unique_ptr req) { + return get_cf_stats(ctx, &column_family::stats::memtable_switch_count); }); cf::get_estimated_row_size_histogram.set(r, [] (std::unique_ptr req) { @@ -157,49 +234,28 @@ void set_column_family(http_context& ctx, routes& r) { return make_ready_future(0); }); - cf::get_read_latency.set(r, [] (std::unique_ptr req) { - //TBD - //auto id = get_uuid(req->param["name"], ctx.db.local()); - return make_ready_future(0); + cf::get_pending_flushes.set(r, [&ctx] (std::unique_ptr req) { + return get_cf_stats(ctx,req->param["name"] ,&column_family::stats::pending_flushes); }); - cf::get_all_read_latency.set(r, [] (std::unique_ptr req) { - //TBD - //auto id = get_uuid(req->param["name"], ctx.db.local()); - return make_ready_future(0); + cf::get_all_pending_flushes.set(r, [&ctx] (std::unique_ptr req) { + return get_cf_stats(ctx, &column_family::stats::pending_flushes); }); - cf::get_range_latency.set(r, [] (std::unique_ptr req) { - //TBD - //auto id = get_uuid(req->param["name"], ctx.db.local()); - return make_ready_future(0); + cf::get_read.set(r, [&ctx] (std::unique_ptr req) { + return get_cf_stats(ctx,req->param["name"] ,&column_family::stats::reads); }); - cf::get_all_range_latency.set(r, [] (std::unique_ptr req) { - //TBD - return make_ready_future(0); + cf::get_all_read.set(r, [&ctx] (std::unique_ptr req) { + return map_cf_stats(ctx, &column_family::stats::reads); }); - cf::get_write_latency.set(r, [] (std::unique_ptr req) { - //TBD - //auto id = get_uuid(req->param["name"], ctx.db.local()); - return make_ready_future(0); + cf::get_write.set(r, [&ctx] (std::unique_ptr req) { + return get_cf_stats(ctx,req->param["name"] ,&column_family::stats::writes); }); - cf::get_all_write_latency.set(r, [] (std::unique_ptr req) { - //TBD - return make_ready_future(0); - }); - - cf::get_pending_flushes.set(r, [] (std::unique_ptr req) { - //TBD - //auto id = get_uuid(req->param["name"], ctx.db.local()); - return make_ready_future(0); - }); - - cf::get_all_pending_flushes.set(r, [] (std::unique_ptr req) { - //TBD - return make_ready_future(0); + cf::get_all_write.set(r, [&ctx] (std::unique_ptr req) { + return map_cf_stats(ctx, &column_family::stats::writes); }); cf::get_pending_compactions.set(r, [] (std::unique_ptr req) { @@ -474,6 +530,5 @@ void set_column_family(http_context& ctx, routes& r) { std::vector res; return make_ready_future(res); }); - } }