/Users/deen/code/yugabyte-db/src/yb/tablet/operations/split_operation.cc
Line | Count | Source (jump to first uncovered line) |
1 | | // |
2 | | // Copyright (c) YugaByte, Inc. |
3 | | // |
4 | | // Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except |
5 | | // in compliance with the License. You may obtain a copy of the License at |
6 | | // |
7 | | // http://www.apache.org/licenses/LICENSE-2.0 |
8 | | // |
9 | | // Unless required by applicable law or agreed to in writing, software distributed under the License |
10 | | // is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express |
11 | | // or implied. See the License for the specific language governing permissions and limitations |
12 | | // under the License. |
13 | | // |
14 | | // |
15 | | |
16 | | #include "yb/tablet/operations/split_operation.h" |
17 | | |
18 | | #include "yb/common/wire_protocol.h" |
19 | | |
20 | | #include "yb/consensus/consensus.pb.h" |
21 | | #include "yb/consensus/consensus_error.h" |
22 | | #include "yb/consensus/consensus_round.h" |
23 | | |
24 | | #include "yb/tablet/tablet.h" |
25 | | #include "yb/tablet/tablet_splitter.h" |
26 | | |
27 | | #include "yb/util/logging.h" |
28 | | #include "yb/util/status_format.h" |
29 | | |
30 | | using namespace std::literals; |
31 | | |
32 | | namespace yb { |
33 | | namespace tablet { |
34 | | |
35 | | template <> |
36 | | void RequestTraits<tablet::SplitTabletRequestPB>::SetAllocatedRequest( |
37 | 0 | consensus::ReplicateMsg* replicate, SplitTabletRequestPB* request) { |
38 | 0 | replicate->set_allocated_split_request(request); |
39 | 0 | } |
40 | | |
41 | | template <> |
42 | | SplitTabletRequestPB* RequestTraits<SplitTabletRequestPB>::MutableRequest( |
43 | 116 | consensus::ReplicateMsg* replicate) { |
44 | 116 | return replicate->mutable_split_request(); |
45 | 116 | } |
46 | | |
47 | 71 | void SplitOperation::AddedAsPending() { |
48 | 71 | tablet()->RegisterOperationFilter(this); |
49 | 71 | } |
50 | | |
51 | 67 | void SplitOperation::RemovedFromPending() { |
52 | 67 | tablet()->UnregisterOperationFilter(this); |
53 | 67 | } |
54 | | |
55 | | Status SplitOperation::RejectionStatus( |
56 | | OpId split_op_id, OpId rejected_op_id, consensus::OperationType op_type, |
57 | 23 | const TabletId& child1, const TabletId& child2) { |
58 | 23 | auto status = STATUS_EC_FORMAT( |
59 | 23 | IllegalState, consensus::ConsensusError(consensus::ConsensusErrorPB::TABLET_SPLIT), |
60 | 23 | "Tablet split has been $0, operation $1 $2 should be retried to new tablets", |
61 | 23 | split_op_id.empty() ? "applied" : Format("added to Raft log ($0)", split_op_id), |
62 | 23 | OperationType_Name(op_type), rejected_op_id); |
63 | 23 | return status.CloneAndAddErrorCode(SplitChildTabletIdsData({child1, child2})); |
64 | 23 | } |
65 | | |
66 | | // Returns whether Raft operation of op_type is allowed to be added to Raft log of the tablet |
67 | | // for which split tablet Raft operation has been already added to Raft log. |
68 | 27 | bool SplitOperation::ShouldAllowOpAfterSplitTablet(const consensus::OperationType op_type) { |
69 | | // Old tablet remains running for remote bootstrap purposes for some time and could receive |
70 | | // Raft operations. |
71 | | |
72 | | // If new OperationType is added, make an explicit deliberate decision whether new op type |
73 | | // should be allowed to be added into Raft log for old (pre-split) tablet. |
74 | 27 | switch (op_type) { |
75 | | // We allow NO_OP, so old tablet can have leader changes in case of re-elections. |
76 | 1 | case consensus::NO_OP: FALLTHROUGH_INTENDED; |
77 | | // We allow SNAPSHOT_OP, so old tablet can be restored. |
78 | 1 | case consensus::SNAPSHOT_OP: FALLTHROUGH_INTENDED; |
79 | | // Allow CHANGE_CONFIG_OP, so the old tablet replicas can be moved between tservers while we |
80 | | // keep the tablet available. |
81 | 4 | case consensus::CHANGE_CONFIG_OP: |
82 | 4 | return true; |
83 | 0 | case consensus::UNKNOWN_OP: FALLTHROUGH_INTENDED; |
84 | 23 | case consensus::WRITE_OP: FALLTHROUGH_INTENDED; |
85 | 23 | case consensus::CHANGE_METADATA_OP: FALLTHROUGH_INTENDED; |
86 | 23 | case consensus::HISTORY_CUTOFF_OP: FALLTHROUGH_INTENDED; |
87 | 23 | case consensus::UPDATE_TRANSACTION_OP: FALLTHROUGH_INTENDED; |
88 | 23 | case consensus::TRUNCATE_OP: FALLTHROUGH_INTENDED; |
89 | 23 | case consensus::SPLIT_OP: |
90 | 23 | return false; |
91 | 27 | } |
92 | 0 | FATAL_INVALID_ENUM_VALUE(consensus::OperationType, op_type); |
93 | 0 | } |
94 | | |
95 | | Status SplitOperation::CheckOperationAllowed( |
96 | 47 | const OpId& id, consensus::OperationType op_type) const { |
97 | 47 | if (id == op_id() || ShouldAllowOpAfterSplitTablet(op_type)2 ) { |
98 | 45 | return Status::OK(); |
99 | 45 | } |
100 | | |
101 | | // TODO(tsplit): for optimization - include new tablet IDs into response, so client knows |
102 | | // earlier where to retry. |
103 | | // TODO(tsplit): test - check that split_op_id_ is correctly aborted. |
104 | | // TODO(tsplit): test - check that split_op_id_ is correctly restored during bootstrap. |
105 | 2 | return RejectionStatus( |
106 | 2 | op_id(), id, op_type, request()->new_tablet1_id(), request()->new_tablet2_id()); |
107 | 47 | } |
108 | | |
109 | 71 | Status SplitOperation::Prepare() { |
110 | 71 | VLOG_WITH_PREFIX0 (2) << "Prepare"0 ; |
111 | 71 | return Status::OK(); |
112 | 71 | } |
113 | | |
114 | 0 | Status SplitOperation::DoAborted(const Status& status) { |
115 | 0 | VLOG_WITH_PREFIX(2) << "DoAborted"; |
116 | 0 | return status; |
117 | 0 | } |
118 | | |
119 | 70 | Status SplitOperation::DoReplicated(int64_t leader_term, Status* complete_status) { |
120 | 70 | VLOG_WITH_PREFIX0 (2) << "Apply"0 ; |
121 | 70 | return tablet_splitter().ApplyTabletSplit(this, /* raft_log= */ nullptr); |
122 | 70 | } |
123 | | |
124 | | } // namespace tablet |
125 | | } // namespace yb |