| // Licensed to the Apache Software Foundation (ASF) under one |
| // or more contributor license agreements. See the NOTICE file |
| // distributed with this work for additional information |
| // regarding copyright ownership. The ASF licenses this file |
| // to you under the Apache License, Version 2.0 (the |
| // "License"); you may not use this file except in compliance |
| // with the License. You may obtain a copy of the License at |
| // |
| // http://www.apache.org/licenses/LICENSE-2.0 |
| // |
| // Unless required by applicable law or agreed to in writing, |
| // software distributed under the License is distributed on an |
| // "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY |
| // KIND, either express or implied. See the License for the |
| // specific language governing permissions and limitations |
| // under the License. |
| |
| #ifndef KUDU_TABLET_MUTATION_H |
| #define KUDU_TABLET_MUTATION_H |
| |
| #include <cstdint> |
| #include <cstring> |
| #include <new> |
| #include <ostream> |
| #include <string> |
| |
| #include <glog/logging.h> |
| |
| #include "kudu/common/row_changelist.h" |
| #include "kudu/common/timestamp.h" |
| #include "kudu/gutil/atomicops.h" |
| #include "kudu/gutil/macros.h" |
| #include "kudu/gutil/port.h" |
| #include "kudu/util/slice.h" |
| |
| namespace kudu { |
| |
| class Schema; |
| |
| namespace tablet { |
| |
| // A single mutation associated with a row. |
| // This object also acts as a node in a linked list connected to other |
| // mutations in the row. |
| // |
| // This is a variable-length object. |
| class Mutation { |
| public: |
| Mutation() { } |
| |
| // Create a new Mutation object with a copy of the given changelist. |
| // The object is allocated from the provided Arena. |
| template<class ArenaType> |
| static Mutation *CreateInArena( |
| ArenaType *arena, Timestamp timestamp, const RowChangeList &rcl); |
| |
| RowChangeList changelist() const { |
| return RowChangeList(Slice(changelist_data_, changelist_size_)); |
| } |
| |
| Timestamp timestamp() const { return timestamp_; } |
| |
| Mutation *next() { return next_; } |
| const Mutation *next() const { return next_; } |
| |
| // Same as 'next()' but loads with 'Acquire' ordering semantics. |
| // This must be used when traversing the mutation list associated with |
| // an in-memory store. |
| const Mutation* acquire_next() const { |
| return reinterpret_cast<const Mutation*>(base::subtle::Acquire_Load( |
| reinterpret_cast<const AtomicWord*>(&next_))); |
| } |
| Mutation* acquire_next() { |
| return reinterpret_cast<Mutation*>(base::subtle::Acquire_Load( |
| reinterpret_cast<AtomicWord*>(&next_))); |
| } |
| |
| void set_next(Mutation *next) { |
| next_ = next; |
| } |
| |
| // Return a stringified version of the given list of mutations. |
| // This should only be used for debugging/logging. |
| static std::string StringifyMutationList(const Schema &schema, const Mutation *head); |
| |
| // Appends this mutation to the list given by 'redo_head' and 'redo_tail'. |
| // |
| // This function is atomic provided that callers are externally synchronized |
| // on a per mutation list (i.e. per row) basis. Without this synchronization, |
| // the non-atomicity between 'redo_head' and 'redo_tail' writes may cause errors. |
| // |
| // This operation uses "Release" memory semantics (see atomicops.h). The |
| // pointers as well as all of the mutations in the list must be word-aligned. |
| void AppendToListAtomic(Mutation** redo_head, Mutation** redo_tail); |
| |
| void PrependToList(Mutation** list) { |
| this->next_ = *list; |
| *list = this; |
| } |
| |
| // O(n) algorithm to reverse the order of a linked list of mutations. |
| static void ReverseMutationList(Mutation** list); |
| |
| private: |
| friend class MSRow; |
| friend class MemRowSet; |
| |
| // The op timestamp which made this mutation. If this op is not committed in |
| // the snapshot of the reader, this mutation should be ignored. |
| Timestamp timestamp_; |
| |
| // Link to the next mutation on this row |
| Mutation *next_; |
| |
| uint32_t changelist_size_; |
| |
| // The actual encoded RowChangeList |
| char changelist_data_[0]; |
| |
| DISALLOW_COPY_AND_ASSIGN(Mutation); |
| }; |
| |
| template<class ArenaType> |
| inline Mutation *Mutation::CreateInArena( |
| ArenaType *arena, Timestamp timestamp, const RowChangeList &rcl) { |
| DCHECK(!rcl.is_null()); |
| |
| size_t size = sizeof(Mutation) + rcl.slice().size(); |
| void *storage = arena->AllocateBytesAligned(size, BASE_PORT_H_ALIGN_OF(Mutation)); |
| CHECK(storage) << "failed to allocate storage from arena"; |
| auto ret = new (storage) Mutation(); |
| ret->timestamp_ = timestamp; |
| ret->next_ = NULL; |
| ret->changelist_size_ = rcl.slice().size(); |
| memcpy(ret->changelist_data_, rcl.slice().data(), rcl.slice().size()); |
| return ret; |
| } |
| |
| inline void Mutation::ReverseMutationList(Mutation** list) { |
| Mutation* prev = nullptr; |
| Mutation* cur = *list; |
| while (cur != nullptr) { |
| Mutation* next = cur->next_; |
| cur->next_ = prev; |
| prev = cur; |
| cur = next; |
| } |
| *list = prev; |
| } |
| |
| } // namespace tablet |
| } // namespace kudu |
| |
| #endif |