From 8a77bfe35b37dc5cdd67a1a5447be3e640453dfa Mon Sep 17 00:00:00 2001 From: Duarte Nunes Date: Sat, 4 Mar 2017 14:25:48 +0100 Subject: [PATCH] db/view: Calculate clustering ranges for MV read-before-write query Introduce the calculate_affected_clustering_ranges() function to calculate the smallest subject of affected clustering ranges that we need to query for. The update_requires_read_before_write() function checks whether a view is potentially affected by the base update. The patch also cleans up the may_be_affected_by() function. Signed-off-by: Duarte Nunes --- db/view/view.cc | 89 +++++++++++++++++++++++++++++++++++++++++++++++-- db/view/view.hh | 6 ++++ 2 files changed, 92 insertions(+), 3 deletions(-) diff --git a/db/view/view.cc b/db/view/view.cc index d44c27c4aa..209cf7e69a 100644 --- a/db/view/view.cc +++ b/db/view/view.cc @@ -39,6 +39,9 @@ * along with Scylla. If not, see . */ +#include +#include + #include #include @@ -47,6 +50,7 @@ #include "cql3/util.hh" #include "db/view/view.hh" #include "gms/inet_address.hh" +#include "keys.hh" #include "locator/network_topology_strategy.hh" #include "service/storage_service.hh" #include "view_info.hh" @@ -131,14 +135,14 @@ bool clustering_prefix_matches(const schema& base, const view_info& view, const }); } -bool may_be_affected_by(const schema& base, const view_info view, const dht::decorated_key& key, const rows_entry& update) { +bool may_be_affected_by(const schema& base, const view_info& view, const dht::decorated_key& key, const rows_entry& update) { // We can guarantee that the view won't be affected if: // - the primary key is excluded by the view filter (note that this isn't true of the filter on regular columns: // even if an update don't match a view condition on a regular column, that update can still invalidate a - // pre-existing entry); + // pre-existing entry) - note that the upper layers should already have checked the partition key; // - the update doesn't modify any of the columns impacting the view (where "impacting" the view means that column // is neither included in the view, nor used by the view filter). - if (!partition_key_matches(base, view, key) && !clustering_prefix_matches(base, view, key.key(), update.key())) { + if (!clustering_prefix_matches(base, view, key.key(), update.key())) { return false; } @@ -157,6 +161,27 @@ bool may_be_affected_by(const schema& base, const view_info view, const dht::dec return affected; } +static bool update_requires_read_before_write(const schema& base, + const std::vector& views, + const dht::decorated_key& key, + const rows_entry& update) { + for (auto&& v : views) { + view_info& vf = *v->view_info(); + // A view whose primary key contains only the base's primary key columns doesn't require a read-before-write. + // However, if the view has restrictions on regular columns, then a write that doesn't match those filters + // needs to add a tombstone (assuming a previous update matched those filter and created a view entry); for + // now we just do a read-before-write in that case. + if (!vf.base_non_pk_column_in_view_pk(base) + && vf.select_statement().get_restrictions()->get_non_pk_restriction().empty()) { + continue; + } + if (may_be_affected_by(base, vf, key, update)) { + return true; + } + } + return false; +} + bool matches_view_filter(const schema& base, const view_info& view, const partition_key& key, const clustering_row& update, gc_clock::time_point now) { return clustering_prefix_matches(base, view, key, update.key()) && boost::algorithm::all_of( @@ -629,6 +654,64 @@ future> generate_view_updates( return f.finally([builder = std::move(builder)] { }); } +query::clustering_row_ranges calculate_affected_clustering_ranges(const schema& base, + const dht::decorated_key& key, + const mutation_partition& mp, + const std::vector& views) { + std::vector> row_ranges; + std::vector> view_row_ranges; + clustering_key_prefix_view::tri_compare cmp(base); + if (mp.partition_tombstone() || !mp.row_tombstones().empty()) { + for (auto&& v : views) { + // FIXME: #2371 + if (v->view_info()->select_statement().get_restrictions()->has_unrestricted_clustering_columns()) { + view_row_ranges.push_back(nonwrapping_range::make_open_ended_both_sides()); + break; + } + for (auto&& r : v->view_info()->partition_slice().default_row_ranges()) { + view_row_ranges.push_back(r.transform(std::mem_fn(&clustering_key_prefix::view))); + } + } + } + if (mp.partition_tombstone()) { + std::swap(row_ranges, view_row_ranges); + } else { + // FIXME: Optimize, as most often than not clustering keys will not be restricted. + for (auto&& rt : mp.row_tombstones()) { + nonwrapping_range rtr( + bound_view::to_range_bound(rt.start_bound()), + bound_view::to_range_bound(rt.end_bound())); + for (auto&& vr : view_row_ranges) { + auto overlap = rtr.intersection(vr, cmp); + if (overlap) { + row_ranges.push_back(std::move(overlap).value()); + } + } + } + } + + for (auto&& row : mp.clustered_rows()) { + if (update_requires_read_before_write(base, views, key, row)) { + row_ranges.emplace_back(row.key()); + } + } + + // Note that the views could have restrictions on regular columns, + // but even if that's the case we shouldn't apply those when we read, + // because even if an existing row doesn't match the view filter, the + // update can change that in which case we'll need to know the existing + // content, in case the view includes a column that is not included in + // this mutation. + + //FIXME: Unfortunate copy. + return boost::copy_range( + nonwrapping_range::deoverlap(std::move(row_ranges), cmp) + | boost::adaptors::transformed([] (auto&& v) { + return std::move(v).transform([] (auto&& ckv) { return clustering_key_prefix(ckv); }); + })); + +} + // Calculate the node ("natural endpoint") to which this node should send // a view update. // diff --git a/db/view/view.hh b/db/view/view.hh index d2c61065a3..a4ea7649f1 100644 --- a/db/view/view.hh +++ b/db/view/view.hh @@ -85,6 +85,12 @@ future> generate_view_updates( streamed_mutation&& updates, streamed_mutation&& existings); +query::clustering_row_ranges calculate_affected_clustering_ranges( + const schema& base, + const dht::decorated_key& key, + const mutation_partition& mp, + const std::vector& views); + void mutate_MV(const dht::token& base_token, std::vector mutations);