/* * Copyright (C) 2016 ScyllaDB */ /* * This file is part of Scylla. * * Scylla is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * Scylla is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with Scylla. If not, see . */ #include #include "partition_version.hh" static void remove_or_mark_as_unique_owner(partition_version* current) { while (current && !current->is_referenced()) { auto next = current->next(); current_allocator().destroy(current); current = next; } if (current) { current->back_reference().mark_as_unique_owner(); } } partition_version::partition_version(partition_version&& pv) noexcept : anchorless_list_base_hook(std::move(pv)) , _backref(pv._backref) , _partition(std::move(pv._partition)) { if (_backref) { _backref->_version = this; } pv._backref = nullptr; } partition_version& partition_version::operator=(partition_version&& pv) noexcept { if (this != &pv) { this->~partition_version(); new (this) partition_version(std::move(pv)); } return *this; } partition_version::~partition_version() { if (_backref) { _backref->_version = nullptr; } } partition_snapshot::~partition_snapshot() { if (_version && _version.is_unique_owner()) { auto v = &*_version; _version = {}; remove_or_mark_as_unique_owner(v); } else if (_entry) { _entry->_snapshot = nullptr; } } void partition_snapshot::merge_partition_versions() { if (_version && !_version.is_unique_owner()) { auto v = &*_version; _version = { }; auto first_used = v; while (first_used->prev() && !first_used->is_referenced()) { first_used = first_used->prev(); } auto current = first_used->next(); while (current && !current->is_referenced()) { auto next = current->next(); try { first_used->partition().apply(*_schema, std::move(current->partition())); current_allocator().destroy(current); } catch (...) { // Set _version so that the merge can be retried. _version = partition_version_ref(*current); throw; } current = next; } } } unsigned partition_snapshot::version_count() { unsigned count = 0; for (auto&& v : versions()) { (void)v; count++; } return count; } partition_entry::partition_entry(mutation_partition mp) { auto new_version = current_allocator().construct(std::move(mp)); _version = partition_version_ref(*new_version); } partition_entry::~partition_entry() { if (!_version) { return; } if (_snapshot) { _snapshot->_version = std::move(_version); _snapshot->_version.mark_as_unique_owner(); _snapshot->_entry = nullptr; } else { auto v = &*_version; _version = { }; remove_or_mark_as_unique_owner(v); } } void partition_entry::set_version(partition_version* new_version) { if (_snapshot) { _snapshot->_version = std::move(_version); _snapshot->_entry = nullptr; } _snapshot = nullptr; _version = partition_version_ref(*new_version); } void partition_entry::apply(const schema& s, partition_version* pv, const schema& pv_schema) { if (!_snapshot) { _version->partition().apply(s, std::move(pv->partition()), pv_schema); current_allocator().destroy(pv); } else { if (s.version() != pv_schema.version()) { pv->partition().upgrade(pv_schema, s); } pv->insert_before(*_version); set_version(pv); } } void partition_entry::apply(const schema& s, const mutation_partition& mp, const schema& mp_schema) { if (!_snapshot) { _version->partition().apply(s, mp, mp_schema); } else { mutation_partition mp1 = mp; if (s.version() != mp_schema.version()) { mp1.upgrade(mp_schema, s); } auto new_version = current_allocator().construct(std::move(mp1)); new_version->insert_before(*_version); set_version(new_version); } } void partition_entry::apply(const schema& s, mutation_partition&& mp, const schema& mp_schema) { if (!_snapshot) { _version->partition().apply(s, std::move(mp), mp_schema); } else { if (s.version() != mp_schema.version()) { apply(s, mp, mp_schema); } else { auto new_version = current_allocator().construct(std::move(mp)); new_version->insert_before(*_version); set_version(new_version); } } } void partition_entry::apply(const schema& s, mutation_partition_view mpv, const schema& mp_schema) { if (!_snapshot) { _version->partition().apply(s, mpv, mp_schema); } else { mutation_partition mp(s.shared_from_this()); mp.apply(s, mpv, mp_schema); auto new_version = current_allocator().construct(std::move(mp)); new_version->insert_before(*_version); set_version(new_version); } } void partition_entry::apply(const schema& s, partition_entry&& pe, const schema& mp_schema) { auto begin = &*pe._version; auto snapshot = pe._snapshot; if (pe._snapshot) { pe._snapshot->_version = std::move(pe._version); pe._snapshot->_entry = nullptr; pe._snapshot = nullptr; } pe._version = { }; auto current = begin; if (!current->next() && !current->is_referenced()) { try { apply(s, current, mp_schema); } catch (...) { pe._version = partition_version_ref(*current); throw; } return; } try { while (current && !current->is_referenced()) { auto next = current->next(); apply(s, std::move(current->partition()), mp_schema); // Leave current->partition() valid (albeit empty) in case we throw later. current->partition() = mutation_partition(mp_schema.shared_from_this()); current = next; } while (current) { auto next = current->next(); apply(s, current->partition(), mp_schema); current = next; } } catch (...) { if (snapshot) { pe._snapshot = snapshot; snapshot->_entry = &pe; pe._version = std::move(snapshot->_version); } else { pe._version = partition_version_ref(*begin); } throw; } current = begin; while (current && !current->is_referenced()) { auto next = current->next(); current_allocator().destroy(current); current = next; } if (current) { current->back_reference().mark_as_unique_owner(); } } mutation_partition partition_entry::squashed(schema_ptr from, schema_ptr to) { mutation_partition mp(to); for (auto&& v : _version->all_elements()) { mp.apply(*to, v.partition(), *from); } return mp; } void partition_entry::upgrade(schema_ptr from, schema_ptr to) { auto new_version = current_allocator().construct(mutation_partition(to)); try { for (auto&& v : _version->all_elements()) { new_version->partition().apply(*to, v.partition(), *from); } } catch (...) { current_allocator().destroy(new_version); throw; } auto old_version = &*_version; set_version(new_version); remove_or_mark_as_unique_owner(old_version); } lw_shared_ptr partition_entry::read(schema_ptr entry_schema) { if (_snapshot) { return _snapshot->shared_from_this(); } else { auto snp = make_lw_shared(entry_schema, this); _snapshot = snp.get(); return snp; } }