Coverage Report

Created: 2026-08-03 06:48

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
be/src/storage/mow/key_probe.h
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
#pragma once
19
20
#include <cstdint>
21
#include <memory>
22
#include <string>
23
#include <vector>
24
25
#include "common/status.h"
26
#include "storage/olap_common.h"
27
#include "storage/rowset/rowset_fwd.h"
28
#include "storage/utils.h"
29
30
namespace doris {
31
class BaseTablet;
32
class IOlapColumnDataAccessor;
33
class RowKeyEncoder;
34
class TabletSchema;
35
class SegmentCacheHandle;
36
struct MowContext;
37
struct PartialUpdateStats;
38
39
namespace segment_v2 {
40
41
// Result of looking up one primary key in a merge-on-write load.
42
enum class KeyProbeResult : uint8_t {
43
    // Key absent: brand-new row.
44
    NOT_FOUND = 0,
45
    // Key exists; the incoming row replaces the old one.
46
    FOUND = 1,
47
    // Stored row has a larger sequence value, so the incoming row loses.
48
    FOUND_NEWER = 2,
49
};
50
51
struct ProbeOutcome {
52
    KeyProbeResult result {KeyProbeResult::NOT_FOUND};
53
    // Only meaningful when result != NOT_FOUND; a miss leaves it default-constructed.
54
    RowLocation loc;
55
    // Keeps the rowset holding `loc` alive while the caller reads old-row columns.
56
    RowsetSharedPtr rowset;
57
    // true  -> cells not given in the input take their default/null value
58
    // false -> the old row at `loc` must be read into the fill plan
59
    bool use_default_or_null {true};
60
};
61
62
// The old row plus its encoded sequence value, returned by probe_previous_seq_value. The sequence
63
// value is empty unless outcome.result is FOUND.
64
struct PrevSeqProbe {
65
    ProbeOutcome outcome;
66
    std::string encoded_seq_value;
67
};
68
69
// IMPORTANT: probe() applies delete-bitmap marks right away (not batched), using
70
// TEMP_VERSION_COMMON. Segcompaction and the pre-commit checks read these TEMP marks mid-load, so
71
// delaying them would lose deletes.
72
class MowKeyProbe {
73
public:
74
    // Which rows this probe marks deleted in mow_context->delete_bitmap.
75
    enum class MarkDeleted : uint8_t {
76
        // Pure lookup: marking is somebody else's job (the row binlog history read).
77
        NONE = 0,
78
        // Mark the old row, i.e. the row already in the table that the incoming row replaces. On a
79
        // sequence loss nothing is marked at all: the caller of this mode drops the losing row
80
        // itself instead of writing it into the segment.
81
        OLD_ROW = 1,
82
        // Mark the old row, and the incoming row too when it loses on sequence: the writer has
83
        // already written that row into the segment, so it cannot just be dropped.
84
        OLD_AND_LOSING_ROW = 2,
85
    };
86
87
    // The use_defaults_* flags list the situations in which the probe sets
88
    // ProbeOutcome::use_default_or_null instead of asking for the old row's values.
89
    struct Policy {
90
        MarkDeleted mark_deleted {MarkDeleted::OLD_AND_LOSING_ROW};
91
        // Only without a sequence column: that one must survive for merge-on-read compaction.
92
        bool use_defaults_for_delete_signed {true};
93
        // A row that loses on sequence does not take effect, so nothing fills it.
94
        bool use_defaults_for_seq_loser {true};
95
        // Flexible partial update: the old row was already deleted earlier in this same load, so
96
        // the incoming row is brand new.
97
        bool use_defaults_for_in_load_deleted {false};
98
    };
99
100
    // lookup_schema goes to BaseTablet::lookup_row_key; has_sequence_col comes from the input's
101
    // schema and drives use_defaults_for_delete_signed; writing_rowset_id/writing_segment_id
102
    // identify the segment being written, used only to mark a sequence loser.
103
    MowKeyProbe(BaseTablet* tablet, TabletSchema* lookup_schema, bool has_sequence_col,
104
                std::shared_ptr<MowContext> mow_context, const RowsetId& writing_rowset_id,
105
                uint32_t writing_segment_id, Policy policy);
106
107
    // The partial update fill paths: mark the old row, or the incoming row when it loses on
108
    // sequence, and read the old row's values for the columns the input does not carry. Two cases
109
    // take defaults instead: a losing row (it never takes effect) and a delete-signed row without a
110
    // sequence column (its values are never read back). `flexible` adds the insert-after-delete
111
    // rule.
112
    static MowKeyProbe for_partial_update(BaseTablet* tablet, TabletSchema* lookup_schema,
113
                                          bool has_sequence_col,
114
                                          std::shared_ptr<MowContext> mow_context,
115
                                          const RowsetId& writing_rowset_id,
116
1.78k
                                          uint32_t writing_segment_id, bool flexible) {
117
1.78k
        return MowKeyProbe {tablet,
118
1.78k
                            lookup_schema,
119
1.78k
                            has_sequence_col,
120
1.78k
                            std::move(mow_context),
121
1.78k
                            writing_rowset_id,
122
1.78k
                            writing_segment_id,
123
1.78k
                            Policy {
124
1.78k
                                    .mark_deleted = MarkDeleted::OLD_AND_LOSING_ROW,
125
1.78k
                                    .use_defaults_for_delete_signed = true,
126
1.78k
                                    .use_defaults_for_seq_loser = true,
127
1.78k
                                    .use_defaults_for_in_load_deleted = flexible,
128
1.78k
                            }};
129
1.78k
    }
130
131
    // The row binlog history lookup: marks nothing, and keeps the old values of a sequence loser,
132
    // and of a delete-signed row when a __BEFORE__* image is wanted.
133
    static MowKeyProbe for_row_binlog(BaseTablet* tablet, TabletSchema* lookup_schema,
134
                                      bool has_sequence_col,
135
8
                                      std::shared_ptr<MowContext> mow_context, bool write_before) {
136
8
        return MowKeyProbe {tablet,
137
8
                            lookup_schema,
138
8
                            has_sequence_col,
139
8
                            std::move(mow_context),
140
8
                            RowsetId {},
141
8
                            0,
142
8
                            Policy {
143
8
                                    .mark_deleted = MarkDeleted::NONE,
144
8
                                    .use_defaults_for_delete_signed = !write_before,
145
8
                                    .use_defaults_for_seq_loser = false,
146
8
                                    .use_defaults_for_in_load_deleted = false,
147
8
                            }};
148
8
    }
149
150
    // Probe one row. `key` is the full encoded key (with seq suffix when key_has_seq_suffix).
151
    // `segment_pos` is the row's position in the segment being written (self-mark only). `stats`
152
    // counts new/updated/deleted rows, except under MarkDeleted::NONE which counts nothing; a
153
    // caller that doesn't track partial-update counts passes a throwaway.
154
    //
155
    // On NOT_FOUND it bumps stats.num_rows_new_added, but PartialUpdateInfo::handle_new_key() needs
156
    // caller-side state, so the partial update callers still run it themselves.
157
    Result<ProbeOutcome> probe(const std::string& key, size_t segment_pos, bool key_has_seq_suffix,
158
                               bool have_delete_sign,
159
                               const std::vector<RowsetSharedPtr>& specified_rowsets,
160
                               std::vector<std::unique_ptr<SegmentCacheHandle>>& segment_caches,
161
                               PartialUpdateStats& stats) const;
162
163
    // Lookup without the seq suffix; returns the old row plus its encoded sequence value
164
    // (BlockAggregator). Never touches the delete bitmap, and ignores the policy entirely.
165
    Result<PrevSeqProbe> probe_previous_seq_value(
166
            const std::string& key, const std::vector<RowsetSharedPtr>& specified_rowsets,
167
            std::vector<std::unique_ptr<SegmentCacheHandle>>& segment_caches) const;
168
169
    // Erase the row-cache entry. Erase-only: the rowset isn't visible yet, so inserting could
170
    // expose uncommitted data if the load fails.
171
    static void maybe_invalidate_row_cache(int64_t tablet_id, const TabletSchema& schema,
172
                                           DataWriteType write_type, const std::string& key);
173
174
private:
175
    BaseTablet* _tablet = nullptr;
176
    TabletSchema* _lookup_schema = nullptr;
177
    bool _has_sequence_col = false;
178
    std::shared_ptr<MowContext> _mow_context;
179
    RowsetId _writing_rowset_id;
180
    uint32_t _writing_segment_id = 0;
181
    Policy _policy;
182
};
183
184
std::string encode_mow_key_invalidate_cache(
185
        const RowKeyEncoder& key_encoder, const std::vector<IOlapColumnDataAccessor*>& key_columns,
186
        const IOlapColumnDataAccessor* seq_column, size_t pos, bool row_has_seq, int64_t tablet_id,
187
        const TabletSchema& schema, DataWriteType write_type);
188
189
} // namespace segment_v2
190
} // namespace doris