add_library(crimson::cflags INTERFACE IMPORTED)
set(crimson_cflag_definitions "WITH_CRIMSON=1")
+option(WITH_CRIMSON_DETAILED_SAMPLING
+ "Enable detailed onode-tree sampling metrics in crimson-seastore" OFF)
+if(WITH_CRIMSON_DETAILED_SAMPLING)
+ list(APPEND crimson_cflag_definitions "CRIMSON_DETAILED_SAMPLING=1")
+endif()
+
set_target_properties(crimson::cflags PROPERTIES
INTERFACE_COMPILE_DEFINITIONS "${crimson_cflag_definitions}"
INTERFACE_LINK_LIBRARIES Seastar::seastar)
eagain_ifuture<Node::search_result_t> Node::lower_bound(
context_t c, const key_hobj_t& key)
{
+#ifdef CRIMSON_DETAILED_SAMPLING
+ ++c.t.get_onode_tree_stats().lookup_count;
+#endif
return seastar::do_with(
MatchHistory(), [this, c, &key](auto& history) {
return lower_bound_tracked(c, key, history);
value_config_t vconf,
Ref<Node>&& this_ref)
{
+#ifdef CRIMSON_DETAILED_SAMPLING
+ ++c.t.get_onode_tree_stats().lookup_count;
+#endif
return seastar::do_with(
MatchHistory(), [this, c, &key, vconf,
this_ref = std::move(this_ref)] (auto& history) mutable {
InternalNode::lower_bound_tracked(
context_t c, const key_hobj_t& key, MatchHistory& history)
{
+#ifdef CRIMSON_DETAILED_SAMPLING
+ auto& tstats = c.t.get_onode_tree_stats();
+ ++tstats.nodes_visited;
+ auto cmp0 = key.ncmp_str;
+ auto result = impl->lower_bound(key, history);
+ tstats.string_cmp_count += key.ncmp_str - cmp0;
+#else
auto result = impl->lower_bound(key, history);
+#endif
return get_or_track_child(c, result.position, result.p_value->value
).si_then([c, &key, &history](auto child) {
// XXX(multi-type): pass result.mstat to child
context_t c, const key_hobj_t& key, MatchHistory& history)
{
key_view_t index_key;
+#ifdef CRIMSON_DETAILED_SAMPLING
+ auto& tstats = c.t.get_onode_tree_stats();
+ ++tstats.nodes_visited;
+ auto cmp0 = key.ncmp_str;
+ auto result = impl->lower_bound(key, history, &index_key);
+ tstats.string_cmp_count += key.ncmp_str - cmp0;
+#else
auto result = impl->lower_bound(key, history, &index_key);
+#endif
Ref<tree_cursor_t> cursor;
if (result.position.is_end()) {
assert(!result.p_value);
*/
class key_hobj_t {
public:
+ // number of ns/oid comparisons this query key participated in during lookup.
+ // Only incremented under CRIMSON_DETAILED_SAMPLING; otherwise stays zero.
+ mutable uint64_t ncmp_str = 0;
+
explicit key_hobj_t(const ghobject_t& _ghobj) {
if (_ghobj.is_max()) {
ghobj = _MAX_OID();
template <IsFullKey T>
auto operator<=>(const T& key, const ns_oid_view_t& target) {
+#ifdef CRIMSON_DETAILED_SAMPLING
+ // only key_hobj_t carries the counter
+ if constexpr (requires { key.ncmp_str; }) {
+ ++key.ncmp_str;
+ }
+#endif
auto ret = key.nspace() <=> string_view_masked_t{target.nspace};
if (ret != 0)
return ret;
);
}
+ metrics.add_group(
+ "onode_tree",
+ {
+ sm::make_counter(
+ "onode_lookups",
+ [this] { return stats.onode_lookups; },
+ sm::description("onode-tree find + insert search"),
+ {sm::label_instance("shard_store_index", std::to_string(store_index))}
+ ),
+ sm::make_counter(
+ "onode_lookup_nodes",
+ [this] { return stats.onode_lookup_nodes; },
+ sm::description("nodes searched across onode-tree"),
+ {sm::label_instance("shard_store_index", std::to_string(store_index))}
+ ),
+ sm::make_counter(
+ "onode_lookup_str_cmp_count",
+ [this] { return stats.onode_str_cmp_count; },
+ sm::description("ns/oid key comparisons during onode-tree ops"),
+ {sm::label_instance("shard_store_index", std::to_string(store_index))}
+ ),
+ sm::make_counter(
+ "onode_inserts",
+ [this] { return stats.onode_inserts; },
+ sm::description("onode-tree key inserts"),
+ {sm::label_instance("shard_store_index", std::to_string(store_index))}
+ ),
+ sm::make_counter(
+ "onode_updates",
+ [this] { return stats.onode_updates; },
+ sm::description("onode-tree value updates"),
+ {sm::label_instance("shard_store_index", std::to_string(store_index))}
+ ),
+ sm::make_counter(
+ "onode_erases",
+ [this] { return stats.onode_erases; },
+ sm::description("onode-tree key erases"),
+ {sm::label_instance("shard_store_index", std::to_string(store_index))}
+ ),
+ sm::make_gauge(
+ "onode_extents_delta",
+ [this] { return stats.onode_extents_delta; },
+ sm::description("net onode-tree extents added(+)/removed(-)"),
+ {sm::label_instance("shard_store_index", std::to_string(store_index))}
+ ),
+ }
+ );
+
metrics.add_group(
"seastore",
{
add_latency_sample(
op_type_t::DO_TRANSACTION,
std::chrono::steady_clock::now() - ctx.begin_timestamp);
+ add_onode_tree_sample(ctx.transaction->get_onode_tree_stats());
throttler.put();
std::array<seastar::metrics::histogram, LAT_MAX> op_lat;
seastar::metrics::histogram conflict_replays;
std::array<seastar::metrics::histogram, STAGE_MAX> stage_lat;
+ uint64_t onode_lookups = 0; // tree lookups
+ uint64_t onode_lookup_nodes = 0; // nodes searched (~depth/lookup)
+ uint64_t onode_str_cmp_count = 0;// ns/oid memcmp calls
+ uint64_t onode_inserts = 0;
+ uint64_t onode_updates = 0;
+ uint64_t onode_erases = 0;
+ int64_t onode_extents_delta = 0;
} stats;
+ void add_onode_tree_sample(const Transaction::tree_stats_t& ts) {
+ stats.onode_lookups += ts.lookup_count;
+ stats.onode_lookup_nodes += ts.nodes_visited;
+ stats.onode_str_cmp_count += ts.string_cmp_count;
+ stats.onode_inserts += ts.num_inserts;
+ stats.onode_updates += ts.num_updates;
+ stats.onode_erases += ts.num_erases;
+ stats.onode_extents_delta += ts.extents_num_delta;
+ }
+
seastar::metrics::histogram& get_latency(
op_type_t op_type) {
assert(static_cast<std::size_t>(op_type) < stats.op_lat.size());
uint64_t num_erases = 0;
uint64_t num_updates = 0;
int64_t extents_num_delta = 0;
+ uint64_t lookup_count = 0;
+ uint64_t nodes_visited = 0;
+ uint64_t string_cmp_count = 0;
bool is_clear() const {
return (depth == 0 &&
num_inserts == 0 &&
num_erases == 0 &&
num_updates == 0 &&
- extents_num_delta == 0);
+ extents_num_delta == 0 &&
+ lookup_count == 0 &&
+ nodes_visited == 0 &&
+ string_cmp_count == 0);
}
};
tree_stats_t& get_onode_tree_stats() {