From 9162748b18e2c4f30cf6d3f489b8ea9b42328bdd Mon Sep 17 00:00:00 2001 From: Eliran Sinvani Date: Mon, 23 Nov 2020 15:02:31 +0200 Subject: [PATCH] materialized views: create view schemas with proper base table reference. Newly created view schemas don't always have their base info, this is bad since such schemas don't support read nor write. This leaves us vulnerable to a race condition where there is an attempt to use this schema for read or write. Here we initialize the base reference and also reconfigure the view to conform to the new computed column type, which makes it usable for write and not only reads. We do it for views created in the migration manager following announcements and also for copied schemas. --- db/schema_tables.cc | 37 ++++++++++++++++++++++++++++++++++++- schema.cc | 3 +++ 2 files changed, 39 insertions(+), 1 deletion(-) diff --git a/db/schema_tables.cc b/db/schema_tables.cc index ff96b49119..1ef399fc4a 100644 --- a/db/schema_tables.cc +++ b/db/schema_tables.cc @@ -1226,7 +1226,42 @@ static void merge_tables_and_views(distributed& proxy, return create_table_from_mutations(proxy, std::move(sm)); }); auto views_diff = diff_table_or_view(proxy, std::move(views_before), std::move(views_after), [&] (schema_mutations sm) { - return create_view_from_mutations(proxy, std::move(sm)); + // The view schema mutation should be created with reference to the base table schema because we definitely know it by now. + // If we don't do it we are leaving a window where write commands to this schema are illegal. + // There are 3 possibilities: + // 1. The table was altered - in this case we want the view to correspond to this new table schema. + // 2. The table was just created - the table is guarantied to be published with the view in that case. + // 3. The view itself was altered - in that case we already know the base table so we can take it from + // the database object. + view_ptr vp = create_view_from_mutations(proxy, std::move(sm)); + schema_ptr base_schema; + for (auto&& s : tables_diff.altered) { + if (s.new_schema.get()->ks_name() == vp->ks_name() && s.new_schema.get()->cf_name() == vp->view_info()->base_name() ) { + base_schema = s.new_schema; + break; + } + } + if (!base_schema) { + for (auto&& s : tables_diff.created) { + if (s.get()->ks_name() == vp->ks_name() && s.get()->cf_name() == vp->view_info()->base_name() ) { + base_schema = s; + break; + } + } + } + + if (!base_schema) { + base_schema = proxy.local().local_db().find_schema(vp->ks_name(), vp->view_info()->base_name()); + } + + // Now when we have a referenced base - just in case we are registering an old view (this can happen in a mixed cluster) + // lets make it write enabled by updating it's compute columns. + view_ptr fixed_vp = maybe_fix_legacy_secondary_index_mv_schema(proxy.local().get_db().local(), vp, base_schema, preserve_version::yes); + if(fixed_vp) { + vp = fixed_vp; + } + vp->view_info()->set_base_info(vp->view_info()->make_base_dependent_view_info(*base_schema)); + return vp; }); proxy.local().get_db().invoke_on_all([&] (database& db) { diff --git a/schema.cc b/schema.cc index bedba9b531..1e2e5fab1f 100644 --- a/schema.cc +++ b/schema.cc @@ -456,6 +456,9 @@ schema::schema(const schema& o) rebuild(); if (o.is_view()) { _view_info = std::make_unique<::view_info>(*this, o.view_info()->raw()); + if (o.view_info()->base_info()) { + _view_info->set_base_info(o.view_info()->base_info()); + } } }