/Users/deen/code/yugabyte-db/src/yb/rocksdb/db/write_batch_test.cc
Line | Count | Source (jump to first uncovered line) |
1 | | // Copyright (c) 2011-present, Facebook, Inc. All rights reserved. |
2 | | // This source code is licensed under the BSD-style license found in the |
3 | | // LICENSE file in the root directory of this source tree. An additional grant |
4 | | // of patent rights can be found in the PATENTS file in the same directory. |
5 | | // |
6 | | // The following only applies to changes made to this file as part of YugaByte development. |
7 | | // |
8 | | // Portions Copyright (c) YugaByte, Inc. |
9 | | // |
10 | | // Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except |
11 | | // in compliance with the License. You may obtain a copy of the License at |
12 | | // |
13 | | // http://www.apache.org/licenses/LICENSE-2.0 |
14 | | // |
15 | | // Unless required by applicable law or agreed to in writing, software distributed under the License |
16 | | // is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express |
17 | | // or implied. See the License for the specific language governing permissions and limitations |
18 | | // under the License. |
19 | | // |
20 | | // Copyright (c) 2011 The LevelDB Authors. All rights reserved. |
21 | | // Use of this source code is governed by a BSD-style license that can be |
22 | | // found in the LICENSE file. See the AUTHORS file for names of contributors. |
23 | | |
24 | | #include <memory> |
25 | | #include <string> |
26 | | |
27 | | #include <gtest/gtest.h> |
28 | | |
29 | | #include "yb/rocksdb/db.h" |
30 | | |
31 | | #include "yb/rocksdb/db/memtable.h" |
32 | | #include "yb/rocksdb/db/column_family.h" |
33 | | #include "yb/rocksdb/db/write_batch_internal.h" |
34 | | #include "yb/rocksdb/db/writebuffer.h" |
35 | | #include "yb/rocksdb/env.h" |
36 | | #include "yb/rocksdb/memtablerep.h" |
37 | | #include "yb/rocksdb/utilities/write_batch_with_index.h" |
38 | | #include "yb/rocksdb/table/scoped_arena_iterator.h" |
39 | | #include "yb/rocksdb/util/logging.h" |
40 | | #include "yb/util/string_util.h" |
41 | | #include "yb/util/test_macros.h" |
42 | | #include "yb/rocksdb/util/testutil.h" |
43 | | |
44 | | namespace rocksdb { |
45 | | |
46 | 30 | static std::string PrintContents(WriteBatch* b) { |
47 | 30 | InternalKeyComparator cmp(BytewiseComparator()); |
48 | 30 | auto factory = std::make_shared<SkipListFactory>(); |
49 | 30 | Options options; |
50 | 30 | options.memtable_factory = factory; |
51 | 30 | ImmutableCFOptions ioptions(options); |
52 | 30 | WriteBuffer wb(options.db_write_buffer_size); |
53 | 30 | MemTable* mem = |
54 | 30 | new MemTable(cmp, ioptions, MutableCFOptions(options, ioptions), &wb, |
55 | 30 | kMaxSequenceNumber); |
56 | 30 | mem->Ref(); |
57 | 30 | std::string state; |
58 | 30 | ColumnFamilyMemTablesDefault cf_mems_default(mem); |
59 | 30 | Status s = WriteBatchInternal::InsertInto(b, &cf_mems_default, nullptr); |
60 | 30 | size_t count = 0; |
61 | 30 | int put_count = 0; |
62 | 30 | int delete_count = 0; |
63 | 30 | int single_delete_count = 0; |
64 | 30 | int merge_count = 0; |
65 | 30 | Arena arena; |
66 | 30 | ScopedArenaIterator iter(mem->NewIterator(ReadOptions(), &arena)); |
67 | 72 | for (iter->SeekToFirst(); iter->Valid(); iter->Next()) { |
68 | 42 | ParsedInternalKey ikey; |
69 | 42 | memset(static_cast<void*>(&ikey), 0, sizeof(ikey)); |
70 | 42 | EXPECT_TRUE(ParseInternalKey(iter->key(), &ikey)); |
71 | 42 | switch (ikey.type) { |
72 | 26 | case kTypeValue: |
73 | 26 | state.append("Put("); |
74 | 26 | state.append(ikey.user_key.ToString()); |
75 | 26 | state.append(", "); |
76 | 26 | state.append(iter->value().ToString()); |
77 | 26 | state.append(")"); |
78 | 26 | count++; |
79 | 26 | put_count++; |
80 | 26 | break; |
81 | 11 | case kTypeDeletion: |
82 | 11 | state.append("Delete("); |
83 | 11 | state.append(ikey.user_key.ToString()); |
84 | 11 | state.append(")"); |
85 | 11 | count++; |
86 | 11 | delete_count++; |
87 | 11 | break; |
88 | 3 | case kTypeSingleDeletion: |
89 | 3 | state.append("SingleDelete("); |
90 | 3 | state.append(ikey.user_key.ToString()); |
91 | 3 | state.append(")"); |
92 | 3 | count++; |
93 | 3 | single_delete_count++; |
94 | 3 | break; |
95 | 2 | case kTypeMerge: |
96 | 2 | state.append("Merge("); |
97 | 2 | state.append(ikey.user_key.ToString()); |
98 | 2 | state.append(", "); |
99 | 2 | state.append(iter->value().ToString()); |
100 | 2 | state.append(")"); |
101 | 2 | count++; |
102 | 2 | merge_count++; |
103 | 2 | break; |
104 | 0 | default: |
105 | 0 | assert(false); |
106 | 0 | break; |
107 | 42 | } |
108 | 42 | state.append("@"); |
109 | 42 | state.append(NumberToString(ikey.sequence)); |
110 | 42 | } |
111 | 30 | EXPECT_EQ(b->HasPut(), put_count > 0); |
112 | 30 | EXPECT_EQ(b->HasDelete(), delete_count > 0); |
113 | 30 | EXPECT_EQ(b->HasSingleDelete(), single_delete_count > 0); |
114 | 30 | EXPECT_EQ(b->HasMerge(), merge_count > 0); |
115 | 30 | if (!s.ok()) { |
116 | 1 | state.append(s.ToString(false)); |
117 | 29 | } else if (count != WriteBatchInternal::Count(b)) { |
118 | 0 | state.append("CountMismatch()"); |
119 | 0 | } |
120 | 30 | delete mem->Unref(); |
121 | 30 | return state; |
122 | 30 | } |
123 | | |
124 | | class WriteBatchTest : public RocksDBTest {}; |
125 | | |
126 | 1 | TEST_F(WriteBatchTest, Empty) { |
127 | 1 | WriteBatch batch; |
128 | 1 | ASSERT_EQ("", PrintContents(&batch)); |
129 | 1 | ASSERT_EQ(0, WriteBatchInternal::Count(&batch)); |
130 | 1 | ASSERT_EQ(0, batch.Count()); |
131 | 1 | } |
132 | | |
133 | 1 | TEST_F(WriteBatchTest, Multiple) { |
134 | 1 | WriteBatch batch; |
135 | 1 | batch.Put(Slice("foo"), Slice("bar")); |
136 | 1 | batch.Delete(Slice("box")); |
137 | 1 | batch.Put(Slice("baz"), Slice("boo")); |
138 | 1 | WriteBatchInternal::SetSequence(&batch, 100); |
139 | 1 | ASSERT_EQ(100U, WriteBatchInternal::Sequence(&batch)); |
140 | 1 | ASSERT_EQ(3, WriteBatchInternal::Count(&batch)); |
141 | 1 | ASSERT_EQ("Put(baz, boo)@102" |
142 | 1 | "Delete(box)@101" |
143 | 1 | "Put(foo, bar)@100", |
144 | 1 | PrintContents(&batch)); |
145 | 1 | ASSERT_EQ(3, batch.Count()); |
146 | 1 | } |
147 | | |
148 | 1 | TEST_F(WriteBatchTest, Corruption) { |
149 | 1 | WriteBatch batch; |
150 | 1 | batch.Put(Slice("foo"), Slice("bar")); |
151 | 1 | batch.Delete(Slice("box")); |
152 | 1 | WriteBatchInternal::SetSequence(&batch, 200); |
153 | 1 | Slice contents = WriteBatchInternal::Contents(&batch); |
154 | 1 | WriteBatchInternal::SetContents(&batch, |
155 | 1 | Slice(contents.data(), contents.size() - 1)); |
156 | 1 | ASSERT_EQ("Put(foo, bar)@200" |
157 | 1 | "Corruption: bad WriteBatch Delete", |
158 | 1 | PrintContents(&batch)); |
159 | 1 | } |
160 | | |
161 | 1 | TEST_F(WriteBatchTest, Append) { |
162 | 1 | WriteBatch b1, b2; |
163 | 1 | WriteBatchInternal::SetSequence(&b1, 200); |
164 | 1 | WriteBatchInternal::SetSequence(&b2, 300); |
165 | 1 | WriteBatchInternal::Append(&b1, &b2); |
166 | 1 | ASSERT_EQ("", |
167 | 1 | PrintContents(&b1)); |
168 | 1 | ASSERT_EQ(0, b1.Count()); |
169 | 1 | b2.Put("a", "va"); |
170 | 1 | WriteBatchInternal::Append(&b1, &b2); |
171 | 1 | ASSERT_EQ("Put(a, va)@200", |
172 | 1 | PrintContents(&b1)); |
173 | 1 | ASSERT_EQ(1, b1.Count()); |
174 | 1 | b2.Clear(); |
175 | 1 | b2.Put("b", "vb"); |
176 | 1 | WriteBatchInternal::Append(&b1, &b2); |
177 | 1 | ASSERT_EQ("Put(a, va)@200" |
178 | 1 | "Put(b, vb)@201", |
179 | 1 | PrintContents(&b1)); |
180 | 1 | ASSERT_EQ(2, b1.Count()); |
181 | 1 | b2.Delete("foo"); |
182 | 1 | WriteBatchInternal::Append(&b1, &b2); |
183 | 1 | ASSERT_EQ("Put(a, va)@200" |
184 | 1 | "Put(b, vb)@202" |
185 | 1 | "Put(b, vb)@201" |
186 | 1 | "Delete(foo)@203", |
187 | 1 | PrintContents(&b1)); |
188 | 1 | ASSERT_EQ(4, b1.Count()); |
189 | 1 | } |
190 | | |
191 | 1 | TEST_F(WriteBatchTest, SingleDeletion) { |
192 | 1 | WriteBatch batch; |
193 | 1 | WriteBatchInternal::SetSequence(&batch, 100); |
194 | 1 | ASSERT_EQ("", PrintContents(&batch)); |
195 | 1 | ASSERT_EQ(0, batch.Count()); |
196 | 1 | batch.Put("a", "va"); |
197 | 1 | ASSERT_EQ("Put(a, va)@100", PrintContents(&batch)); |
198 | 1 | ASSERT_EQ(1, batch.Count()); |
199 | 1 | batch.SingleDelete("a"); |
200 | 1 | ASSERT_EQ( |
201 | 1 | "SingleDelete(a)@101" |
202 | 1 | "Put(a, va)@100", |
203 | 1 | PrintContents(&batch)); |
204 | 1 | ASSERT_EQ(2, batch.Count()); |
205 | 1 | } |
206 | | |
207 | | namespace { |
208 | | |
209 | | struct TestHandler : public WriteBatch::Handler { |
210 | | std::string seen; |
211 | | virtual Status PutCF(uint32_t column_family_id, const SliceParts& key, |
212 | 13 | const SliceParts& value) override { |
213 | 13 | if (column_family_id == 0) { |
214 | 9 | seen += "Put(" + key.TheOnlyPart().ToDebugString() + ", " + |
215 | 9 | value.TheOnlyPart().ToDebugString() + ")"; |
216 | 4 | } else { |
217 | 4 | seen += "PutCF(" + ToString(column_family_id) + ", " + |
218 | 4 | key.TheOnlyPart().ToDebugString() + ", " + value.TheOnlyPart().ToDebugString() + ")"; |
219 | 4 | } |
220 | 13 | return Status::OK(); |
221 | 13 | } |
222 | | virtual Status DeleteCF(uint32_t column_family_id, |
223 | 4 | const Slice& key) override { |
224 | 4 | if (column_family_id == 0) { |
225 | 2 | seen += "Delete(" + key.ToDebugString() + ")"; |
226 | 2 | } else { |
227 | 2 | seen += "DeleteCF(" + ToString(column_family_id) + ", " + |
228 | 2 | key.ToDebugString() + ")"; |
229 | 2 | } |
230 | 4 | return Status::OK(); |
231 | 4 | } |
232 | | virtual Status SingleDeleteCF(uint32_t column_family_id, |
233 | 4 | const Slice& key) override { |
234 | 4 | if (column_family_id == 0) { |
235 | 2 | seen += "SingleDelete(" + key.ToDebugString() + ")"; |
236 | 2 | } else { |
237 | 2 | seen += "SingleDeleteCF(" + ToString(column_family_id) + ", " + |
238 | 2 | key.ToDebugString() + ")"; |
239 | 2 | } |
240 | 4 | return Status::OK(); |
241 | 4 | } |
242 | | virtual Status MergeCF(uint32_t column_family_id, const Slice& key, |
243 | 5 | const Slice& value) override { |
244 | 5 | if (column_family_id == 0) { |
245 | 3 | seen += "Merge(" + key.ToDebugString() + ", " + value.ToDebugString() + ")"; |
246 | 2 | } else { |
247 | 2 | seen += "MergeCF(" + ToString(column_family_id) + ", " + |
248 | 2 | key.ToDebugString() + ", " + value.ToDebugString() + ")"; |
249 | 2 | } |
250 | 5 | return Status::OK(); |
251 | 5 | } |
252 | 3 | void LogData(const Slice& blob) override { |
253 | 3 | seen += "LogData(" + blob.ToDebugString() + ")"; |
254 | 3 | } |
255 | | }; |
256 | | |
257 | | } // namespace |
258 | | |
259 | 1 | TEST_F(WriteBatchTest, PutNotImplemented) { |
260 | 1 | WriteBatch batch; |
261 | 1 | batch.Put(Slice("k1"), Slice("v1")); |
262 | 1 | ASSERT_EQ(1, batch.Count()); |
263 | 1 | ASSERT_EQ("Put(k1, v1)@0", PrintContents(&batch)); |
264 | | |
265 | 1 | WriteBatch::Handler handler; |
266 | 1 | ASSERT_OK(batch.Iterate(&handler)); |
267 | 1 | } |
268 | | |
269 | 1 | TEST_F(WriteBatchTest, DeleteNotImplemented) { |
270 | 1 | WriteBatch batch; |
271 | 1 | batch.Delete(Slice("k2")); |
272 | 1 | ASSERT_EQ(1, batch.Count()); |
273 | 1 | ASSERT_EQ("Delete(k2)@0", PrintContents(&batch)); |
274 | | |
275 | 1 | WriteBatch::Handler handler; |
276 | 1 | ASSERT_OK(batch.Iterate(&handler)); |
277 | 1 | } |
278 | | |
279 | 1 | TEST_F(WriteBatchTest, SingleDeleteNotImplemented) { |
280 | 1 | WriteBatch batch; |
281 | 1 | batch.SingleDelete(Slice("k2")); |
282 | 1 | ASSERT_EQ(1, batch.Count()); |
283 | 1 | ASSERT_EQ("SingleDelete(k2)@0", PrintContents(&batch)); |
284 | | |
285 | 1 | WriteBatch::Handler handler; |
286 | 1 | ASSERT_OK(batch.Iterate(&handler)); |
287 | 1 | } |
288 | | |
289 | 1 | TEST_F(WriteBatchTest, MergeNotImplemented) { |
290 | 1 | WriteBatch batch; |
291 | 1 | batch.Merge(Slice("foo"), Slice("bar")); |
292 | 1 | ASSERT_EQ(1, batch.Count()); |
293 | 1 | ASSERT_EQ("Merge(foo, bar)@0", PrintContents(&batch)); |
294 | | |
295 | 1 | WriteBatch::Handler handler; |
296 | 1 | ASSERT_OK(batch.Iterate(&handler)); |
297 | 1 | } |
298 | | |
299 | 1 | TEST_F(WriteBatchTest, Blob) { |
300 | 1 | WriteBatch batch; |
301 | 1 | batch.Put(Slice("k1"), Slice("v1")); |
302 | 1 | batch.Put(Slice("k2"), Slice("v2")); |
303 | 1 | batch.Put(Slice("k3"), Slice("v3")); |
304 | 1 | batch.PutLogData(Slice("blob1")); |
305 | 1 | batch.Delete(Slice("k2")); |
306 | 1 | batch.SingleDelete(Slice("k3")); |
307 | 1 | batch.PutLogData(Slice("blob2")); |
308 | 1 | batch.Merge(Slice("foo"), Slice("bar")); |
309 | 1 | ASSERT_EQ(6, batch.Count()); |
310 | 1 | ASSERT_EQ( |
311 | 1 | "Merge(foo, bar)@5" |
312 | 1 | "Put(k1, v1)@0" |
313 | 1 | "Delete(k2)@3" |
314 | 1 | "Put(k2, v2)@1" |
315 | 1 | "SingleDelete(k3)@4" |
316 | 1 | "Put(k3, v3)@2", |
317 | 1 | PrintContents(&batch)); |
318 | | |
319 | 1 | TestHandler handler; |
320 | 1 | ASSERT_OK(batch.Iterate(&handler)); |
321 | 1 | ASSERT_EQ( |
322 | 1 | "Put(k1, v1)" |
323 | 1 | "Put(k2, v2)" |
324 | 1 | "Put(k3, v3)" |
325 | 1 | "LogData(blob1)" |
326 | 1 | "Delete(k2)" |
327 | 1 | "SingleDelete(k3)" |
328 | 1 | "LogData(blob2)" |
329 | 1 | "Merge(foo, bar)", |
330 | 1 | handler.seen); |
331 | 1 | } |
332 | | |
333 | | // It requires more than 30GB of memory to run the test. With single memory |
334 | | // allocation of more than 30GB. |
335 | | // Not all platform can run it. Also it runs a long time. So disable it. |
336 | | TEST_F(WriteBatchTest, DISABLED_ManyUpdates) { |
337 | | // Insert key and value of 3GB and push total batch size to 12GB. |
338 | | static const size_t kKeyValueSize = 4u; |
339 | | static const uint32_t kNumUpdates = 3 << 30; |
340 | | std::string raw(kKeyValueSize, 'A'); |
341 | | WriteBatch batch(kNumUpdates * (4 + kKeyValueSize * 2) + 1024u); |
342 | | char c = 'A'; |
343 | | for (uint32_t i = 0; i < kNumUpdates; i++) { |
344 | | if (c > 'Z') { |
345 | | c = 'A'; |
346 | | } |
347 | | raw[0] = c; |
348 | | raw[raw.length() - 1] = c; |
349 | | c++; |
350 | | batch.Put(raw, raw); |
351 | | } |
352 | | |
353 | | ASSERT_EQ(kNumUpdates, batch.Count()); |
354 | | |
355 | | struct NoopHandler : public WriteBatch::Handler { |
356 | | uint32_t num_seen = 0; |
357 | | char expected_char = 'A'; |
358 | | virtual Status PutCF(uint32_t column_family_id, const SliceParts& key, |
359 | | const SliceParts& value) override { |
360 | | EXPECT_EQ(kKeyValueSize, key.TheOnlyPart().size()); |
361 | | EXPECT_EQ(kKeyValueSize, value.TheOnlyPart().size()); |
362 | | EXPECT_EQ(expected_char, key.TheOnlyPart()[0]); |
363 | | EXPECT_EQ(expected_char, value.TheOnlyPart()[0]); |
364 | | EXPECT_EQ(expected_char, key.TheOnlyPart()[kKeyValueSize - 1]); |
365 | | EXPECT_EQ(expected_char, value.TheOnlyPart()[kKeyValueSize - 1]); |
366 | | expected_char++; |
367 | | if (expected_char > 'Z') { |
368 | | expected_char = 'A'; |
369 | | } |
370 | | ++num_seen; |
371 | | return Status::OK(); |
372 | | } |
373 | | virtual Status DeleteCF(uint32_t column_family_id, |
374 | 0 | const Slice& key) override { |
375 | 0 | EXPECT_TRUE(false); |
376 | 0 | return Status::OK(); |
377 | 0 | } |
378 | | virtual Status SingleDeleteCF(uint32_t column_family_id, |
379 | 0 | const Slice& key) override { |
380 | 0 | EXPECT_TRUE(false); |
381 | 0 | return Status::OK(); |
382 | 0 | } |
383 | | virtual Status MergeCF(uint32_t column_family_id, const Slice& key, |
384 | 0 | const Slice& value) override { |
385 | 0 | EXPECT_TRUE(false); |
386 | 0 | return Status::OK(); |
387 | 0 | } |
388 | 0 | void LogData(const Slice& blob) override { EXPECT_TRUE(false); } |
389 | 0 | bool Continue() override { return num_seen < kNumUpdates; } |
390 | | } handler; |
391 | | |
392 | | ASSERT_OK(batch.Iterate(&handler)); |
393 | | ASSERT_EQ(kNumUpdates, handler.num_seen); |
394 | | } |
395 | | |
396 | | // The test requires more than 18GB memory to run it, with single memory |
397 | | // allocation of more than 12GB. Not all the platform can run it. So disable it. |
398 | | TEST_F(WriteBatchTest, DISABLED_LargeKeyValue) { |
399 | | // Insert key and value of 3GB and push total batch size to 12GB. |
400 | | static const size_t kKeyValueSize = 3221225472u; |
401 | | std::string raw(kKeyValueSize, 'A'); |
402 | | WriteBatch batch(12884901888u + 1024u); |
403 | | for (char i = 0; i < 2; i++) { |
404 | | raw[0] = 'A' + i; |
405 | | raw[raw.length() - 1] = 'A' - i; |
406 | | batch.Put(raw, raw); |
407 | | } |
408 | | |
409 | | ASSERT_EQ(2, batch.Count()); |
410 | | |
411 | | struct NoopHandler : public WriteBatch::Handler { |
412 | | int num_seen = 0; |
413 | | virtual Status PutCF(uint32_t column_family_id, const SliceParts& key, |
414 | | const SliceParts& value) override { |
415 | | EXPECT_EQ(kKeyValueSize, key.TheOnlyPart().size()); |
416 | | EXPECT_EQ(kKeyValueSize, value.TheOnlyPart().size()); |
417 | | EXPECT_EQ('A' + num_seen, key.TheOnlyPart()[0]); |
418 | | EXPECT_EQ('A' + num_seen, value.TheOnlyPart()[0]); |
419 | | EXPECT_EQ('A' - num_seen, key.TheOnlyPart()[kKeyValueSize - 1]); |
420 | | EXPECT_EQ('A' - num_seen, value.TheOnlyPart()[kKeyValueSize - 1]); |
421 | | ++num_seen; |
422 | | return Status::OK(); |
423 | | } |
424 | | virtual Status DeleteCF(uint32_t column_family_id, |
425 | 0 | const Slice& key) override { |
426 | 0 | EXPECT_TRUE(false); |
427 | 0 | return Status::OK(); |
428 | 0 | } |
429 | | virtual Status SingleDeleteCF(uint32_t column_family_id, |
430 | 0 | const Slice& key) override { |
431 | 0 | EXPECT_TRUE(false); |
432 | 0 | return Status::OK(); |
433 | 0 | } |
434 | | virtual Status MergeCF(uint32_t column_family_id, const Slice& key, |
435 | 0 | const Slice& value) override { |
436 | 0 | EXPECT_TRUE(false); |
437 | 0 | return Status::OK(); |
438 | 0 | } |
439 | 0 | void LogData(const Slice& blob) override { EXPECT_TRUE(false); } |
440 | 0 | bool Continue() override { return num_seen < 2; } |
441 | | } handler; |
442 | | |
443 | | ASSERT_OK(batch.Iterate(&handler)); |
444 | | ASSERT_EQ(2, handler.num_seen); |
445 | | } |
446 | | |
447 | 1 | TEST_F(WriteBatchTest, Continue) { |
448 | 1 | WriteBatch batch; |
449 | | |
450 | 1 | struct Handler : public TestHandler { |
451 | 1 | int num_seen = 0; |
452 | 1 | virtual Status PutCF(uint32_t column_family_id, const SliceParts& key, |
453 | 2 | const SliceParts& value) override { |
454 | 2 | ++num_seen; |
455 | 2 | return TestHandler::PutCF(column_family_id, key, value); |
456 | 2 | } |
457 | 1 | virtual Status DeleteCF(uint32_t column_family_id, |
458 | 1 | const Slice& key) override { |
459 | 1 | ++num_seen; |
460 | 1 | return TestHandler::DeleteCF(column_family_id, key); |
461 | 1 | } |
462 | 1 | virtual Status SingleDeleteCF(uint32_t column_family_id, |
463 | 1 | const Slice& key) override { |
464 | 1 | ++num_seen; |
465 | 1 | return TestHandler::SingleDeleteCF(column_family_id, key); |
466 | 1 | } |
467 | 1 | virtual Status MergeCF(uint32_t column_family_id, const Slice& key, |
468 | 0 | const Slice& value) override { |
469 | 0 | ++num_seen; |
470 | 0 | return TestHandler::MergeCF(column_family_id, key, value); |
471 | 0 | } |
472 | 1 | void LogData(const Slice& blob) override { |
473 | 1 | ++num_seen; |
474 | 1 | TestHandler::LogData(blob); |
475 | 1 | } |
476 | 6 | bool Continue() override { return num_seen < 5; } |
477 | 1 | } handler; |
478 | | |
479 | 1 | batch.Put(Slice("k1"), Slice("v1")); |
480 | 1 | batch.Put(Slice("k2"), Slice("v2")); |
481 | 1 | batch.PutLogData(Slice("blob1")); |
482 | 1 | batch.Delete(Slice("k1")); |
483 | 1 | batch.SingleDelete(Slice("k2")); |
484 | 1 | batch.PutLogData(Slice("blob2")); |
485 | 1 | batch.Merge(Slice("foo"), Slice("bar")); |
486 | 1 | auto status = batch.Iterate(&handler); |
487 | 1 | LOG(INFO) << "Iterate result: " << status; |
488 | 1 | ASSERT_EQ( |
489 | 1 | "Put(k1, v1)" |
490 | 1 | "Put(k2, v2)" |
491 | 1 | "LogData(blob1)" |
492 | 1 | "Delete(k1)" |
493 | 1 | "SingleDelete(k2)", |
494 | 1 | handler.seen); |
495 | 1 | } |
496 | | |
497 | 1 | TEST_F(WriteBatchTest, PutGatherSlices) { |
498 | 1 | WriteBatch batch; |
499 | 1 | batch.Put(Slice("foo"), Slice("bar")); |
500 | | |
501 | 1 | { |
502 | | // Try a write where the key is one slice but the value is two |
503 | 1 | Slice key_slice("baz"); |
504 | 1 | Slice value_slices[2] = { Slice("header"), Slice("payload") }; |
505 | 1 | batch.Put(SliceParts(&key_slice, 1), |
506 | 1 | SliceParts(value_slices, 2)); |
507 | 1 | } |
508 | | |
509 | 1 | { |
510 | | // One where the key is composite but the value is a single slice |
511 | 1 | Slice key_slices[3] = { Slice("key"), Slice("part2"), Slice("part3") }; |
512 | 1 | Slice value_slice("value"); |
513 | 1 | batch.Put(SliceParts(key_slices, 3), |
514 | 1 | SliceParts(&value_slice, 1)); |
515 | 1 | } |
516 | | |
517 | 1 | WriteBatchInternal::SetSequence(&batch, 100); |
518 | 1 | ASSERT_EQ("Put(baz, headerpayload)@101" |
519 | 1 | "Put(foo, bar)@100" |
520 | 1 | "Put(keypart2part3, value)@102", |
521 | 1 | PrintContents(&batch)); |
522 | 1 | ASSERT_EQ(3, batch.Count()); |
523 | 1 | } |
524 | | |
525 | | namespace { |
526 | | class ColumnFamilyHandleImplDummy : public ColumnFamilyHandleImpl { |
527 | | public: |
528 | | explicit ColumnFamilyHandleImplDummy(int id) |
529 | 8 | : ColumnFamilyHandleImpl(nullptr, nullptr, nullptr), id_(id) {} |
530 | 31 | uint32_t GetID() const override { return id_; } |
531 | 7 | const Comparator* user_comparator() const override { |
532 | 7 | return BytewiseComparator(); |
533 | 7 | } |
534 | | |
535 | | private: |
536 | | uint32_t id_; |
537 | | }; |
538 | | |
539 | | } // anonymous namespace |
540 | | |
541 | 1 | TEST_F(WriteBatchTest, ColumnFamiliesBatchTest) { |
542 | 1 | WriteBatch batch; |
543 | 1 | ColumnFamilyHandleImplDummy zero(0), two(2), three(3), eight(8); |
544 | 1 | batch.Put(&zero, Slice("foo"), Slice("bar")); |
545 | 1 | batch.Put(&two, Slice("twofoo"), Slice("bar2")); |
546 | 1 | batch.Put(&eight, Slice("eightfoo"), Slice("bar8")); |
547 | 1 | batch.Delete(&eight, Slice("eightfoo")); |
548 | 1 | batch.SingleDelete(&two, Slice("twofoo")); |
549 | 1 | batch.Merge(&three, Slice("threethree"), Slice("3three")); |
550 | 1 | batch.Put(&zero, Slice("foo"), Slice("bar")); |
551 | 1 | batch.Merge(Slice("omom"), Slice("nom")); |
552 | | |
553 | 1 | TestHandler handler; |
554 | 1 | ASSERT_OK(batch.Iterate(&handler)); |
555 | 1 | ASSERT_EQ( |
556 | 1 | "Put(foo, bar)" |
557 | 1 | "PutCF(2, twofoo, bar2)" |
558 | 1 | "PutCF(8, eightfoo, bar8)" |
559 | 1 | "DeleteCF(8, eightfoo)" |
560 | 1 | "SingleDeleteCF(2, twofoo)" |
561 | 1 | "MergeCF(3, threethree, 3three)" |
562 | 1 | "Put(foo, bar)" |
563 | 1 | "Merge(omom, nom)", |
564 | 1 | handler.seen); |
565 | 1 | } |
566 | | |
567 | | #ifndef ROCKSDB_LITE |
568 | 1 | TEST_F(WriteBatchTest, ColumnFamiliesBatchWithIndexTest) { |
569 | 1 | WriteBatchWithIndex batch; |
570 | 1 | ColumnFamilyHandleImplDummy zero(0), two(2), three(3), eight(8); |
571 | 1 | batch.Put(&zero, Slice("foo"), Slice("bar")); |
572 | 1 | batch.Put(&two, Slice("twofoo"), Slice("bar2")); |
573 | 1 | batch.Put(&eight, Slice("eightfoo"), Slice("bar8")); |
574 | 1 | batch.Delete(&eight, Slice("eightfoo")); |
575 | 1 | batch.SingleDelete(&two, Slice("twofoo")); |
576 | 1 | batch.Merge(&three, Slice("threethree"), Slice("3three")); |
577 | 1 | batch.Put(&zero, Slice("foo"), Slice("bar")); |
578 | 1 | batch.Merge(Slice("omom"), Slice("nom")); |
579 | | |
580 | 1 | std::unique_ptr<WBWIIterator> iter; |
581 | | |
582 | 1 | iter.reset(batch.NewIterator(&eight)); |
583 | 1 | iter->Seek("eightfoo"); |
584 | 1 | ASSERT_OK(iter->status()); |
585 | 1 | ASSERT_TRUE(iter->Valid()); |
586 | 1 | ASSERT_EQ(WriteType::kPutRecord, iter->Entry().type); |
587 | 1 | ASSERT_EQ("eightfoo", iter->Entry().key.ToString()); |
588 | 1 | ASSERT_EQ("bar8", iter->Entry().value.ToString()); |
589 | | |
590 | 1 | iter->Next(); |
591 | 1 | ASSERT_OK(iter->status()); |
592 | 1 | ASSERT_TRUE(iter->Valid()); |
593 | 1 | ASSERT_EQ(WriteType::kDeleteRecord, iter->Entry().type); |
594 | 1 | ASSERT_EQ("eightfoo", iter->Entry().key.ToString()); |
595 | | |
596 | 1 | iter->Next(); |
597 | 1 | ASSERT_OK(iter->status()); |
598 | 1 | ASSERT_TRUE(!iter->Valid()); |
599 | | |
600 | 1 | iter.reset(batch.NewIterator(&two)); |
601 | 1 | iter->Seek("twofoo"); |
602 | 1 | ASSERT_OK(iter->status()); |
603 | 1 | ASSERT_TRUE(iter->Valid()); |
604 | 1 | ASSERT_EQ(WriteType::kPutRecord, iter->Entry().type); |
605 | 1 | ASSERT_EQ("twofoo", iter->Entry().key.ToString()); |
606 | 1 | ASSERT_EQ("bar2", iter->Entry().value.ToString()); |
607 | | |
608 | 1 | iter->Next(); |
609 | 1 | ASSERT_OK(iter->status()); |
610 | 1 | ASSERT_TRUE(iter->Valid()); |
611 | 1 | ASSERT_EQ(WriteType::kSingleDeleteRecord, iter->Entry().type); |
612 | 1 | ASSERT_EQ("twofoo", iter->Entry().key.ToString()); |
613 | | |
614 | 1 | iter->Next(); |
615 | 1 | ASSERT_OK(iter->status()); |
616 | 1 | ASSERT_TRUE(!iter->Valid()); |
617 | | |
618 | 1 | iter.reset(batch.NewIterator()); |
619 | 1 | iter->Seek("gggg"); |
620 | 1 | ASSERT_OK(iter->status()); |
621 | 1 | ASSERT_TRUE(iter->Valid()); |
622 | 1 | ASSERT_EQ(WriteType::kMergeRecord, iter->Entry().type); |
623 | 1 | ASSERT_EQ("omom", iter->Entry().key.ToString()); |
624 | 1 | ASSERT_EQ("nom", iter->Entry().value.ToString()); |
625 | | |
626 | 1 | iter->Next(); |
627 | 1 | ASSERT_OK(iter->status()); |
628 | 1 | ASSERT_TRUE(!iter->Valid()); |
629 | | |
630 | 1 | iter.reset(batch.NewIterator(&zero)); |
631 | 1 | iter->Seek("foo"); |
632 | 1 | ASSERT_OK(iter->status()); |
633 | 1 | ASSERT_TRUE(iter->Valid()); |
634 | 1 | ASSERT_EQ(WriteType::kPutRecord, iter->Entry().type); |
635 | 1 | ASSERT_EQ("foo", iter->Entry().key.ToString()); |
636 | 1 | ASSERT_EQ("bar", iter->Entry().value.ToString()); |
637 | | |
638 | 1 | iter->Next(); |
639 | 1 | ASSERT_OK(iter->status()); |
640 | 1 | ASSERT_TRUE(iter->Valid()); |
641 | 1 | ASSERT_EQ(WriteType::kPutRecord, iter->Entry().type); |
642 | 1 | ASSERT_EQ("foo", iter->Entry().key.ToString()); |
643 | 1 | ASSERT_EQ("bar", iter->Entry().value.ToString()); |
644 | | |
645 | 1 | iter->Next(); |
646 | 1 | ASSERT_OK(iter->status()); |
647 | 1 | ASSERT_TRUE(iter->Valid()); |
648 | 1 | ASSERT_EQ(WriteType::kMergeRecord, iter->Entry().type); |
649 | 1 | ASSERT_EQ("omom", iter->Entry().key.ToString()); |
650 | 1 | ASSERT_EQ("nom", iter->Entry().value.ToString()); |
651 | | |
652 | 1 | iter->Next(); |
653 | 1 | ASSERT_OK(iter->status()); |
654 | 1 | ASSERT_TRUE(!iter->Valid()); |
655 | | |
656 | 1 | TestHandler handler; |
657 | 1 | ASSERT_OK(batch.GetWriteBatch()->Iterate(&handler)); |
658 | 1 | ASSERT_EQ( |
659 | 1 | "Put(foo, bar)" |
660 | 1 | "PutCF(2, twofoo, bar2)" |
661 | 1 | "PutCF(8, eightfoo, bar8)" |
662 | 1 | "DeleteCF(8, eightfoo)" |
663 | 1 | "SingleDeleteCF(2, twofoo)" |
664 | 1 | "MergeCF(3, threethree, 3three)" |
665 | 1 | "Put(foo, bar)" |
666 | 1 | "Merge(omom, nom)", |
667 | 1 | handler.seen); |
668 | 1 | } |
669 | | #endif // !ROCKSDB_LITE |
670 | | |
671 | 1 | TEST_F(WriteBatchTest, SavePointTest) { |
672 | 1 | Status s; |
673 | 1 | WriteBatch batch; |
674 | 1 | batch.SetSavePoint(); |
675 | | |
676 | 1 | batch.Put("A", "a"); |
677 | 1 | batch.Put("B", "b"); |
678 | 1 | batch.SetSavePoint(); |
679 | | |
680 | 1 | batch.Put("C", "c"); |
681 | 1 | batch.Delete("A"); |
682 | 1 | batch.SetSavePoint(); |
683 | 1 | batch.SetSavePoint(); |
684 | | |
685 | 1 | ASSERT_OK(batch.RollbackToSavePoint()); |
686 | 1 | ASSERT_EQ( |
687 | 1 | "Delete(A)@3" |
688 | 1 | "Put(A, a)@0" |
689 | 1 | "Put(B, b)@1" |
690 | 1 | "Put(C, c)@2", |
691 | 1 | PrintContents(&batch)); |
692 | | |
693 | 1 | ASSERT_OK(batch.RollbackToSavePoint()); |
694 | 1 | ASSERT_OK(batch.RollbackToSavePoint()); |
695 | 1 | ASSERT_EQ( |
696 | 1 | "Put(A, a)@0" |
697 | 1 | "Put(B, b)@1", |
698 | 1 | PrintContents(&batch)); |
699 | | |
700 | 1 | batch.Delete("A"); |
701 | 1 | batch.Put("B", "bb"); |
702 | | |
703 | 1 | ASSERT_OK(batch.RollbackToSavePoint()); |
704 | 1 | ASSERT_EQ("", PrintContents(&batch)); |
705 | | |
706 | 1 | s = batch.RollbackToSavePoint(); |
707 | 1 | ASSERT_TRUE(s.IsNotFound()); |
708 | 1 | ASSERT_EQ("", PrintContents(&batch)); |
709 | | |
710 | 1 | batch.Put("D", "d"); |
711 | 1 | batch.Delete("A"); |
712 | | |
713 | 1 | batch.SetSavePoint(); |
714 | | |
715 | 1 | batch.Put("A", "aaa"); |
716 | | |
717 | 1 | ASSERT_OK(batch.RollbackToSavePoint()); |
718 | 1 | ASSERT_EQ( |
719 | 1 | "Delete(A)@1" |
720 | 1 | "Put(D, d)@0", |
721 | 1 | PrintContents(&batch)); |
722 | | |
723 | 1 | batch.SetSavePoint(); |
724 | | |
725 | 1 | batch.Put("D", "d"); |
726 | 1 | batch.Delete("A"); |
727 | | |
728 | 1 | ASSERT_OK(batch.RollbackToSavePoint()); |
729 | 1 | ASSERT_EQ( |
730 | 1 | "Delete(A)@1" |
731 | 1 | "Put(D, d)@0", |
732 | 1 | PrintContents(&batch)); |
733 | | |
734 | 1 | s = batch.RollbackToSavePoint(); |
735 | 1 | ASSERT_TRUE(s.IsNotFound()); |
736 | 1 | ASSERT_EQ( |
737 | 1 | "Delete(A)@1" |
738 | 1 | "Put(D, d)@0", |
739 | 1 | PrintContents(&batch)); |
740 | | |
741 | 1 | WriteBatch batch2; |
742 | | |
743 | 1 | s = batch2.RollbackToSavePoint(); |
744 | 1 | ASSERT_TRUE(s.IsNotFound()); |
745 | 1 | ASSERT_EQ("", PrintContents(&batch2)); |
746 | | |
747 | 1 | batch2.Delete("A"); |
748 | 1 | batch2.SetSavePoint(); |
749 | | |
750 | 1 | s = batch2.RollbackToSavePoint(); |
751 | 1 | ASSERT_OK(s); |
752 | 1 | ASSERT_EQ("Delete(A)@0", PrintContents(&batch2)); |
753 | | |
754 | 1 | batch2.Clear(); |
755 | 1 | ASSERT_EQ("", PrintContents(&batch2)); |
756 | | |
757 | 1 | batch2.SetSavePoint(); |
758 | | |
759 | 1 | batch2.Delete("B"); |
760 | 1 | ASSERT_EQ("Delete(B)@0", PrintContents(&batch2)); |
761 | | |
762 | 1 | batch2.SetSavePoint(); |
763 | 1 | s = batch2.RollbackToSavePoint(); |
764 | 1 | ASSERT_OK(s); |
765 | 1 | ASSERT_EQ("Delete(B)@0", PrintContents(&batch2)); |
766 | | |
767 | 1 | s = batch2.RollbackToSavePoint(); |
768 | 1 | ASSERT_OK(s); |
769 | 1 | ASSERT_EQ("", PrintContents(&batch2)); |
770 | | |
771 | 1 | s = batch2.RollbackToSavePoint(); |
772 | 1 | ASSERT_TRUE(s.IsNotFound()); |
773 | 1 | ASSERT_EQ("", PrintContents(&batch2)); |
774 | 1 | } |
775 | | |
776 | | } // namespace rocksdb |
777 | | |
778 | 13.2k | int main(int argc, char** argv) { |
779 | 13.2k | ::testing::InitGoogleTest(&argc, argv); |
780 | 13.2k | return RUN_ALL_TESTS(); |
781 | 13.2k | } |