Skip to content

Commit 80f004b

Browse files
dataroaringclaude
andcommitted
[test](read-uncommitted) Add unit and regression tests for UncommittedRowsetRegistry
BE unit tests cover register/get/unregister/expiration/sharding logic. Regression test verifies session variable, queries on dup/mow/mor tables, isolation level switching, and predicate pushdown under READ-UNCOMMITTED. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
1 parent 016c2ad commit 80f004b

File tree

2 files changed

+431
-0
lines changed

2 files changed

+431
-0
lines changed
Lines changed: 233 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,233 @@
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 "olap/uncommitted_rowset_registry.h"
19+
20+
#include <gtest/gtest.h>
21+
22+
#include <memory>
23+
#include <vector>
24+
25+
#include "common/config.h"
26+
#include "olap/rowset/rowset_meta.h"
27+
#include "olap/tablet_schema.h"
28+
#include "testutil/mock_rowset.h"
29+
30+
namespace doris {
31+
32+
class UncommittedRowsetRegistryTest : public testing::Test {
33+
protected:
34+
// Helper: create a MockRowset with a unique RowsetId.
35+
static RowsetSharedPtr create_mock_rowset(int64_t id_high) {
36+
auto schema = std::make_shared<TabletSchema>();
37+
auto rs_meta = std::make_shared<RowsetMeta>();
38+
RowsetId rid;
39+
rid.init(id_high);
40+
rs_meta->set_rowset_id(rid);
41+
rs_meta->set_rowset_type(BETA_ROWSET);
42+
RowsetSharedPtr rowset;
43+
EXPECT_EQ(Status::OK(), MockRowset::create_rowset(schema, rs_meta, &rowset));
44+
return rowset;
45+
}
46+
47+
// Helper: create an UncommittedRowsetEntry with the given fields.
48+
static std::shared_ptr<UncommittedRowsetEntry> make_entry(int64_t tablet_id,
49+
int64_t txn_id,
50+
int64_t id_high) {
51+
auto entry = std::make_shared<UncommittedRowsetEntry>();
52+
entry->rowset = create_mock_rowset(id_high);
53+
entry->transaction_id = txn_id;
54+
entry->partition_id = 1;
55+
entry->tablet_id = tablet_id;
56+
entry->register_time_ms = 0; // will be overwritten by register_rowset
57+
return entry;
58+
}
59+
};
60+
61+
TEST_F(UncommittedRowsetRegistryTest, RegisterAndGet) {
62+
UncommittedRowsetRegistry registry;
63+
64+
auto entry = make_entry(/*tablet_id=*/100, /*txn_id=*/1, /*id_high=*/1);
65+
registry.register_rowset(entry);
66+
67+
std::vector<std::shared_ptr<UncommittedRowsetEntry>> result;
68+
registry.get_uncommitted_rowsets(100, &result);
69+
70+
ASSERT_EQ(result.size(), 1);
71+
EXPECT_EQ(result[0]->tablet_id, 100);
72+
EXPECT_EQ(result[0]->transaction_id, 1);
73+
EXPECT_GT(result[0]->register_time_ms, 0);
74+
}
75+
76+
TEST_F(UncommittedRowsetRegistryTest, GetEmpty) {
77+
UncommittedRowsetRegistry registry;
78+
79+
std::vector<std::shared_ptr<UncommittedRowsetEntry>> result;
80+
registry.get_uncommitted_rowsets(999, &result);
81+
82+
EXPECT_TRUE(result.empty());
83+
}
84+
85+
TEST_F(UncommittedRowsetRegistryTest, MultipleTabletsIsolation) {
86+
UncommittedRowsetRegistry registry;
87+
88+
registry.register_rowset(make_entry(100, 1, 1));
89+
registry.register_rowset(make_entry(200, 2, 2));
90+
91+
std::vector<std::shared_ptr<UncommittedRowsetEntry>> result100;
92+
registry.get_uncommitted_rowsets(100, &result100);
93+
ASSERT_EQ(result100.size(), 1);
94+
EXPECT_EQ(result100[0]->transaction_id, 1);
95+
96+
std::vector<std::shared_ptr<UncommittedRowsetEntry>> result200;
97+
registry.get_uncommitted_rowsets(200, &result200);
98+
ASSERT_EQ(result200.size(), 1);
99+
EXPECT_EQ(result200[0]->transaction_id, 2);
100+
}
101+
102+
TEST_F(UncommittedRowsetRegistryTest, MultipleEntriesSameTablet) {
103+
UncommittedRowsetRegistry registry;
104+
105+
registry.register_rowset(make_entry(100, 10, 10));
106+
registry.register_rowset(make_entry(100, 20, 20));
107+
registry.register_rowset(make_entry(100, 30, 30));
108+
109+
std::vector<std::shared_ptr<UncommittedRowsetEntry>> result;
110+
registry.get_uncommitted_rowsets(100, &result);
111+
112+
ASSERT_EQ(result.size(), 3);
113+
// Verify all three transaction IDs are present
114+
std::set<int64_t> txn_ids;
115+
for (const auto& e : result) {
116+
txn_ids.insert(e->transaction_id);
117+
}
118+
EXPECT_TRUE(txn_ids.count(10));
119+
EXPECT_TRUE(txn_ids.count(20));
120+
EXPECT_TRUE(txn_ids.count(30));
121+
}
122+
123+
TEST_F(UncommittedRowsetRegistryTest, UnregisterByTxnId) {
124+
UncommittedRowsetRegistry registry;
125+
126+
registry.register_rowset(make_entry(100, 1, 1));
127+
128+
// Verify it exists
129+
std::vector<std::shared_ptr<UncommittedRowsetEntry>> result;
130+
registry.get_uncommitted_rowsets(100, &result);
131+
ASSERT_EQ(result.size(), 1);
132+
133+
// Unregister
134+
registry.unregister_rowset(100, 1);
135+
136+
result.clear();
137+
registry.get_uncommitted_rowsets(100, &result);
138+
EXPECT_TRUE(result.empty());
139+
}
140+
141+
TEST_F(UncommittedRowsetRegistryTest, UnregisterNonexistent) {
142+
UncommittedRowsetRegistry registry;
143+
144+
// Should not crash or throw on empty registry
145+
registry.unregister_rowset(999, 1);
146+
147+
// Register something, then unregister a non-existent txn_id
148+
registry.register_rowset(make_entry(100, 1, 1));
149+
registry.unregister_rowset(100, 999);
150+
151+
// Original entry should still be present
152+
std::vector<std::shared_ptr<UncommittedRowsetEntry>> result;
153+
registry.get_uncommitted_rowsets(100, &result);
154+
ASSERT_EQ(result.size(), 1);
155+
EXPECT_EQ(result[0]->transaction_id, 1);
156+
}
157+
158+
TEST_F(UncommittedRowsetRegistryTest, UnregisterLeavesOtherEntries) {
159+
UncommittedRowsetRegistry registry;
160+
161+
registry.register_rowset(make_entry(100, 1, 1));
162+
registry.register_rowset(make_entry(100, 2, 2));
163+
164+
registry.unregister_rowset(100, 1);
165+
166+
std::vector<std::shared_ptr<UncommittedRowsetEntry>> result;
167+
registry.get_uncommitted_rowsets(100, &result);
168+
ASSERT_EQ(result.size(), 1);
169+
EXPECT_EQ(result[0]->transaction_id, 2);
170+
}
171+
172+
TEST_F(UncommittedRowsetRegistryTest, RemoveExpiredEntries) {
173+
UncommittedRowsetRegistry registry;
174+
175+
// Save and override the config
176+
int32_t saved_expire = config::uncommitted_rowset_expire_sec;
177+
config::uncommitted_rowset_expire_sec = 60; // 60 seconds
178+
179+
// Register two entries
180+
auto entry_old = make_entry(100, 1, 1);
181+
auto entry_fresh = make_entry(100, 2, 2);
182+
registry.register_rowset(entry_old);
183+
registry.register_rowset(entry_fresh);
184+
185+
// Simulate: make entry_old look like it was registered 120 seconds ago
186+
entry_old->register_time_ms -= 120 * 1000L;
187+
188+
registry.remove_expired_entries();
189+
190+
std::vector<std::shared_ptr<UncommittedRowsetEntry>> result;
191+
registry.get_uncommitted_rowsets(100, &result);
192+
ASSERT_EQ(result.size(), 1);
193+
EXPECT_EQ(result[0]->transaction_id, 2);
194+
195+
// Restore config
196+
config::uncommitted_rowset_expire_sec = saved_expire;
197+
}
198+
199+
TEST_F(UncommittedRowsetRegistryTest, ShardingDistribution) {
200+
UncommittedRowsetRegistry registry;
201+
202+
// Tablets 0 and 16 both map to shard 0 (0 % 16 == 16 % 16 == 0).
203+
// They should still be isolated by tablet_id in the shard's map.
204+
registry.register_rowset(make_entry(0, 1, 1));
205+
registry.register_rowset(make_entry(16, 2, 2));
206+
207+
std::vector<std::shared_ptr<UncommittedRowsetEntry>> result0;
208+
registry.get_uncommitted_rowsets(0, &result0);
209+
ASSERT_EQ(result0.size(), 1);
210+
EXPECT_EQ(result0[0]->transaction_id, 1);
211+
212+
std::vector<std::shared_ptr<UncommittedRowsetEntry>> result16;
213+
registry.get_uncommitted_rowsets(16, &result16);
214+
ASSERT_EQ(result16.size(), 1);
215+
EXPECT_EQ(result16[0]->transaction_id, 2);
216+
217+
// Tablets 1 and 2 map to different shards (1 % 16 != 2 % 16).
218+
// Basic correctness check for different-shard tablets.
219+
registry.register_rowset(make_entry(1, 3, 3));
220+
registry.register_rowset(make_entry(2, 4, 4));
221+
222+
std::vector<std::shared_ptr<UncommittedRowsetEntry>> result1;
223+
registry.get_uncommitted_rowsets(1, &result1);
224+
ASSERT_EQ(result1.size(), 1);
225+
EXPECT_EQ(result1[0]->transaction_id, 3);
226+
227+
std::vector<std::shared_ptr<UncommittedRowsetEntry>> result2;
228+
registry.get_uncommitted_rowsets(2, &result2);
229+
ASSERT_EQ(result2.size(), 1);
230+
EXPECT_EQ(result2[0]->transaction_id, 4);
231+
}
232+
233+
} // namespace doris

0 commit comments

Comments
 (0)