@@ -49,6 +49,50 @@ class RGVisibilityTest : public ::testing::Test {
4949 RGVisibilityInstance* rgVisibility;
5050};
5151
52+ static bool rgBitSet (const uint64_t * bitmap, uint32_t rowId) {
53+ return ((bitmap[rowId / 64 ] >> (rowId % 64 )) & 1ULL ) != 0 ;
54+ }
55+
56+ static void runConcurrentRGDeletes (RGVisibilityInstance* visibility,
57+ ReplayMode mode,
58+ uint64_t ts,
59+ int rowCount = 64 ,
60+ int threadCount = 8 ) {
61+ ASSERT_EQ (rowCount % threadCount, 0 );
62+ std::atomic<bool > start{false };
63+ std::vector<std::thread> threads;
64+ int rowsPerThread = rowCount / threadCount;
65+
66+ for (int t = 0 ; t < threadCount; t++) {
67+ threads.emplace_back ([&, t]() {
68+ while (!start.load (std::memory_order_acquire)) {
69+ std::this_thread::yield ();
70+ }
71+ for (int i = 0 ; i < rowsPerThread; i++) {
72+ uint32_t rowId = static_cast <uint32_t >(t * rowsPerThread + i);
73+ visibility->deleteRGRecord (rowId, ts, mode);
74+ }
75+ });
76+ }
77+
78+ start.store (true , std::memory_order_release);
79+ for (auto & thread : threads) {
80+ thread.join ();
81+ }
82+ }
83+
84+ static void expectRGRows (RGVisibilityInstance* visibility,
85+ uint64_t queryTs,
86+ int rowCount,
87+ bool expectedSet) {
88+ uint64_t * bitmap = visibility->getRGVisibilityBitmap (queryTs);
89+ for (int row = 0 ; row < rowCount; row++) {
90+ EXPECT_EQ (expectedSet, rgBitSet (bitmap, static_cast <uint32_t >(row)))
91+ << " row=" << row << " queryTs=" << queryTs;
92+ }
93+ delete[] bitmap;
94+ }
95+
5296TEST_F (RGVisibilityTest, BasicDeleteAndVisibility) {
5397 uint64_t timestamp1 = 100 ;
5498 uint64_t timestamp2 = 200 ;
@@ -67,6 +111,34 @@ TEST_F(RGVisibilityTest, BasicDeleteAndVisibility) {
67111 delete[] bitmap2;
68112}
69113
114+ TEST_F (RGVisibilityTest, ConcurrentNormalModeAppendsDeleteChain) {
115+ constexpr uint64_t baseTs = 100 ;
116+ RGVisibilityInstance visibility (ROW_COUNT , baseTs, nullptr );
117+
118+ runConcurrentRGDeletes (&visibility, ReplayMode::NORMAL , baseTs + 1 );
119+
120+ expectRGRows (&visibility, baseTs, 64 , false );
121+ expectRGRows (&visibility, baseTs + 1 , 64 , true );
122+ }
123+
124+ TEST_F (RGVisibilityTest, ConcurrentVersionedModeFoldsWithCow) {
125+ constexpr uint64_t baseTs = 100 ;
126+ RGVisibilityInstance visibility (ROW_COUNT , baseTs, nullptr );
127+
128+ runConcurrentRGDeletes (&visibility, ReplayMode::VERSIONED , baseTs - 1 );
129+
130+ expectRGRows (&visibility, baseTs, 64 , true );
131+ }
132+
133+ TEST_F (RGVisibilityTest, ConcurrentExclusiveModeFoldsWithAtomicOr) {
134+ constexpr uint64_t baseTs = 100 ;
135+ RGVisibilityInstance visibility (ROW_COUNT , baseTs, nullptr );
136+
137+ runConcurrentRGDeletes (&visibility, ReplayMode::EXCLUSIVE , baseTs - 1 );
138+
139+ expectRGRows (&visibility, baseTs, 64 , true );
140+ }
141+
70142TEST_F (RGVisibilityTest, MultiThread) {
71143 struct DeleteRecord {
72144 uint64_t timestamp;
0 commit comments