be/src/storage/mow/key_probe.cpp
Line | Count | Source |
1 | | // Licensed to the Apache Software Foundation (ASF) under one |
2 | | // or more contributor license agreements. See the NOTICE file |
3 | | // distributed with this work for additional information |
4 | | // regarding copyright ownership. The ASF licenses this file |
5 | | // to you under the Apache License, Version 2.0 (the |
6 | | // "License"); you may not use this file except in compliance |
7 | | // with the License. You may obtain a copy of the License at |
8 | | // |
9 | | // http://www.apache.org/licenses/LICENSE-2.0 |
10 | | // |
11 | | // Unless required by applicable law or agreed to in writing, |
12 | | // software distributed under the License is distributed on an |
13 | | // "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY |
14 | | // KIND, either express or implied. See the License for the |
15 | | // specific language governing permissions and limitations |
16 | | // under the License. |
17 | | |
18 | | #include "storage/mow/key_probe.h" |
19 | | |
20 | | #include "common/cast_set.h" |
21 | | #include "common/config.h" |
22 | | #include "common/logging.h" |
23 | | #include "service/point_query_executor.h" |
24 | | #include "storage/key/row_key_encoder.h" |
25 | | #include "storage/partial_update_info.h" |
26 | | #include "storage/tablet/base_tablet.h" |
27 | | #include "storage/tablet/tablet_meta.h" |
28 | | #include "storage/tablet/tablet_schema.h" |
29 | | |
30 | | namespace doris::segment_v2 { |
31 | | |
32 | | using namespace ErrorCode; |
33 | | |
34 | | MowKeyProbe::MowKeyProbe(BaseTablet* tablet, TabletSchema* lookup_schema, bool has_sequence_col, |
35 | | std::shared_ptr<MowContext> mow_context, const RowsetId& writing_rowset_id, |
36 | | uint32_t writing_segment_id, Policy policy) |
37 | 1.72k | : _tablet(tablet), |
38 | 1.72k | _lookup_schema(lookup_schema), |
39 | 1.72k | _has_sequence_col(has_sequence_col), |
40 | 1.72k | _mow_context(std::move(mow_context)), |
41 | 1.72k | _writing_rowset_id(writing_rowset_id), |
42 | 1.72k | _writing_segment_id(writing_segment_id), |
43 | 1.72k | _policy(policy) {} |
44 | | |
45 | | Result<ProbeOutcome> MowKeyProbe::probe( |
46 | | const std::string& key, size_t segment_pos, bool key_has_seq_suffix, bool have_delete_sign, |
47 | | const std::vector<RowsetSharedPtr>& specified_rowsets, |
48 | | std::vector<std::unique_ptr<SegmentCacheHandle>>& segment_caches, |
49 | 26.6k | PartialUpdateStats& stats) const { |
50 | 26.6k | RowLocation loc; |
51 | | // save rowset shared ptr so this rowset wouldn't delete |
52 | 26.6k | RowsetSharedPtr rowset; |
53 | 26.6k | auto st = _tablet->lookup_row_key(key, _lookup_schema, key_has_seq_suffix, specified_rowsets, |
54 | 26.6k | &loc, _mow_context->max_version, segment_caches, &rowset, |
55 | 26.6k | /*with_rowid=*/false); |
56 | 26.6k | if (st.is<KEY_NOT_FOUND>()) { |
57 | 638 | ++stats.num_rows_new_added; |
58 | 638 | return ProbeOutcome {KeyProbeResult::NOT_FOUND, {}, nullptr, /*use_default_or_null=*/true}; |
59 | 638 | } |
60 | 25.9k | if (!st.ok() && !st.is<KEY_ALREADY_EXISTS>()) { |
61 | 0 | LOG(WARNING) << "failed to lookup row key, tablet_id=" << _tablet->tablet_id() |
62 | 0 | << ", txn_id=" << _mow_context->txn_id << ", error: " << st; |
63 | 0 | return ResultError(std::move(st)); |
64 | 0 | } |
65 | | |
66 | | // Stored row's seq is larger, so the incoming row loses. |
67 | 25.9k | bool seq_loses = st.is<KEY_ALREADY_EXISTS>(); |
68 | | // A delete-signed row's value columns are never read back, so there is nothing to carry |
69 | | // forward -- except when the table has a sequence column, whose value must still be read or the |
70 | | // merge-on-read compaction policy produces wrong results. |
71 | | // TODO(bobhan1): only read seq col rather than all columns in this situation for partial update |
72 | | // and flexible partial update |
73 | 25.9k | bool delete_sign_skip = |
74 | 25.9k | have_delete_sign && !_has_sequence_col && _policy.use_defaults_for_delete_signed; |
75 | | // Flexible PU insert-after-delete: an earlier row of this same load already deleted the old |
76 | | // row, so the insert counts as a brand-new row. Its sequence value, if the input does not carry |
77 | | // one, is filled by BlockAggregator::aggregate_for_insert_after_delete(). |
78 | | // Evaluated last so the two cheap rules above short-circuit the delete bitmap lookup. |
79 | 25.9k | auto in_load_deleted = [&] { |
80 | 24.4k | return _policy.use_defaults_for_in_load_deleted && |
81 | 24.4k | _mow_context->delete_bitmap->contains( |
82 | 1.84k | {loc.rowset_id, loc.segment_id, DeleteBitmap::TEMP_VERSION_COMMON}, |
83 | 1.84k | loc.row_id); |
84 | 24.4k | }; |
85 | | // Skip reading the old row (fill defaults) in any of these cases. |
86 | 25.9k | bool use_default = (seq_loses && _policy.use_defaults_for_seq_loser) || delete_sign_skip || |
87 | 25.9k | in_load_deleted(); |
88 | 25.9k | ProbeOutcome outcome {seq_loses ? KeyProbeResult::FOUND_NEWER : KeyProbeResult::FOUND, loc, |
89 | 25.9k | std::move(rowset), use_default}; |
90 | | |
91 | | // Apply the delete-bitmap marks right away -- see class comment (segcompaction). |
92 | 25.9k | if (seq_loses) { |
93 | 56 | if (_policy.mark_deleted == MarkDeleted::OLD_AND_LOSING_ROW) { |
94 | | // although we need to mark delete current row, we still need to read missing columns |
95 | | // for this row, we need to ensure that each column is aligned |
96 | 49 | _mow_context->delete_bitmap->add( |
97 | 49 | {_writing_rowset_id, _writing_segment_id, DeleteBitmap::TEMP_VERSION_COMMON}, |
98 | 49 | cast_set<uint32_t>(segment_pos)); |
99 | 49 | ++stats.num_rows_deleted; |
100 | 49 | } |
101 | 25.9k | } else if (_policy.mark_deleted != MarkDeleted::NONE) { |
102 | 25.9k | _mow_context->delete_bitmap->add( |
103 | 25.9k | {loc.rowset_id, loc.segment_id, DeleteBitmap::TEMP_VERSION_COMMON}, loc.row_id); |
104 | 25.9k | ++stats.num_rows_updated; |
105 | 25.9k | } |
106 | 25.9k | return outcome; |
107 | 25.9k | } |
108 | | |
109 | | Result<PrevSeqProbe> MowKeyProbe::probe_previous_seq_value( |
110 | | const std::string& key, const std::vector<RowsetSharedPtr>& specified_rowsets, |
111 | 2 | std::vector<std::unique_ptr<SegmentCacheHandle>>& segment_caches) const { |
112 | 2 | RowLocation loc; |
113 | 2 | RowsetSharedPtr rowset; |
114 | 2 | PrevSeqProbe result; |
115 | | // Unlike probe() above, this lookup keeps with_rowid: its callers encode the key from the |
116 | | // sort-key view, which for a cluster-key table carries the trailing rowid that has to be |
117 | | // stripped again. Such a table can not reach this path today -- partial update on a cluster-key |
118 | | // table is rejected up front -- but the encoding, not the probe, decides the flag. |
119 | 2 | auto st = |
120 | 2 | _tablet->lookup_row_key(key, _lookup_schema, /*with_seq_col=*/false, specified_rowsets, |
121 | 2 | &loc, _mow_context->max_version, segment_caches, &rowset, |
122 | 2 | /*with_rowid=*/true, &result.encoded_seq_value); |
123 | 2 | if (st.is<KEY_NOT_FOUND>()) { |
124 | | // lookup_row_key writes the sequence value before it checks the delete bitmap, so a key |
125 | | // whose only row is already marked deleted lands here with that row's value attached. |
126 | 1 | result.encoded_seq_value.clear(); |
127 | 1 | result.outcome = ProbeOutcome {KeyProbeResult::NOT_FOUND, |
128 | 1 | {}, |
129 | 1 | nullptr, |
130 | 1 | /*use_default_or_null=*/true}; |
131 | 1 | return result; |
132 | 1 | } |
133 | 1 | if (!st.ok()) { |
134 | 0 | return ResultError(std::move(st)); |
135 | 0 | } |
136 | 1 | result.outcome.result = KeyProbeResult::FOUND; |
137 | 1 | result.outcome.loc = loc; |
138 | 1 | result.outcome.rowset = std::move(rowset); |
139 | 1 | result.outcome.use_default_or_null = false; |
140 | 1 | return result; |
141 | 1 | } |
142 | | |
143 | | void MowKeyProbe::maybe_invalidate_row_cache(int64_t tablet_id, const TabletSchema& schema, |
144 | 9.67M | DataWriteType write_type, const std::string& key) { |
145 | | // Just invalid row cache for simplicity, since the rowset is not visible at present. If we |
146 | | // update/insert cache, if load failed rowset will not be visible but cached data will be |
147 | | // visible, and lead to inconsistency. |
148 | 9.67M | if (!config::disable_storage_row_cache && schema.has_row_store_for_all_columns() && |
149 | 9.67M | write_type == DataWriteType::TYPE_DIRECT) { |
150 | | // invalidate cache |
151 | 2 | RowCache::instance()->erase({tablet_id, key}); |
152 | 2 | } |
153 | 9.67M | } |
154 | | |
155 | | std::string encode_mow_key_invalidate_cache( |
156 | | const RowKeyEncoder& key_encoder, const std::vector<IOlapColumnDataAccessor*>& key_columns, |
157 | | const IOlapColumnDataAccessor* seq_column, size_t pos, bool row_has_seq, int64_t tablet_id, |
158 | 8.58M | const TabletSchema& schema, DataWriteType write_type) { |
159 | 8.58M | std::string key = key_encoder.full_encode_primary_keys(key_columns, pos); |
160 | | // the row cache uses the key without the seq as its key, so invalidate before the suffix |
161 | 8.58M | MowKeyProbe::maybe_invalidate_row_cache(tablet_id, schema, write_type, key); |
162 | 8.58M | if (row_has_seq) { |
163 | 31.6k | key_encoder.append_seq_suffix(&key, seq_column, pos); |
164 | 31.6k | } |
165 | 8.58M | return key; |
166 | 8.58M | } |
167 | | |
168 | | } // namespace doris::segment_v2 |