Skip to content

Commit 9afc5a7

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 9afc5a7

File tree

2 files changed

+430
-0
lines changed

2 files changed

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

0 commit comments

Comments
 (0)