mutation_partion: Use row_tombstone

This patch replaces the current row tombstone representation by a
row_tombstone.

The intent of the patch is thus to reify the idea of shadowable
tombstones, that up until now we considered all materialized view row
tombstones to be.

We need to distinguish shadowable from non-shadowable row tombstones
to support scenarios such as, when inserting to a table with a
materialzied view:

1. insert into base (p, v1, v2) values (3, 1, 3) using timestamp 1
2. delete from base using timestamp 2 where p = 3
3. insert into base (p, v1) values (3, 1) using timestamp 3

These should yield a view row where v2 is definitely null, but with
the current implementation, v2 will pop back with its value v2=3@TS=1,
even though its dead in the base row. This is because the row
tombstone inserted at 2) is a shadowable one.

This patch only addresses the memory representation of such
row_tombstones.

Signed-off-by: Duarte Nunes <duarte@scylladb.com>
This commit is contained in:
Duarte Nunes
2017-03-21 23:16:13 +01:00
parent 6a2bccd4ae
commit 4e693383f7
22 changed files with 134 additions and 131 deletions

View File

@@ -355,7 +355,7 @@ void view_updates::do_delete_old_entry(const partition_key& base_key, const clus
static_pointer_cast<const collection_type_impl>(def->type)->for_each_cell(cell.as_collection_mutation(), set_max_ts);
}
});
get_view_row(base_key, existing).apply(tombstone(ts, now));
get_view_row(base_key, existing).apply(shadowable_tombstone(ts, now));
}
/**
@@ -528,12 +528,12 @@ void view_update_builder::generate_update(clustering_row&& update, stdx::optiona
// We allow existing to be disengaged, which we treat the same as an empty row.
if (existing) {
existing->marker().compact_and_expire(tombstone(), _now, always_gc, gc_before);
existing->cells().compact_and_expire(*_schema, column_kind::regular_column, tombstone(), _now, always_gc, gc_before);
existing->cells().compact_and_expire(*_schema, column_kind::regular_column, row_tombstone(), _now, always_gc, gc_before);
update.apply(*_schema, *existing);
}
update.marker().compact_and_expire(tombstone(), _now, always_gc, gc_before);
update.cells().compact_and_expire(*_schema, column_kind::regular_column, tombstone(), _now, always_gc, gc_before);
update.cells().compact_and_expire(*_schema, column_kind::regular_column, row_tombstone(), _now, always_gc, gc_before);
for (auto&& v : _view_updates) {
v.generate_update(_updates.key(), update, existing, _now);
@@ -558,7 +558,7 @@ future<stop_iteration> view_update_builder::on_results() {
apply_tracked_tombstones(_update_tombstone_tracker, update);
auto tombstone = _existing_tombstone_tracker.current_tombstone();
auto existing = tombstone
? stdx::optional<clustering_row>(stdx::in_place, update.key(), std::move(tombstone), row_marker(), ::row())
? stdx::optional<clustering_row>(stdx::in_place, update.key(), row_tombstone(std::move(tombstone)), row_marker(), ::row())
: stdx::nullopt;
generate_update(std::move(update), std::move(existing));
}
@@ -577,7 +577,7 @@ future<stop_iteration> view_update_builder::on_results() {
// tombstone, since we wouldn't have read the existing row otherwise. We don't assert that in case the
// read method ever changes.
if (tombstone) {
auto update = clustering_row(existing.key(), std::move(tombstone), row_marker(), ::row());
auto update = clustering_row(existing.key(), row_tombstone(std::move(tombstone)), row_marker(), ::row());
generate_update(std::move(update), { std::move(existing) });
}
}
@@ -602,7 +602,7 @@ future<stop_iteration> view_update_builder::on_results() {
// We don't care if it's a range tombstone, as we're only looking for existing entries that get deleted
if (!_existing->is_range_tombstone()) {
auto& existing = _existing->as_clustering_row();
auto update = clustering_row(existing.key(), std::move(tombstone), row_marker(), ::row());
auto update = clustering_row(existing.key(), row_tombstone(std::move(tombstone)), row_marker(), ::row());
generate_update(std::move(update), { std::move(existing) });
}
return advance_existings();