YugabyteDB (2.13.0.0-b42, bfc6a6643e7399ac8a0e81d06a3ee6d6571b33ab)

Coverage Report

Created: 2022-03-09 17:30

/Users/deen/code/yugabyte-db/src/yb/rocksdb/utilities/transactions/transaction_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
21
#ifndef ROCKSDB_LITE
22
23
#include <string>
24
25
#include "yb/rocksdb/db/db_impl.h"
26
#include "yb/rocksdb/db.h"
27
#include "yb/rocksdb/options.h"
28
#include "yb/rocksdb/utilities/transaction.h"
29
#include "yb/rocksdb/utilities/transaction_db.h"
30
#include "yb/rocksdb/table/mock_table.h"
31
#include "yb/rocksdb/util/logging.h"
32
#include "yb/rocksdb/util/sync_point.h"
33
#include "yb/rocksdb/util/testharness.h"
34
#include "yb/rocksdb/util/testutil.h"
35
#include "yb/rocksdb/utilities/merge_operators.h"
36
#include "yb/rocksdb/utilities/merge_operators/string_append/stringappend.h"
37
38
#include "yb/util/status_log.h"
39
#include "yb/util/test_util.h"
40
41
using std::string;
42
43
namespace rocksdb {
44
45
class TransactionTest : public RocksDBTest {
46
 public:
47
  TransactionDB* db;
48
  string dbname;
49
  Options options;
50
51
  TransactionDBOptions txn_db_options;
52
53
39
  TransactionTest() {
54
39
    options.create_if_missing = true;
55
39
    options.max_write_buffer_number = 2;
56
39
    options.write_buffer_size = 4 * 1024;
57
39
    options.level0_file_num_compaction_trigger = 2;
58
39
    options.merge_operator = MergeOperators::CreateFromStringId("stringappend");
59
39
    dbname = test::TmpDir() + "/transaction_testdb";
60
61
39
    CHECK_OK(DestroyDB(dbname, options));
62
39
    txn_db_options.transaction_lock_timeout = 0;
63
39
    txn_db_options.default_lock_timeout = 0;
64
39
    Status s = TransactionDB::Open(options, txn_db_options, dbname, &db);
65
39
    assert(s.ok());
66
39
  }
67
68
39
  ~TransactionTest() {
69
39
    delete db;
70
39
    CHECK_OK(DestroyDB(dbname, options));
71
39
  }
72
73
3
  Status ReOpen() {
74
3
    delete db;
75
3
    RETURN_NOT_OK(DestroyDB(dbname, options));
76
77
3
    Status s = TransactionDB::Open(options, txn_db_options, dbname, &db);
78
79
3
    return s;
80
3
  }
81
};
82
83
1
TEST_F(TransactionTest, DoubleEmptyWrite) {
84
1
  WriteOptions write_options;
85
1
  write_options.sync = true;
86
1
  write_options.disableWAL = false;
87
88
1
  WriteBatch batch;
89
90
1
  ASSERT_OK(db->Write(write_options, &batch));
91
1
  ASSERT_OK(db->Write(write_options, &batch));
92
1
}
93
94
1
TEST_F(TransactionTest, SuccessTest) {
95
1
  WriteOptions write_options;
96
1
  ReadOptions read_options;
97
1
  string value;
98
1
  Status s;
99
100
1
  ASSERT_OK(db->Put(write_options, Slice("foo"), Slice("bar")));
101
1
  ASSERT_OK(db->Put(write_options, Slice("foo2"), Slice("bar")));
102
103
1
  Transaction* txn = db->BeginTransaction(write_options, TransactionOptions());
104
1
  ASSERT_TRUE(txn);
105
106
1
  ASSERT_EQ(0, txn->GetNumPuts());
107
108
1
  s = txn->GetForUpdate(read_options, "foo", &value);
109
1
  ASSERT_OK(s);
110
1
  ASSERT_EQ(value, "bar");
111
112
1
  s = txn->Put(Slice("foo"), Slice("bar2"));
113
1
  ASSERT_OK(s);
114
115
1
  ASSERT_EQ(1, txn->GetNumPuts());
116
117
1
  s = txn->GetForUpdate(read_options, "foo", &value);
118
1
  ASSERT_OK(s);
119
1
  ASSERT_EQ(value, "bar2");
120
121
1
  s = txn->Commit();
122
1
  ASSERT_OK(s);
123
124
1
  s = db->Get(read_options, "foo", &value);
125
1
  ASSERT_OK(s);
126
1
  ASSERT_EQ(value, "bar2");
127
128
1
  delete txn;
129
1
}
130
131
1
TEST_F(TransactionTest, FirstWriteTest) {
132
1
  WriteOptions write_options;
133
134
  // Test conflict checking against the very first write to a db.
135
  // The transaction's snapshot will have seq 1 and the following write
136
  // will have sequence 1.
137
1
  Status s = db->Put(write_options, "A", "a");
138
139
1
  Transaction* txn = db->BeginTransaction(write_options);
140
1
  txn->SetSnapshot();
141
142
1
  ASSERT_OK(s);
143
144
1
  s = txn->Put("A", "b");
145
1
  ASSERT_OK(s);
146
147
1
  delete txn;
148
1
}
149
150
1
TEST_F(TransactionTest, FirstWriteTest2) {
151
1
  WriteOptions write_options;
152
153
1
  Transaction* txn = db->BeginTransaction(write_options);
154
1
  txn->SetSnapshot();
155
156
  // Test conflict checking against the very first write to a db.
157
  // The transaction's snapshot is a seq 0 while the following write
158
  // will have sequence 1.
159
1
  Status s = db->Put(write_options, "A", "a");
160
1
  ASSERT_OK(s);
161
162
1
  s = txn->Put("A", "b");
163
1
  ASSERT_TRUE(s.IsBusy());
164
165
1
  delete txn;
166
1
}
167
168
1
TEST_F(TransactionTest, WriteOptionsTest) {
169
1
  WriteOptions write_options;
170
1
  write_options.sync = true;
171
1
  write_options.disableWAL = true;
172
173
1
  Transaction* txn = db->BeginTransaction(write_options);
174
1
  ASSERT_TRUE(txn);
175
176
1
  ASSERT_TRUE(txn->GetWriteOptions()->sync);
177
178
1
  write_options.sync = false;
179
1
  txn->SetWriteOptions(write_options);
180
1
  ASSERT_FALSE(txn->GetWriteOptions()->sync);
181
1
  ASSERT_TRUE(txn->GetWriteOptions()->disableWAL);
182
183
1
  delete txn;
184
1
}
185
186
1
TEST_F(TransactionTest, WriteConflictTest) {
187
1
  WriteOptions write_options;
188
1
  ReadOptions read_options;
189
1
  string value;
190
1
  Status s;
191
192
1
  ASSERT_OK(db->Put(write_options, "foo", "A"));
193
1
  ASSERT_OK(db->Put(write_options, "foo2", "B"));
194
195
1
  Transaction* txn = db->BeginTransaction(write_options);
196
1
  ASSERT_TRUE(txn);
197
198
1
  s = txn->Put("foo", "A2");
199
1
  ASSERT_OK(s);
200
201
1
  s = txn->Put("foo2", "B2");
202
1
  ASSERT_OK(s);
203
204
  // This Put outside of a transaction will conflict with the previous write
205
1
  s = db->Put(write_options, "foo", "xxx");
206
1
  ASSERT_TRUE(s.IsTimedOut());
207
208
1
  s = db->Get(read_options, "foo", &value);
209
1
  ASSERT_EQ(value, "A");
210
211
1
  s = txn->Commit();
212
1
  ASSERT_OK(s);
213
214
1
  ASSERT_OK(db->Get(read_options, "foo", &value));
215
1
  ASSERT_EQ(value, "A2");
216
1
  ASSERT_OK(db->Get(read_options, "foo2", &value));
217
1
  ASSERT_EQ(value, "B2");
218
219
1
  delete txn;
220
1
}
221
222
1
TEST_F(TransactionTest, WriteConflictTest2) {
223
1
  WriteOptions write_options;
224
1
  ReadOptions read_options;
225
1
  TransactionOptions txn_options;
226
1
  string value;
227
1
  Status s;
228
229
1
  ASSERT_OK(db->Put(write_options, "foo", "bar"));
230
231
1
  txn_options.set_snapshot = true;
232
1
  Transaction* txn = db->BeginTransaction(write_options, txn_options);
233
1
  ASSERT_TRUE(txn);
234
235
  // This Put outside of a transaction will conflict with a later write
236
1
  s = db->Put(write_options, "foo", "barz");
237
1
  ASSERT_OK(s);
238
239
1
  s = txn->Put("foo2", "X");
240
1
  ASSERT_OK(s);
241
242
1
  s = txn->Put("foo",
243
1
               "bar2");  // Conflicts with write done after snapshot taken
244
1
  ASSERT_TRUE(s.IsBusy());
245
246
1
  s = txn->Put("foo3", "Y");
247
1
  ASSERT_OK(s);
248
249
1
  s = db->Get(read_options, "foo", &value);
250
1
  ASSERT_EQ(value, "barz");
251
252
1
  ASSERT_EQ(2, txn->GetNumKeys());
253
254
1
  s = txn->Commit();
255
1
  ASSERT_OK(s);  // Txn should commit, but only write foo2 and foo3
256
257
  // Verify that transaction wrote foo2 and foo3 but not foo
258
1
  ASSERT_OK(db->Get(read_options, "foo", &value));
259
1
  ASSERT_EQ(value, "barz");
260
261
1
  ASSERT_OK(db->Get(read_options, "foo2", &value));
262
1
  ASSERT_EQ(value, "X");
263
264
1
  ASSERT_OK(db->Get(read_options, "foo3", &value));
265
1
  ASSERT_EQ(value, "Y");
266
267
1
  delete txn;
268
1
}
269
270
1
TEST_F(TransactionTest, ReadConflictTest) {
271
1
  WriteOptions write_options;
272
1
  ReadOptions read_options, snapshot_read_options;
273
1
  TransactionOptions txn_options;
274
1
  string value;
275
1
  Status s;
276
277
1
  ASSERT_OK(db->Put(write_options, "foo", "bar"));
278
1
  ASSERT_OK(db->Put(write_options, "foo2", "bar"));
279
280
1
  txn_options.set_snapshot = true;
281
1
  Transaction* txn = db->BeginTransaction(write_options, txn_options);
282
1
  ASSERT_TRUE(txn);
283
284
1
  txn->SetSnapshot();
285
1
  snapshot_read_options.snapshot = txn->GetSnapshot();
286
287
1
  ASSERT_OK(txn->GetForUpdate(snapshot_read_options, "foo", &value));
288
1
  ASSERT_EQ(value, "bar");
289
290
  // This Put outside of a transaction will conflict with the previous read
291
1
  s = db->Put(write_options, "foo", "barz");
292
1
  ASSERT_TRUE(s.IsTimedOut());
293
294
1
  s = db->Get(read_options, "foo", &value);
295
1
  ASSERT_EQ(value, "bar");
296
297
1
  s = txn->Get(read_options, "foo", &value);
298
1
  ASSERT_EQ(value, "bar");
299
300
1
  s = txn->Commit();
301
1
  ASSERT_OK(s);
302
303
1
  delete txn;
304
1
}
305
306
1
TEST_F(TransactionTest, TxnOnlyTest) {
307
  // Test to make sure transactions work when there are no other writes in an
308
  // empty db.
309
310
1
  WriteOptions write_options;
311
1
  ReadOptions read_options;
312
1
  string value;
313
1
  Status s;
314
315
1
  Transaction* txn = db->BeginTransaction(write_options);
316
1
  ASSERT_TRUE(txn);
317
318
1
  s = txn->Put("x", "y");
319
1
  ASSERT_OK(s);
320
321
1
  s = txn->Commit();
322
1
  ASSERT_OK(s);
323
324
1
  delete txn;
325
1
}
326
327
1
TEST_F(TransactionTest, FlushTest) {
328
1
  WriteOptions write_options;
329
1
  ReadOptions read_options, snapshot_read_options;
330
1
  string value;
331
1
  Status s;
332
333
1
  ASSERT_OK(db->Put(write_options, Slice("foo"), Slice("bar")));
334
1
  ASSERT_OK(db->Put(write_options, Slice("foo2"), Slice("bar")));
335
336
1
  Transaction* txn = db->BeginTransaction(write_options);
337
1
  ASSERT_TRUE(txn);
338
339
1
  snapshot_read_options.snapshot = txn->GetSnapshot();
340
341
1
  ASSERT_OK(txn->GetForUpdate(snapshot_read_options, "foo", &value));
342
1
  ASSERT_EQ(value, "bar");
343
344
1
  s = txn->Put(Slice("foo"), Slice("bar2"));
345
1
  ASSERT_OK(s);
346
347
1
  ASSERT_OK(txn->GetForUpdate(snapshot_read_options, "foo", &value));
348
1
  ASSERT_EQ(value, "bar2");
349
350
  // Put a random key so we have a memtable to flush
351
1
  s = db->Put(write_options, "dummy", "dummy");
352
1
  ASSERT_OK(s);
353
354
  // force a memtable flush
355
1
  FlushOptions flush_ops;
356
1
  ASSERT_OK(db->Flush(flush_ops));
357
358
1
  s = txn->Commit();
359
  // txn should commit since the flushed table is still in MemtableList History
360
1
  ASSERT_OK(s);
361
362
1
  ASSERT_OK(db->Get(read_options, "foo", &value));
363
1
  ASSERT_EQ(value, "bar2");
364
365
1
  delete txn;
366
1
}
367
368
1
TEST_F(TransactionTest, FlushTest2) {
369
1
  const size_t num_tests = 3;
370
371
4
  for (size_t n = 0; n < num_tests; n++) {
372
    // Test different table factories
373
3
    switch (n) {
374
1
      case 0:
375
1
        break;
376
1
      case 1:
377
1
        options.table_factory.reset(new mock::MockTableFactory());
378
1
        break;
379
1
      case 2: {
380
1
        PlainTableOptions pt_opts;
381
1
        pt_opts.hash_table_ratio = 0;
382
1
        options.table_factory.reset(NewPlainTableFactory(pt_opts));
383
1
        break;
384
3
      }
385
3
    }
386
387
3
    Status s = ReOpen();
388
3
    ASSERT_OK(s);
389
390
3
    WriteOptions write_options;
391
3
    ReadOptions read_options, snapshot_read_options;
392
3
    TransactionOptions txn_options;
393
3
    string value;
394
395
3
    DBImpl* db_impl = reinterpret_cast<DBImpl*>(db->GetBaseDB());
396
397
3
    ASSERT_OK(db->Put(write_options, Slice("foo"), Slice("bar")));
398
3
    ASSERT_OK(db->Put(write_options, Slice("foo2"), Slice("bar2")));
399
3
    ASSERT_OK(db->Put(write_options, Slice("foo3"), Slice("bar3")));
400
401
3
    txn_options.set_snapshot = true;
402
3
    Transaction* txn = db->BeginTransaction(write_options, txn_options);
403
3
    ASSERT_TRUE(txn);
404
405
3
    snapshot_read_options.snapshot = txn->GetSnapshot();
406
407
3
    ASSERT_OK(txn->GetForUpdate(snapshot_read_options, "foo", &value));
408
3
    ASSERT_EQ(value, "bar");
409
410
3
    s = txn->Put(Slice("foo"), Slice("bar2"));
411
3
    ASSERT_OK(s);
412
413
3
    ASSERT_OK(txn->GetForUpdate(snapshot_read_options, "foo", &value));
414
3
    ASSERT_EQ(value, "bar2");
415
    // verify foo is locked by txn
416
3
    s = db->Delete(write_options, "foo");
417
3
    ASSERT_TRUE(s.IsTimedOut());
418
419
3
    s = db->Put(write_options, "Z", "z");
420
3
    ASSERT_OK(s);
421
3
    s = db->Put(write_options, "dummy", "dummy");
422
3
    ASSERT_OK(s);
423
424
3
    s = db->Put(write_options, "S", "s");
425
3
    ASSERT_OK(s);
426
3
    s = db->SingleDelete(write_options, "S");
427
3
    ASSERT_OK(s);
428
429
3
    s = txn->Delete("S");
430
    // Should fail after encountering a write to S in memtable
431
3
    ASSERT_TRUE(s.IsBusy());
432
433
    // force a memtable flush
434
3
    s = db_impl->TEST_FlushMemTable(true);
435
3
    ASSERT_OK(s);
436
437
    // Put a random key so we have a MemTable to flush
438
3
    s = db->Put(write_options, "dummy", "dummy2");
439
3
    ASSERT_OK(s);
440
441
    // force a memtable flush
442
3
    ASSERT_OK(db_impl->TEST_FlushMemTable(true));
443
444
3
    s = db->Put(write_options, "dummy", "dummy3");
445
3
    ASSERT_OK(s);
446
447
    // force a memtable flush
448
    // Since our test db has max_write_buffer_number=2, this flush will cause
449
    // the first memtable to get purged from the MemtableList history.
450
3
    ASSERT_OK(db_impl->TEST_FlushMemTable(true));
451
452
3
    s = txn->Put("X", "Y");
453
    // Should succeed after verifying there is no write to X in SST file
454
3
    ASSERT_OK(s);
455
456
3
    s = txn->Put("Z", "zz");
457
    // Should fail after encountering a write to Z in SST file
458
3
    ASSERT_TRUE(s.IsBusy());
459
460
3
    s = txn->GetForUpdate(read_options, "foo2", &value);
461
    // should succeed since key was written before txn started
462
3
    ASSERT_OK(s);
463
    // verify foo2 is locked by txn
464
3
    s = db->Delete(write_options, "foo2");
465
3
    ASSERT_TRUE(s.IsTimedOut());
466
467
3
    s = txn->Delete("S");
468
    // Should fail after encountering a write to S in SST file
469
3
    ASSERT_TRUE(s.IsBusy());
470
471
    // Write a bunch of keys to db to force a compaction
472
3
    Random rnd(47);
473
3.00k
    for (int i = 0; i < 1000; i++) {
474
3.00k
      s = db->Put(write_options, std::to_string(i),
475
3.00k
                  CompressibleString(&rnd, 0.8, 100, &value));
476
3.00k
      ASSERT_OK(s);
477
3.00k
    }
478
479
3
    s = txn->Put("X", "yy");
480
    // Should succeed after verifying there is no write to X in SST file
481
3
    ASSERT_OK(s);
482
483
3
    s = txn->Put("Z", "zzz");
484
    // Should fail after encountering a write to Z in SST file
485
3
    ASSERT_TRUE(s.IsBusy());
486
487
3
    s = txn->Delete("S");
488
    // Should fail after encountering a write to S in SST file
489
3
    ASSERT_TRUE(s.IsBusy());
490
491
3
    s = txn->GetForUpdate(read_options, "foo3", &value);
492
    // should succeed since key was written before txn started
493
3
    ASSERT_OK(s);
494
    // verify foo3 is locked by txn
495
3
    s = db->Delete(write_options, "foo3");
496
3
    ASSERT_TRUE(s.IsTimedOut());
497
498
3
    ASSERT_OK(db_impl->TEST_WaitForCompact());
499
500
3
    s = txn->Commit();
501
3
    ASSERT_OK(s);
502
503
    // Transaction should only write the keys that succeeded.
504
3
    s = db->Get(read_options, "foo", &value);
505
3
    ASSERT_EQ(value, "bar2");
506
507
3
    s = db->Get(read_options, "X", &value);
508
3
    ASSERT_OK(s);
509
3
    ASSERT_EQ("yy", value);
510
511
3
    s = db->Get(read_options, "Z", &value);
512
3
    ASSERT_OK(s);
513
3
    ASSERT_EQ("z", value);
514
515
3
  delete txn;
516
3
  }
517
1
}
518
519
1
TEST_F(TransactionTest, NoSnapshotTest) {
520
1
  WriteOptions write_options;
521
1
  ReadOptions read_options;
522
1
  string value;
523
1
  Status s;
524
525
1
  ASSERT_OK(db->Put(write_options, "AAA", "bar"));
526
527
1
  Transaction* txn = db->BeginTransaction(write_options);
528
1
  ASSERT_TRUE(txn);
529
530
  // Modify key after transaction start
531
1
  ASSERT_OK(db->Put(write_options, "AAA", "bar1"));
532
533
  // Read and write without a snapshot
534
1
  ASSERT_OK(txn->GetForUpdate(read_options, "AAA", &value));
535
1
  ASSERT_EQ(value, "bar1");
536
1
  s = txn->Put("AAA", "bar2");
537
1
  ASSERT_OK(s);
538
539
  // Should commit since read/write was done after data changed
540
1
  s = txn->Commit();
541
1
  ASSERT_OK(s);
542
543
1
  ASSERT_OK(txn->GetForUpdate(read_options, "AAA", &value));
544
1
  ASSERT_EQ(value, "bar2");
545
546
1
  delete txn;
547
1
}
548
549
1
TEST_F(TransactionTest, MultipleSnapshotTest) {
550
1
  WriteOptions write_options;
551
1
  ReadOptions read_options, snapshot_read_options;
552
1
  string value;
553
1
  Status s;
554
555
1
  ASSERT_OK(db->Put(write_options, "AAA", "bar"));
556
1
  ASSERT_OK(db->Put(write_options, "BBB", "bar"));
557
1
  ASSERT_OK(db->Put(write_options, "CCC", "bar"));
558
559
1
  Transaction* txn = db->BeginTransaction(write_options);
560
1
  ASSERT_TRUE(txn);
561
562
1
  ASSERT_OK(db->Put(write_options, "AAA", "bar1"));
563
564
  // Read and write without a snapshot
565
1
  ASSERT_OK(txn->GetForUpdate(read_options, "AAA", &value));
566
1
  ASSERT_EQ(value, "bar1");
567
1
  s = txn->Put("AAA", "bar2");
568
1
  ASSERT_OK(s);
569
570
  // Modify BBB before snapshot is taken
571
1
  ASSERT_OK(db->Put(write_options, "BBB", "bar1"));
572
573
1
  txn->SetSnapshot();
574
1
  snapshot_read_options.snapshot = txn->GetSnapshot();
575
576
  // Read and write with snapshot
577
1
  ASSERT_OK(txn->GetForUpdate(snapshot_read_options, "BBB", &value));
578
1
  ASSERT_EQ(value, "bar1");
579
1
  s = txn->Put("BBB", "bar2");
580
1
  ASSERT_OK(s);
581
582
1
  ASSERT_OK(db->Put(write_options, "CCC", "bar1"));
583
584
  // Set a new snapshot
585
1
  txn->SetSnapshot();
586
1
  snapshot_read_options.snapshot = txn->GetSnapshot();
587
588
  // Read and write with snapshot
589
1
  ASSERT_OK(txn->GetForUpdate(snapshot_read_options, "CCC", &value));
590
1
  ASSERT_EQ(value, "bar1");
591
1
  s = txn->Put("CCC", "bar2");
592
1
  ASSERT_OK(s);
593
594
1
  s = txn->GetForUpdate(read_options, "AAA", &value);
595
1
  ASSERT_OK(s);
596
1
  ASSERT_EQ(value, "bar2");
597
1
  s = txn->GetForUpdate(read_options, "BBB", &value);
598
1
  ASSERT_OK(s);
599
1
  ASSERT_EQ(value, "bar2");
600
1
  s = txn->GetForUpdate(read_options, "CCC", &value);
601
1
  ASSERT_OK(s);
602
1
  ASSERT_EQ(value, "bar2");
603
604
1
  s = db->Get(read_options, "AAA", &value);
605
1
  ASSERT_OK(s);
606
1
  ASSERT_EQ(value, "bar1");
607
1
  s = db->Get(read_options, "BBB", &value);
608
1
  ASSERT_OK(s);
609
1
  ASSERT_EQ(value, "bar1");
610
1
  s = db->Get(read_options, "CCC", &value);
611
1
  ASSERT_OK(s);
612
1
  ASSERT_EQ(value, "bar1");
613
614
1
  s = txn->Commit();
615
1
  ASSERT_OK(s);
616
617
1
  s = db->Get(read_options, "AAA", &value);
618
1
  ASSERT_OK(s);
619
1
  ASSERT_EQ(value, "bar2");
620
1
  s = db->Get(read_options, "BBB", &value);
621
1
  ASSERT_OK(s);
622
1
  ASSERT_EQ(value, "bar2");
623
1
  s = db->Get(read_options, "CCC", &value);
624
1
  ASSERT_OK(s);
625
1
  ASSERT_EQ(value, "bar2");
626
627
  // verify that we track multiple writes to the same key at different snapshots
628
1
  delete txn;
629
1
  txn = db->BeginTransaction(write_options);
630
631
  // Potentially conflicting writes
632
1
  ASSERT_OK(db->Put(write_options, "ZZZ", "zzz"));
633
1
  ASSERT_OK(db->Put(write_options, "XXX", "xxx"));
634
635
1
  txn->SetSnapshot();
636
637
1
  TransactionOptions txn_options;
638
1
  txn_options.set_snapshot = true;
639
1
  Transaction* txn2 = db->BeginTransaction(write_options, txn_options);
640
1
  txn2->SetSnapshot();
641
642
  // This should not conflict in txn since the snapshot is later than the
643
  // previous write (spoiler alert:  it will later conflict with txn2).
644
1
  s = txn->Put("ZZZ", "zzzz");
645
1
  ASSERT_OK(s);
646
647
1
  s = txn->Commit();
648
1
  ASSERT_OK(s);
649
650
1
  delete txn;
651
652
  // This will conflict since the snapshot is earlier than another write to ZZZ
653
1
  s = txn2->Put("ZZZ", "xxxxx");
654
1
  ASSERT_TRUE(s.IsBusy());
655
656
1
  s = txn2->Commit();
657
1
  ASSERT_OK(s);
658
659
1
  s = db->Get(read_options, "ZZZ", &value);
660
1
  ASSERT_OK(s);
661
1
  ASSERT_EQ(value, "zzzz");
662
663
1
  delete txn2;
664
1
}
665
666
1
TEST_F(TransactionTest, ColumnFamiliesTest) {
667
1
  WriteOptions write_options;
668
1
  ReadOptions read_options, snapshot_read_options;
669
1
  TransactionOptions txn_options;
670
1
  string value;
671
1
  Status s;
672
673
1
  ColumnFamilyHandle *cfa, *cfb;
674
1
  ColumnFamilyOptions cf_options;
675
676
  // Create 2 new column families
677
1
  s = db->CreateColumnFamily(cf_options, "CFA", &cfa);
678
1
  ASSERT_OK(s);
679
1
  s = db->CreateColumnFamily(cf_options, "CFB", &cfb);
680
1
  ASSERT_OK(s);
681
682
1
  delete cfa;
683
1
  delete cfb;
684
1
  delete db;
685
686
  // open DB with three column families
687
1
  std::vector<ColumnFamilyDescriptor> column_families;
688
  // have to open default column family
689
1
  column_families.push_back(
690
1
      ColumnFamilyDescriptor(kDefaultColumnFamilyName, ColumnFamilyOptions()));
691
  // open the new column families
692
1
  column_families.push_back(
693
1
      ColumnFamilyDescriptor("CFA", ColumnFamilyOptions()));
694
1
  column_families.push_back(
695
1
      ColumnFamilyDescriptor("CFB", ColumnFamilyOptions()));
696
697
1
  std::vector<ColumnFamilyHandle*> handles;
698
699
1
  s = TransactionDB::Open(options, txn_db_options, dbname, column_families,
700
1
                          &handles, &db);
701
1
  ASSERT_OK(s);
702
703
1
  Transaction* txn = db->BeginTransaction(write_options);
704
1
  ASSERT_TRUE(txn);
705
706
1
  txn->SetSnapshot();
707
1
  snapshot_read_options.snapshot = txn->GetSnapshot();
708
709
1
  txn_options.set_snapshot = true;
710
1
  Transaction* txn2 = db->BeginTransaction(write_options, txn_options);
711
1
  ASSERT_TRUE(txn2);
712
713
  // Write some data to the db
714
1
  WriteBatch batch;
715
1
  batch.Put("foo", "foo");
716
1
  batch.Put(handles[1], "AAA", "bar");
717
1
  batch.Put(handles[1], "AAAZZZ", "bar");
718
1
  s = db->Write(write_options, &batch);
719
1
  ASSERT_OK(s);
720
1
  ASSERT_OK(db->Delete(write_options, handles[1], "AAAZZZ"));
721
722
  // These keys do not conflict with existing writes since they're in
723
  // different column families
724
1
  s = txn->Delete("AAA");
725
1
  ASSERT_OK(s);
726
1
  s = txn->GetForUpdate(snapshot_read_options, handles[1], "foo", &value);
727
1
  ASSERT_TRUE(s.IsNotFound());
728
1
  Slice key_slice("AAAZZZ");
729
1
  Slice value_slices[2] = {Slice("bar"), Slice("bar")};
730
1
  s = txn->Put(handles[2], SliceParts(&key_slice, 1),
731
1
               SliceParts(value_slices, 2));
732
1
  ASSERT_OK(s);
733
1
  ASSERT_EQ(3, txn->GetNumKeys());
734
735
1
  s = txn->Commit();
736
1
  ASSERT_OK(s);
737
1
  s = db->Get(read_options, "AAA", &value);
738
1
  ASSERT_TRUE(s.IsNotFound());
739
1
  s = db->Get(read_options, handles[2], "AAAZZZ", &value);
740
1
  ASSERT_EQ(value, "barbar");
741
742
1
  Slice key_slices[3] = {Slice("AAA"), Slice("ZZ"), Slice("Z")};
743
1
  Slice value_slice("barbarbar");
744
745
1
  s = txn2->Delete(handles[2], "XXX");
746
1
  ASSERT_OK(s);
747
1
  s = txn2->Delete(handles[1], "XXX");
748
1
  ASSERT_OK(s);
749
750
  // This write will cause a conflict with the earlier batch write
751
1
  s = txn2->Put(handles[1], SliceParts(key_slices, 3),
752
1
                SliceParts(&value_slice, 1));
753
1
  ASSERT_TRUE(s.IsBusy());
754
755
1
  s = txn2->Commit();
756
1
  ASSERT_OK(s);
757
1
  s = db->Get(read_options, handles[1], "AAAZZZ", &value);
758
1
  ASSERT_EQ(value, "barbar");
759
760
1
  delete txn;
761
1
  delete txn2;
762
763
1
  txn = db->BeginTransaction(write_options, txn_options);
764
1
  snapshot_read_options.snapshot = txn->GetSnapshot();
765
766
1
  txn2 = db->BeginTransaction(write_options, txn_options);
767
1
  ASSERT_TRUE(txn);
768
769
1
  std::vector<ColumnFamilyHandle*> multiget_cfh = {handles[1], handles[2],
770
1
                                                   handles[0], handles[2]};
771
1
  std::vector<Slice> multiget_keys = {"AAA", "AAAZZZ", "foo", "foo"};
772
1
  std::vector<std::string> values(4);
773
774
1
  std::vector<Status> results = txn->MultiGetForUpdate(
775
1
      snapshot_read_options, multiget_cfh, multiget_keys, &values);
776
1
  ASSERT_OK(results[0]);
777
1
  ASSERT_OK(results[1]);
778
1
  ASSERT_OK(results[2]);
779
1
  ASSERT_TRUE(results[3].IsNotFound());
780
1
  ASSERT_EQ(values[0], "bar");
781
1
  ASSERT_EQ(values[1], "barbar");
782
1
  ASSERT_EQ(values[2], "foo");
783
784
1
  s = txn->SingleDelete(handles[2], "ZZZ");
785
1
  ASSERT_OK(s);
786
1
  s = txn->Put(handles[2], "ZZZ", "YYY");
787
1
  ASSERT_OK(s);
788
1
  s = txn->Put(handles[2], "ZZZ", "YYYY");
789
1
  ASSERT_OK(s);
790
1
  s = txn->Delete(handles[2], "ZZZ");
791
1
  ASSERT_OK(s);
792
1
  s = txn->Put(handles[2], "AAAZZZ", "barbarbar");
793
1
  ASSERT_OK(s);
794
795
1
  ASSERT_EQ(5, txn->GetNumKeys());
796
797
  // Txn should commit
798
1
  s = txn->Commit();
799
1
  ASSERT_OK(s);
800
1
  s = db->Get(read_options, handles[2], "ZZZ", &value);
801
1
  ASSERT_TRUE(s.IsNotFound());
802
803
  // Put a key which will conflict with the next txn using the previous snapshot
804
1
  ASSERT_OK(db->Put(write_options, handles[2], "foo", "000"));
805
806
1
  results = txn2->MultiGetForUpdate(snapshot_read_options, multiget_cfh,
807
1
                                    multiget_keys, &values);
808
  // All results should fail since there was a conflict
809
1
  ASSERT_TRUE(results[0].IsBusy());
810
1
  ASSERT_TRUE(results[1].IsBusy());
811
1
  ASSERT_TRUE(results[2].IsBusy());
812
1
  ASSERT_TRUE(results[3].IsBusy());
813
814
1
  s = db->Get(read_options, handles[2], "foo", &value);
815
1
  ASSERT_EQ(value, "000");
816
817
1
  s = txn2->Commit();
818
1
  ASSERT_OK(s);
819
820
1
  s = db->DropColumnFamily(handles[1]);
821
1
  ASSERT_OK(s);
822
1
  s = db->DropColumnFamily(handles[2]);
823
1
  ASSERT_OK(s);
824
825
1
  delete txn;
826
1
  delete txn2;
827
828
3
  for (auto handle : handles) {
829
3
    delete handle;
830
3
  }
831
1
}
832
833
1
TEST_F(TransactionTest, ColumnFamiliesTest2) {
834
1
  WriteOptions write_options;
835
1
  ReadOptions read_options, snapshot_read_options;
836
1
  TransactionOptions txn_options;
837
1
  string value;
838
1
  Status s;
839
840
1
  ColumnFamilyHandle *one, *two;
841
1
  ColumnFamilyOptions cf_options;
842
843
  // Create 2 new column families
844
1
  s = db->CreateColumnFamily(cf_options, "ONE", &one);
845
1
  ASSERT_OK(s);
846
1
  s = db->CreateColumnFamily(cf_options, "TWO", &two);
847
1
  ASSERT_OK(s);
848
849
1
  Transaction* txn1 = db->BeginTransaction(write_options);
850
1
  ASSERT_TRUE(txn1);
851
1
  Transaction* txn2 = db->BeginTransaction(write_options);
852
1
  ASSERT_TRUE(txn2);
853
854
1
  s = txn1->Put(one, "X", "1");
855
1
  ASSERT_OK(s);
856
1
  s = txn1->Put(two, "X", "2");
857
1
  ASSERT_OK(s);
858
1
  s = txn1->Put("X", "0");
859
1
  ASSERT_OK(s);
860
861
1
  s = txn2->Put(one, "X", "11");
862
1
  ASSERT_TRUE(s.IsTimedOut());
863
864
1
  s = txn1->Commit();
865
1
  ASSERT_OK(s);
866
867
  // Drop first column family
868
1
  s = db->DropColumnFamily(one);
869
1
  ASSERT_OK(s);
870
871
  // Should fail since column family was dropped.
872
1
  s = txn2->Commit();
873
1
  ASSERT_OK(s);
874
875
1
  delete txn1;
876
1
  txn1 = db->BeginTransaction(write_options);
877
1
  ASSERT_TRUE(txn1);
878
879
  // Should fail since column family was dropped
880
1
  s = txn1->Put(one, "X", "111");
881
1
  ASSERT_TRUE(s.IsInvalidArgument());
882
883
1
  s = txn1->Put(two, "X", "222");
884
1
  ASSERT_OK(s);
885
886
1
  s = txn1->Put("X", "000");
887
1
  ASSERT_OK(s);
888
889
1
  s = txn1->Commit();
890
1
  ASSERT_OK(s);
891
892
1
  s = db->Get(read_options, two, "X", &value);
893
1
  ASSERT_OK(s);
894
1
  ASSERT_EQ("222", value);
895
896
1
  s = db->Get(read_options, "X", &value);
897
1
  ASSERT_OK(s);
898
1
  ASSERT_EQ("000", value);
899
900
1
  s = db->DropColumnFamily(two);
901
1
  ASSERT_OK(s);
902
903
1
  delete txn1;
904
1
  delete txn2;
905
906
1
  delete one;
907
1
  delete two;
908
1
}
909
910
1
TEST_F(TransactionTest, EmptyTest) {
911
1
  WriteOptions write_options;
912
1
  ReadOptions read_options;
913
1
  string value;
914
1
  Status s;
915
916
1
  s = db->Put(write_options, "aaa", "aaa");
917
1
  ASSERT_OK(s);
918
919
1
  Transaction* txn = db->BeginTransaction(write_options);
920
1
  s = txn->Commit();
921
1
  ASSERT_OK(s);
922
1
  delete txn;
923
924
1
  txn = db->BeginTransaction(write_options);
925
1
  txn->Rollback();
926
1
  delete txn;
927
928
1
  txn = db->BeginTransaction(write_options);
929
1
  s = txn->GetForUpdate(read_options, "aaa", &value);
930
1
  ASSERT_EQ(value, "aaa");
931
932
1
  s = txn->Commit();
933
1
  ASSERT_OK(s);
934
1
  delete txn;
935
936
1
  txn = db->BeginTransaction(write_options);
937
1
  txn->SetSnapshot();
938
939
1
  s = txn->GetForUpdate(read_options, "aaa", &value);
940
1
  ASSERT_EQ(value, "aaa");
941
942
  // Conflicts with previous GetForUpdate
943
1
  s = db->Put(write_options, "aaa", "xxx");
944
1
  ASSERT_TRUE(s.IsTimedOut());
945
946
  // transaction expired!
947
1
  s = txn->Commit();
948
1
  ASSERT_OK(s);
949
1
  delete txn;
950
1
}
951
952
1
TEST_F(TransactionTest, PredicateManyPreceders) {
953
1
  WriteOptions write_options;
954
1
  ReadOptions read_options1, read_options2;
955
1
  TransactionOptions txn_options;
956
1
  string value;
957
1
  Status s;
958
959
1
  txn_options.set_snapshot = true;
960
1
  Transaction* txn1 = db->BeginTransaction(write_options, txn_options);
961
1
  read_options1.snapshot = txn1->GetSnapshot();
962
963
1
  Transaction* txn2 = db->BeginTransaction(write_options);
964
1
  txn2->SetSnapshot();
965
1
  read_options2.snapshot = txn2->GetSnapshot();
966
967
1
  std::vector<Slice> multiget_keys = {"1", "2", "3"};
968
1
  std::vector<std::string> multiget_values;
969
970
1
  std::vector<Status> results =
971
1
      txn1->MultiGetForUpdate(read_options1, multiget_keys, &multiget_values);
972
1
  ASSERT_TRUE(results[1].IsNotFound());
973
974
1
  s = txn2->Put("2", "x");  // Conflict's with txn1's MultiGetForUpdate
975
1
  ASSERT_TRUE(s.IsTimedOut());
976
977
1
  txn2->Rollback();
978
979
1
  multiget_values.clear();
980
1
  results =
981
1
      txn1->MultiGetForUpdate(read_options1, multiget_keys, &multiget_values);
982
1
  ASSERT_TRUE(results[1].IsNotFound());
983
984
1
  s = txn1->Commit();
985
1
  ASSERT_OK(s);
986
987
1
  delete txn1;
988
1
  delete txn2;
989
990
1
  txn1 = db->BeginTransaction(write_options, txn_options);
991
1
  read_options1.snapshot = txn1->GetSnapshot();
992
993
1
  txn2 = db->BeginTransaction(write_options, txn_options);
994
1
  read_options2.snapshot = txn2->GetSnapshot();
995
996
1
  s = txn1->Put("4", "x");
997
1
  ASSERT_OK(s);
998
999
1
  s = txn2->Delete("4");  // conflict
1000
1
  ASSERT_TRUE(s.IsTimedOut());
1001
1002
1
  s = txn1->Commit();
1003
1
  ASSERT_OK(s);
1004
1005
1
  s = txn2->GetForUpdate(read_options2, "4", &value);
1006
1
  ASSERT_TRUE(s.IsBusy());
1007
1008
1
  txn2->Rollback();
1009
1010
1
  delete txn1;
1011
1
  delete txn2;
1012
1
}
1013
1014
1
TEST_F(TransactionTest, LostUpdate) {
1015
1
  WriteOptions write_options;
1016
1
  ReadOptions read_options, read_options1, read_options2;
1017
1
  TransactionOptions txn_options;
1018
1
  string value;
1019
1
  Status s;
1020
1021
  // Test 2 transactions writing to the same key in multiple orders and
1022
  // with/without snapshots
1023
1024
1
  Transaction* txn1 = db->BeginTransaction(write_options);
1025
1
  Transaction* txn2 = db->BeginTransaction(write_options);
1026
1027
1
  s = txn1->Put("1", "1");
1028
1
  ASSERT_OK(s);
1029
1030
1
  s = txn2->Put("1", "2");  // conflict
1031
1
  ASSERT_TRUE(s.IsTimedOut());
1032
1033
1
  s = txn2->Commit();
1034
1
  ASSERT_OK(s);
1035
1036
1
  s = txn1->Commit();
1037
1
  ASSERT_OK(s);
1038
1039
1
  s = db->Get(read_options, "1", &value);
1040
1
  ASSERT_OK(s);
1041
1
  ASSERT_EQ("1", value);
1042
1043
1
  delete txn1;
1044
1
  delete txn2;
1045
1046
1
  txn_options.set_snapshot = true;
1047
1
  txn1 = db->BeginTransaction(write_options, txn_options);
1048
1
  read_options1.snapshot = txn1->GetSnapshot();
1049
1050
1
  txn2 = db->BeginTransaction(write_options, txn_options);
1051
1
  read_options2.snapshot = txn2->GetSnapshot();
1052
1053
1
  s = txn1->Put("1", "3");
1054
1
  ASSERT_OK(s);
1055
1
  s = txn2->Put("1", "4");  // conflict
1056
1
  ASSERT_TRUE(s.IsTimedOut());
1057
1058
1
  s = txn1->Commit();
1059
1
  ASSERT_OK(s);
1060
1061
1
  s = txn2->Commit();
1062
1
  ASSERT_OK(s);
1063
1064
1
  s = db->Get(read_options, "1", &value);
1065
1
  ASSERT_OK(s);
1066
1
  ASSERT_EQ("3", value);
1067
1068
1
  delete txn1;
1069
1
  delete txn2;
1070
1071
1
  txn1 = db->BeginTransaction(write_options, txn_options);
1072
1
  read_options1.snapshot = txn1->GetSnapshot();
1073
1074
1
  txn2 = db->BeginTransaction(write_options, txn_options);
1075
1
  read_options2.snapshot = txn2->GetSnapshot();
1076
1077
1
  s = txn1->Put("1", "5");
1078
1
  ASSERT_OK(s);
1079
1080
1
  s = txn1->Commit();
1081
1
  ASSERT_OK(s);
1082
1083
1
  s = txn2->Put("1", "6");
1084
1
  ASSERT_TRUE(s.IsBusy());
1085
1
  s = txn2->Commit();
1086
1
  ASSERT_OK(s);
1087
1088
1
  s = db->Get(read_options, "1", &value);
1089
1
  ASSERT_OK(s);
1090
1
  ASSERT_EQ("5", value);
1091
1092
1
  delete txn1;
1093
1
  delete txn2;
1094
1095
1
  txn1 = db->BeginTransaction(write_options, txn_options);
1096
1
  read_options1.snapshot = txn1->GetSnapshot();
1097
1098
1
  txn2 = db->BeginTransaction(write_options, txn_options);
1099
1
  read_options2.snapshot = txn2->GetSnapshot();
1100
1101
1
  s = txn1->Put("1", "7");
1102
1
  ASSERT_OK(s);
1103
1
  s = txn1->Commit();
1104
1
  ASSERT_OK(s);
1105
1106
1
  txn2->SetSnapshot();
1107
1
  s = txn2->Put("1", "8");
1108
1
  ASSERT_OK(s);
1109
1
  s = txn2->Commit();
1110
1
  ASSERT_OK(s);
1111
1112
1
  s = db->Get(read_options, "1", &value);
1113
1
  ASSERT_OK(s);
1114
1
  ASSERT_EQ("8", value);
1115
1116
1
  delete txn1;
1117
1
  delete txn2;
1118
1119
1
  txn1 = db->BeginTransaction(write_options);
1120
1
  txn2 = db->BeginTransaction(write_options);
1121
1122
1
  s = txn1->Put("1", "9");
1123
1
  ASSERT_OK(s);
1124
1
  s = txn1->Commit();
1125
1
  ASSERT_OK(s);
1126
1127
1
  s = txn2->Put("1", "10");
1128
1
  ASSERT_OK(s);
1129
1
  s = txn2->Commit();
1130
1
  ASSERT_OK(s);
1131
1132
1
  delete txn1;
1133
1
  delete txn2;
1134
1135
1
  s = db->Get(read_options, "1", &value);
1136
1
  ASSERT_OK(s);
1137
1
  ASSERT_EQ(value, "10");
1138
1
}
1139
1140
1
TEST_F(TransactionTest, UntrackedWrites) {
1141
1
  WriteOptions write_options;
1142
1
  ReadOptions read_options;
1143
1
  string value;
1144
1
  Status s;
1145
1146
  // Verify transaction rollback works for untracked keys.
1147
1
  Transaction* txn = db->BeginTransaction(write_options);
1148
1
  txn->SetSnapshot();
1149
1150
1
  s = txn->PutUntracked("untracked", "0");
1151
1
  ASSERT_OK(s);
1152
1
  txn->Rollback();
1153
1
  s = db->Get(read_options, "untracked", &value);
1154
1
  ASSERT_TRUE(s.IsNotFound());
1155
1156
1
  delete txn;
1157
1
  txn = db->BeginTransaction(write_options);
1158
1
  txn->SetSnapshot();
1159
1160
1
  s = db->Put(write_options, "untracked", "x");
1161
1
  ASSERT_OK(s);
1162
1163
  // Untracked writes should succeed even though key was written after snapshot
1164
1
  s = txn->PutUntracked("untracked", "1");
1165
1
  ASSERT_OK(s);
1166
1
  s = txn->MergeUntracked("untracked", "2");
1167
1
  ASSERT_OK(s);
1168
1
  s = txn->DeleteUntracked("untracked");
1169
1
  ASSERT_OK(s);
1170
1171
  // Conflict
1172
1
  s = txn->Put("untracked", "3");
1173
1
  ASSERT_TRUE(s.IsBusy());
1174
1175
1
  s = txn->Commit();
1176
1
  ASSERT_OK(s);
1177
1178
1
  s = db->Get(read_options, "untracked", &value);
1179
1
  ASSERT_TRUE(s.IsNotFound());
1180
1181
1
  delete txn;
1182
1
}
1183
1184
1
TEST_F(TransactionTest, ExpiredTransaction) {
1185
1
  WriteOptions write_options;
1186
1
  ReadOptions read_options;
1187
1
  TransactionOptions txn_options;
1188
1
  string value;
1189
1
  Status s;
1190
1191
  // Set txn expiration timeout to 0 microseconds (expires instantly)
1192
1
  txn_options.expiration = 0;
1193
1
  Transaction* txn1 = db->BeginTransaction(write_options, txn_options);
1194
1195
1
  s = txn1->Put("X", "1");
1196
1
  ASSERT_OK(s);
1197
1198
1
  s = txn1->Put("Y", "1");
1199
1
  ASSERT_OK(s);
1200
1201
1
  Transaction* txn2 = db->BeginTransaction(write_options);
1202
1203
  // txn2 should be able to write to X since txn1 has expired
1204
1
  s = txn2->Put("X", "2");
1205
1
  ASSERT_OK(s);
1206
1207
1
  s = txn2->Commit();
1208
1
  ASSERT_OK(s);
1209
1
  s = db->Get(read_options, "X", &value);
1210
1
  ASSERT_OK(s);
1211
1
  ASSERT_EQ("2", value);
1212
1213
1
  s = txn1->Put("Z", "1");
1214
1
  ASSERT_OK(s);
1215
1216
  // txn1 should fail to commit since it is expired
1217
1
  s = txn1->Commit();
1218
1
  ASSERT_TRUE(s.IsExpired());
1219
1220
1
  s = db->Get(read_options, "Y", &value);
1221
1
  ASSERT_TRUE(s.IsNotFound());
1222
1223
1
  s = db->Get(read_options, "Z", &value);
1224
1
  ASSERT_TRUE(s.IsNotFound());
1225
1226
1
  delete txn1;
1227
1
  delete txn2;
1228
1
}
1229
1230
1
TEST_F(TransactionTest, ReinitializeTest) {
1231
1
  WriteOptions write_options;
1232
1
  ReadOptions read_options;
1233
1
  TransactionOptions txn_options;
1234
1
  string value;
1235
1
  Status s;
1236
1237
  // Set txn expiration timeout to 0 microseconds (expires instantly)
1238
1
  txn_options.expiration = 0;
1239
1
  Transaction* txn1 = db->BeginTransaction(write_options, txn_options);
1240
1241
  // Reinitialize transaction to no long expire
1242
1
  txn_options.expiration = -1;
1243
1
  txn1 = db->BeginTransaction(write_options, txn_options, txn1);
1244
1245
1
  s = txn1->Put("Z", "z");
1246
1
  ASSERT_OK(s);
1247
1248
  // Should commit since not expired
1249
1
  s = txn1->Commit();
1250
1
  ASSERT_OK(s);
1251
1252
1
  txn1 = db->BeginTransaction(write_options, txn_options, txn1);
1253
1254
1
  s = txn1->Put("Z", "zz");
1255
1
  ASSERT_OK(s);
1256
1257
  // Reinitilize txn1 and verify that Z gets unlocked
1258
1
  txn1 = db->BeginTransaction(write_options, txn_options, txn1);
1259
1260
1
  Transaction* txn2 = db->BeginTransaction(write_options, txn_options, nullptr);
1261
1
  s = txn2->Put("Z", "zzz");
1262
1
  ASSERT_OK(s);
1263
1
  s = txn2->Commit();
1264
1
  ASSERT_OK(s);
1265
1
  delete txn2;
1266
1267
1
  s = db->Get(read_options, "Z", &value);
1268
1
  ASSERT_OK(s);
1269
1
  ASSERT_EQ(value, "zzz");
1270
1271
  // Verify snapshots get reinitialized correctly
1272
1
  txn1->SetSnapshot();
1273
1
  s = txn1->Put("Z", "zzzz");
1274
1
  ASSERT_OK(s);
1275
1276
1
  s = txn1->Commit();
1277
1
  ASSERT_OK(s);
1278
1279
1
  s = db->Get(read_options, "Z", &value);
1280
1
  ASSERT_OK(s);
1281
1
  ASSERT_EQ(value, "zzzz");
1282
1283
1
  txn1 = db->BeginTransaction(write_options, txn_options, txn1);
1284
1
  const Snapshot* snapshot = txn1->GetSnapshot();
1285
1
  ASSERT_FALSE(snapshot);
1286
1287
1
  txn_options.set_snapshot = true;
1288
1
  txn1 = db->BeginTransaction(write_options, txn_options, txn1);
1289
1
  snapshot = txn1->GetSnapshot();
1290
1
  ASSERT_TRUE(snapshot);
1291
1292
1
  s = txn1->Put("Z", "a");
1293
1
  ASSERT_OK(s);
1294
1295
1
  txn1->Rollback();
1296
1297
1
  s = txn1->Put("Y", "y");
1298
1
  ASSERT_OK(s);
1299
1300
1
  txn_options.set_snapshot = false;
1301
1
  txn1 = db->BeginTransaction(write_options, txn_options, txn1);
1302
1
  snapshot = txn1->GetSnapshot();
1303
1
  ASSERT_FALSE(snapshot);
1304
1305
1
  s = txn1->Put("X", "x");
1306
1
  ASSERT_OK(s);
1307
1308
1
  s = txn1->Commit();
1309
1
  ASSERT_OK(s);
1310
1311
1
  s = db->Get(read_options, "Z", &value);
1312
1
  ASSERT_OK(s);
1313
1
  ASSERT_EQ(value, "zzzz");
1314
1315
1
  s = db->Get(read_options, "Y", &value);
1316
1
  ASSERT_TRUE(s.IsNotFound());
1317
1318
1
  delete txn1;
1319
1
}
1320
1321
1
TEST_F(TransactionTest, Rollback) {
1322
1
  WriteOptions write_options;
1323
1
  ReadOptions read_options;
1324
1
  TransactionOptions txn_options;
1325
1
  string value;
1326
1
  Status s;
1327
1328
1
  Transaction* txn1 = db->BeginTransaction(write_options, txn_options);
1329
1330
1
  ASSERT_OK(s);
1331
1332
1
  s = txn1->Put("X", "1");
1333
1
  ASSERT_OK(s);
1334
1335
1
  Transaction* txn2 = db->BeginTransaction(write_options);
1336
1337
  // txn2 should not be able to write to X since txn1 has it locked
1338
1
  s = txn2->Put("X", "2");
1339
1
  ASSERT_TRUE(s.IsTimedOut());
1340
1341
1
  txn1->Rollback();
1342
1
  delete txn1;
1343
1344
  // txn2 should now be able to write to X
1345
1
  s = txn2->Put("X", "3");
1346
1
  ASSERT_OK(s);
1347
1348
1
  s = txn2->Commit();
1349
1
  ASSERT_OK(s);
1350
1351
1
  s = db->Get(read_options, "X", &value);
1352
1
  ASSERT_OK(s);
1353
1
  ASSERT_EQ("3", value);
1354
1355
1
  delete txn2;
1356
1
}
1357
1358
1
TEST_F(TransactionTest, LockLimitTest) {
1359
1
  WriteOptions write_options;
1360
1
  ReadOptions read_options, snapshot_read_options;
1361
1
  TransactionOptions txn_options;
1362
1
  string value;
1363
1
  Status s;
1364
1365
1
  delete db;
1366
1367
  // Open DB with a lock limit of 3
1368
1
  txn_db_options.max_num_locks = 3;
1369
1
  s = TransactionDB::Open(options, txn_db_options, dbname, &db);
1370
1
  ASSERT_OK(s);
1371
1372
  // Create a txn and verify we can only lock up to 3 keys
1373
1
  Transaction* txn = db->BeginTransaction(write_options);
1374
1
  ASSERT_TRUE(txn);
1375
1376
1
  s = txn->Put("X", "x");
1377
1
  ASSERT_OK(s);
1378
1379
1
  s = txn->Put("Y", "y");
1380
1
  ASSERT_OK(s);
1381
1382
1
  s = txn->Put("Z", "z");
1383
1
  ASSERT_OK(s);
1384
1385
  // lock limit reached
1386
1
  s = txn->Put("W", "w");
1387
1
  ASSERT_TRUE(s.IsBusy());
1388
1389
  // re-locking same key shouldn't put us over the limit
1390
1
  s = txn->Put("X", "xx");
1391
1
  ASSERT_OK(s);
1392
1393
1
  s = txn->GetForUpdate(read_options, "W", &value);
1394
1
  ASSERT_TRUE(s.IsBusy());
1395
1
  s = txn->GetForUpdate(read_options, "V", &value);
1396
1
  ASSERT_TRUE(s.IsBusy());
1397
1398
  // re-locking same key shouldn't put us over the limit
1399
1
  s = txn->GetForUpdate(read_options, "Y", &value);
1400
1
  ASSERT_OK(s);
1401
1
  ASSERT_EQ("y", value);
1402
1403
1
  s = txn->Get(read_options, "W", &value);
1404
1
  ASSERT_TRUE(s.IsNotFound());
1405
1406
1
  Transaction* txn2 = db->BeginTransaction(write_options);
1407
1
  ASSERT_TRUE(txn2);
1408
1409
  // "X" currently locked
1410
1
  s = txn2->Put("X", "x");
1411
1
  ASSERT_TRUE(s.IsTimedOut());
1412
1413
  // lock limit reached
1414
1
  s = txn2->Put("M", "m");
1415
1
  ASSERT_TRUE(s.IsBusy());
1416
1417
1
  s = txn->Commit();
1418
1
  ASSERT_OK(s);
1419
1420
1
  s = db->Get(read_options, "X", &value);
1421
1
  ASSERT_OK(s);
1422
1
  ASSERT_EQ("xx", value);
1423
1424
1
  s = db->Get(read_options, "W", &value);
1425
1
  ASSERT_TRUE(s.IsNotFound());
1426
1427
  // Committing txn should release its locks and allow txn2 to proceed
1428
1
  s = txn2->Put("X", "x2");
1429
1
  ASSERT_OK(s);
1430
1431
1
  s = txn2->Delete("X");
1432
1
  ASSERT_OK(s);
1433
1434
1
  s = txn2->Put("M", "m");
1435
1
  ASSERT_OK(s);
1436
1437
1
  s = txn2->Put("Z", "z2");
1438
1
  ASSERT_OK(s);
1439
1440
  // lock limit reached
1441
1
  s = txn2->Delete("Y");
1442
1
  ASSERT_TRUE(s.IsBusy());
1443
1444
1
  s = txn2->Commit();
1445
1
  ASSERT_OK(s);
1446
1447
1
  s = db->Get(read_options, "Z", &value);
1448
1
  ASSERT_OK(s);
1449
1
  ASSERT_EQ("z2", value);
1450
1451
1
  s = db->Get(read_options, "Y", &value);
1452
1
  ASSERT_OK(s);
1453
1
  ASSERT_EQ("y", value);
1454
1455
1
  s = db->Get(read_options, "X", &value);
1456
1
  ASSERT_TRUE(s.IsNotFound());
1457
1458
1
  delete txn;
1459
1
  delete txn2;
1460
1
}
1461
1462
1
TEST_F(TransactionTest, IteratorTest) {
1463
1
  WriteOptions write_options;
1464
1
  ReadOptions read_options, snapshot_read_options;
1465
1
  TransactionOptions txn_options;
1466
1
  string value;
1467
1
  Status s;
1468
1469
  // Write some keys to the db
1470
1
  s = db->Put(write_options, "A", "a");
1471
1
  ASSERT_OK(s);
1472
1473
1
  s = db->Put(write_options, "G", "g");
1474
1
  ASSERT_OK(s);
1475
1476
1
  s = db->Put(write_options, "F", "f");
1477
1
  ASSERT_OK(s);
1478
1479
1
  s = db->Put(write_options, "C", "c");
1480
1
  ASSERT_OK(s);
1481
1482
1
  s = db->Put(write_options, "D", "d");
1483
1
  ASSERT_OK(s);
1484
1485
1
  Transaction* txn = db->BeginTransaction(write_options);
1486
1
  ASSERT_TRUE(txn);
1487
1488
  // Write some keys in a txn
1489
1
  s = txn->Put("B", "b");
1490
1
  ASSERT_OK(s);
1491
1492
1
  s = txn->Put("H", "h");
1493
1
  ASSERT_OK(s);
1494
1495
1
  s = txn->Delete("D");
1496
1
  ASSERT_OK(s);
1497
1498
1
  s = txn->Put("E", "e");
1499
1
  ASSERT_OK(s);
1500
1501
1
  txn->SetSnapshot();
1502
1
  const Snapshot* snapshot = txn->GetSnapshot();
1503
1504
  // Write some keys to the db after the snapshot
1505
1
  s = db->Put(write_options, "BB", "xx");
1506
1
  ASSERT_OK(s);
1507
1508
1
  s = db->Put(write_options, "C", "xx");
1509
1
  ASSERT_OK(s);
1510
1511
1
  read_options.snapshot = snapshot;
1512
1
  Iterator* iter = txn->GetIterator(read_options);
1513
1
  ASSERT_OK(iter->status());
1514
1
  iter->SeekToFirst();
1515
1516
  // Read all keys via iter and lock them all
1517
1
  std::string results[] = {"a", "b", "c", "e", "f", "g", "h"};
1518
8
  for (int i = 0; i < 7; i++) {
1519
7
    ASSERT_OK(iter->status());
1520
7
    ASSERT_TRUE(iter->Valid());
1521
7
    ASSERT_EQ(results[i], iter->value().ToString());
1522
1523
7
    s = txn->GetForUpdate(read_options, iter->key(), nullptr);
1524
7
    if (i == 2) {
1525
      // "C" was modified after txn's snapshot
1526
1
      ASSERT_TRUE(s.IsBusy());
1527
6
    } else {
1528
6
      ASSERT_OK(s);
1529
6
    }
1530
1531
7
    iter->Next();
1532
7
  }
1533
1
  ASSERT_FALSE(iter->Valid());
1534
1535
1
  iter->Seek("G");
1536
1
  ASSERT_OK(iter->status());
1537
1
  ASSERT_TRUE(iter->Valid());
1538
1
  ASSERT_EQ("g", iter->value().ToString());
1539
1540
1
  iter->Prev();
1541
1
  ASSERT_OK(iter->status());
1542
1
  ASSERT_TRUE(iter->Valid());
1543
1
  ASSERT_EQ("f", iter->value().ToString());
1544
1545
1
  iter->Seek("D");
1546
1
  ASSERT_OK(iter->status());
1547
1
  ASSERT_TRUE(iter->Valid());
1548
1
  ASSERT_EQ("e", iter->value().ToString());
1549
1550
1
  iter->Seek("C");
1551
1
  ASSERT_OK(iter->status());
1552
1
  ASSERT_TRUE(iter->Valid());
1553
1
  ASSERT_EQ("c", iter->value().ToString());
1554
1555
1
  iter->Next();
1556
1
  ASSERT_OK(iter->status());
1557
1
  ASSERT_TRUE(iter->Valid());
1558
1
  ASSERT_EQ("e", iter->value().ToString());
1559
1560
1
  iter->Seek("");
1561
1
  ASSERT_OK(iter->status());
1562
1
  ASSERT_TRUE(iter->Valid());
1563
1
  ASSERT_EQ("a", iter->value().ToString());
1564
1565
1
  iter->Seek("X");
1566
1
  ASSERT_OK(iter->status());
1567
1
  ASSERT_FALSE(iter->Valid());
1568
1569
1
  iter->SeekToLast();
1570
1
  ASSERT_OK(iter->status());
1571
1
  ASSERT_TRUE(iter->Valid());
1572
1
  ASSERT_EQ("h", iter->value().ToString());
1573
1574
1
  s = txn->Commit();
1575
1
  ASSERT_OK(s);
1576
1577
1
  delete iter;
1578
1
  delete txn;
1579
1
}
1580
1581
1
TEST_F(TransactionTest, DisableIndexingTest) {
1582
1
  WriteOptions write_options;
1583
1
  ReadOptions read_options;
1584
1
  string value;
1585
1
  Status s;
1586
1587
1
  Transaction* txn = db->BeginTransaction(write_options);
1588
1
  ASSERT_TRUE(txn);
1589
1590
1
  s = txn->Put("A", "a");
1591
1
  ASSERT_OK(s);
1592
1593
1
  s = txn->Get(read_options, "A", &value);
1594
1
  ASSERT_OK(s);
1595
1
  ASSERT_EQ("a", value);
1596
1597
1
  txn->DisableIndexing();
1598
1599
1
  s = txn->Put("B", "b");
1600
1
  ASSERT_OK(s);
1601
1602
1
  s = txn->Get(read_options, "B", &value);
1603
1
  ASSERT_TRUE(s.IsNotFound());
1604
1605
1
  Iterator* iter = txn->GetIterator(read_options);
1606
1
  ASSERT_OK(iter->status());
1607
1608
1
  iter->Seek("B");
1609
1
  ASSERT_OK(iter->status());
1610
1
  ASSERT_FALSE(iter->Valid());
1611
1612
1
  s = txn->Delete("A");
1613
1614
1
  s = txn->Get(read_options, "A", &value);
1615
1
  ASSERT_OK(s);
1616
1
  ASSERT_EQ("a", value);
1617
1618
1
  txn->EnableIndexing();
1619
1620
1
  s = txn->Put("B", "bb");
1621
1
  ASSERT_OK(s);
1622
1623
1
  iter->Seek("B");
1624
1
  ASSERT_OK(iter->status());
1625
1
  ASSERT_TRUE(iter->Valid());
1626
1
  ASSERT_EQ("bb", iter->value().ToString());
1627
1628
1
  s = txn->Get(read_options, "B", &value);
1629
1
  ASSERT_OK(s);
1630
1
  ASSERT_EQ("bb", value);
1631
1632
1
  s = txn->Put("A", "aa");
1633
1
  ASSERT_OK(s);
1634
1635
1
  s = txn->Get(read_options, "A", &value);
1636
1
  ASSERT_OK(s);
1637
1
  ASSERT_EQ("aa", value);
1638
1639
1
  delete iter;
1640
1
  delete txn;
1641
1
}
1642
1643
1
TEST_F(TransactionTest, SavepointTest) {
1644
1
  WriteOptions write_options;
1645
1
  ReadOptions read_options, snapshot_read_options;
1646
1
  TransactionOptions txn_options;
1647
1
  string value;
1648
1
  Status s;
1649
1650
1
  Transaction* txn = db->BeginTransaction(write_options);
1651
1
  ASSERT_TRUE(txn);
1652
1653
1
  ASSERT_EQ(0, txn->GetNumPuts());
1654
1655
1
  s = txn->RollbackToSavePoint();
1656
1
  ASSERT_TRUE(s.IsNotFound());
1657
1658
1
  txn->SetSavePoint();  // 1
1659
1660
1
  ASSERT_OK(txn->RollbackToSavePoint());  // Rollback to beginning of txn
1661
1
  s = txn->RollbackToSavePoint();
1662
1
  ASSERT_TRUE(s.IsNotFound());
1663
1664
1
  s = txn->Put("B", "b");
1665
1
  ASSERT_OK(s);
1666
1667
1
  ASSERT_EQ(1, txn->GetNumPuts());
1668
1
  ASSERT_EQ(0, txn->GetNumDeletes());
1669
1670
1
  s = txn->Commit();
1671
1
  ASSERT_OK(s);
1672
1673
1
  s = db->Get(read_options, "B", &value);
1674
1
  ASSERT_OK(s);
1675
1
  ASSERT_EQ("b", value);
1676
1677
1
  delete txn;
1678
1
  txn = db->BeginTransaction(write_options);
1679
1
  ASSERT_TRUE(txn);
1680
1681
1
  s = txn->Put("A", "a");
1682
1
  ASSERT_OK(s);
1683
1684
1
  s = txn->Put("B", "bb");
1685
1
  ASSERT_OK(s);
1686
1687
1
  s = txn->Put("C", "c");
1688
1
  ASSERT_OK(s);
1689
1690
1
  txn->SetSavePoint();  // 2
1691
1692
1
  s = txn->Delete("B");
1693
1
  ASSERT_OK(s);
1694
1695
1
  s = txn->Put("C", "cc");
1696
1
  ASSERT_OK(s);
1697
1698
1
  s = txn->Put("D", "d");
1699
1
  ASSERT_OK(s);
1700
1701
1
  ASSERT_EQ(5, txn->GetNumPuts());
1702
1
  ASSERT_EQ(1, txn->GetNumDeletes());
1703
1704
1
  ASSERT_OK(txn->RollbackToSavePoint());  // Rollback to 2
1705
1706
1
  ASSERT_EQ(3, txn->GetNumPuts());
1707
1
  ASSERT_EQ(0, txn->GetNumDeletes());
1708
1709
1
  s = txn->Get(read_options, "A", &value);
1710
1
  ASSERT_OK(s);
1711
1
  ASSERT_EQ("a", value);
1712
1713
1
  s = txn->Get(read_options, "B", &value);
1714
1
  ASSERT_OK(s);
1715
1
  ASSERT_EQ("bb", value);
1716
1717
1
  s = txn->Get(read_options, "C", &value);
1718
1
  ASSERT_OK(s);
1719
1
  ASSERT_EQ("c", value);
1720
1721
1
  s = txn->Get(read_options, "D", &value);
1722
1
  ASSERT_TRUE(s.IsNotFound());
1723
1724
1
  s = txn->Put("A", "a");
1725
1
  ASSERT_OK(s);
1726
1727
1
  s = txn->Put("E", "e");
1728
1
  ASSERT_OK(s);
1729
1730
1
  ASSERT_EQ(5, txn->GetNumPuts());
1731
1
  ASSERT_EQ(0, txn->GetNumDeletes());
1732
1733
  // Rollback to beginning of txn
1734
1
  s = txn->RollbackToSavePoint();
1735
1
  ASSERT_TRUE(s.IsNotFound());
1736
1
  txn->Rollback();
1737
1738
1
  ASSERT_EQ(0, txn->GetNumPuts());
1739
1
  ASSERT_EQ(0, txn->GetNumDeletes());
1740
1741
1
  s = txn->Get(read_options, "A", &value);
1742
1
  ASSERT_TRUE(s.IsNotFound());
1743
1744
1
  s = txn->Get(read_options, "B", &value);
1745
1
  ASSERT_OK(s);
1746
1
  ASSERT_EQ("b", value);
1747
1748
1
  s = txn->Get(read_options, "D", &value);
1749
1
  ASSERT_TRUE(s.IsNotFound());
1750
1751
1
  s = txn->Get(read_options, "D", &value);
1752
1
  ASSERT_TRUE(s.IsNotFound());
1753
1754
1
  s = txn->Get(read_options, "E", &value);
1755
1
  ASSERT_TRUE(s.IsNotFound());
1756
1757
1
  s = txn->Put("A", "aa");
1758
1
  ASSERT_OK(s);
1759
1760
1
  s = txn->Put("F", "f");
1761
1
  ASSERT_OK(s);
1762
1763
1
  ASSERT_EQ(2, txn->GetNumPuts());
1764
1
  ASSERT_EQ(0, txn->GetNumDeletes());
1765
1766
1
  txn->SetSavePoint();  // 3
1767
1
  txn->SetSavePoint();  // 4
1768
1769
1
  s = txn->Put("G", "g");
1770
1
  ASSERT_OK(s);
1771
1772
1
  s = txn->SingleDelete("F");
1773
1
  ASSERT_OK(s);
1774
1775
1
  s = txn->Delete("B");
1776
1
  ASSERT_OK(s);
1777
1778
1
  s = txn->Get(read_options, "A", &value);
1779
1
  ASSERT_OK(s);
1780
1
  ASSERT_EQ("aa", value);
1781
1782
1
  s = txn->Get(read_options, "F", &value);
1783
  // According to db.h, doing a SingleDelete on a key that has been
1784
  // overwritten will have undefinied behavior.  So it is unclear what the
1785
  // result of fetching "F" should be. The current implementation will
1786
  // return NotFound in this case.
1787
1
  ASSERT_TRUE(s.IsNotFound());
1788
1789
1
  s = txn->Get(read_options, "B", &value);
1790
1
  ASSERT_TRUE(s.IsNotFound());
1791
1792
1
  ASSERT_EQ(3, txn->GetNumPuts());
1793
1
  ASSERT_EQ(2, txn->GetNumDeletes());
1794
1795
1
  ASSERT_OK(txn->RollbackToSavePoint());  // Rollback to 3
1796
1797
1
  ASSERT_EQ(2, txn->GetNumPuts());
1798
1
  ASSERT_EQ(0, txn->GetNumDeletes());
1799
1800
1
  s = txn->Get(read_options, "F", &value);
1801
1
  ASSERT_OK(s);
1802
1
  ASSERT_EQ("f", value);
1803
1804
1
  s = txn->Get(read_options, "G", &value);
1805
1
  ASSERT_TRUE(s.IsNotFound());
1806
1807
1
  s = txn->Commit();
1808
1
  ASSERT_OK(s);
1809
1810
1
  s = db->Get(read_options, "F", &value);
1811
1
  ASSERT_OK(s);
1812
1
  ASSERT_EQ("f", value);
1813
1814
1
  s = db->Get(read_options, "G", &value);
1815
1
  ASSERT_TRUE(s.IsNotFound());
1816
1817
1
  s = db->Get(read_options, "A", &value);
1818
1
  ASSERT_OK(s);
1819
1
  ASSERT_EQ("aa", value);
1820
1821
1
  s = db->Get(read_options, "B", &value);
1822
1
  ASSERT_OK(s);
1823
1
  ASSERT_EQ("b", value);
1824
1825
1
  s = db->Get(read_options, "C", &value);
1826
1
  ASSERT_TRUE(s.IsNotFound());
1827
1828
1
  s = db->Get(read_options, "D", &value);
1829
1
  ASSERT_TRUE(s.IsNotFound());
1830
1831
1
  s = db->Get(read_options, "E", &value);
1832
1
  ASSERT_TRUE(s.IsNotFound());
1833
1834
1
  delete txn;
1835
1
}
1836
1837
1
TEST_F(TransactionTest, SavepointTest2) {
1838
1
  WriteOptions write_options;
1839
1
  ReadOptions read_options;
1840
1
  TransactionOptions txn_options;
1841
1
  Status s;
1842
1843
1
  txn_options.lock_timeout = 1;  // 1 ms
1844
1
  Transaction* txn1 = db->BeginTransaction(write_options, txn_options);
1845
1
  ASSERT_TRUE(txn1);
1846
1847
1
  s = txn1->Put("A", "");
1848
1
  ASSERT_OK(s);
1849
1850
1
  txn1->SetSavePoint();  // 1
1851
1852
1
  s = txn1->Put("A", "a");
1853
1
  ASSERT_OK(s);
1854
1855
1
  s = txn1->Put("C", "c");
1856
1
  ASSERT_OK(s);
1857
1858
1
  txn1->SetSavePoint();  // 2
1859
1860
1
  s = txn1->Put("A", "a");
1861
1
  ASSERT_OK(s);
1862
1
  s = txn1->Put("B", "b");
1863
1
  ASSERT_OK(s);
1864
1865
1
  ASSERT_OK(txn1->RollbackToSavePoint());  // Rollback to 2
1866
1867
  // Verify that "A" and "C" is still locked while "B" is not
1868
1
  Transaction* txn2 = db->BeginTransaction(write_options, txn_options);
1869
1
  ASSERT_TRUE(txn2);
1870
1871
1
  s = txn2->Put("A", "a2");
1872
1
  ASSERT_TRUE(s.IsTimedOut());
1873
1
  s = txn2->Put("C", "c2");
1874
1
  ASSERT_TRUE(s.IsTimedOut());
1875
1
  s = txn2->Put("B", "b2");
1876
1
  ASSERT_OK(s);
1877
1878
1
  s = txn1->Put("A", "aa");
1879
1
  ASSERT_OK(s);
1880
1
  s = txn1->Put("B", "bb");
1881
1
  ASSERT_TRUE(s.IsTimedOut());
1882
1883
1
  s = txn2->Commit();
1884
1
  ASSERT_OK(s);
1885
1
  delete txn2;
1886
1887
1
  s = txn1->Put("A", "aaa");
1888
1
  ASSERT_OK(s);
1889
1
  s = txn1->Put("B", "bbb");
1890
1
  ASSERT_OK(s);
1891
1
  s = txn1->Put("C", "ccc");
1892
1
  ASSERT_OK(s);
1893
1894
1
  txn1->SetSavePoint();                    // 3
1895
1
  ASSERT_OK(txn1->RollbackToSavePoint());  // Rollback to 3
1896
1897
  // Verify that "A", "B", "C" are still locked
1898
1
  txn2 = db->BeginTransaction(write_options, txn_options);
1899
1
  ASSERT_TRUE(txn2);
1900
1901
1
  s = txn2->Put("A", "a2");
1902
1
  ASSERT_TRUE(s.IsTimedOut());
1903
1
  s = txn2->Put("B", "b2");
1904
1
  ASSERT_TRUE(s.IsTimedOut());
1905
1
  s = txn2->Put("C", "c2");
1906
1
  ASSERT_TRUE(s.IsTimedOut());
1907
1908
1
  ASSERT_OK(txn1->RollbackToSavePoint());  // Rollback to 1
1909
1910
  // Verify that only "A" is locked
1911
1
  s = txn2->Put("A", "a3");
1912
1
  ASSERT_TRUE(s.IsTimedOut());
1913
1
  s = txn2->Put("B", "b3");
1914
1
  ASSERT_OK(s);
1915
1
  s = txn2->Put("C", "c3po");
1916
1
  ASSERT_OK(s);
1917
1918
1
  s = txn1->Commit();
1919
1
  ASSERT_OK(s);
1920
1
  delete txn1;
1921
1922
  // Verify "A" "C" "B" are no longer locked
1923
1
  s = txn2->Put("A", "a4");
1924
1
  ASSERT_OK(s);
1925
1
  s = txn2->Put("B", "b4");
1926
1
  ASSERT_OK(s);
1927
1
  s = txn2->Put("C", "c4");
1928
1
  ASSERT_OK(s);
1929
1930
1
  s = txn2->Commit();
1931
1
  ASSERT_OK(s);
1932
1
  delete txn2;
1933
1
}
1934
1935
1
TEST_F(TransactionTest, UndoGetForUpdateTest) {
1936
1
  WriteOptions write_options;
1937
1
  ReadOptions read_options;
1938
1
  TransactionOptions txn_options;
1939
1
  string value;
1940
1
  Status s;
1941
1942
1
  txn_options.lock_timeout = 1;  // 1 ms
1943
1
  Transaction* txn1 = db->BeginTransaction(write_options, txn_options);
1944
1
  ASSERT_TRUE(txn1);
1945
1946
1
  txn1->UndoGetForUpdate("A");
1947
1948
1
  s = txn1->Commit();
1949
1
  ASSERT_OK(s);
1950
1
  delete txn1;
1951
1952
1
  txn1 = db->BeginTransaction(write_options, txn_options);
1953
1954
1
  txn1->UndoGetForUpdate("A");
1955
1
  s = txn1->GetForUpdate(read_options, "A", &value);
1956
1
  ASSERT_TRUE(s.IsNotFound());
1957
1958
  // Verify that A is locked
1959
1
  Transaction* txn2 = db->BeginTransaction(write_options, txn_options);
1960
1
  s = txn2->Put("A", "a");
1961
1
  ASSERT_TRUE(s.IsTimedOut());
1962
1963
1
  txn1->UndoGetForUpdate("A");
1964
1965
  // Verify that A is now unlocked
1966
1
  s = txn2->Put("A", "a2");
1967
1
  ASSERT_OK(s);
1968
1
  ASSERT_OK(txn2->Commit());
1969
1
  delete txn2;
1970
1
  s = db->Get(read_options, "A", &value);
1971
1
  ASSERT_OK(s);
1972
1
  ASSERT_EQ("a2", value);
1973
1974
1
  s = txn1->Delete("A");
1975
1
  ASSERT_OK(s);
1976
1
  s = txn1->GetForUpdate(read_options, "A", &value);
1977
1
  ASSERT_TRUE(s.IsNotFound());
1978
1979
1
  s = txn1->Put("B", "b3");
1980
1
  ASSERT_OK(s);
1981
1
  s = txn1->GetForUpdate(read_options, "B", &value);
1982
1
  ASSERT_OK(s);
1983
1984
1
  txn1->UndoGetForUpdate("A");
1985
1
  txn1->UndoGetForUpdate("B");
1986
1987
  // Verify that A and B are still locked
1988
1
  txn2 = db->BeginTransaction(write_options, txn_options);
1989
1
  s = txn2->Put("A", "a4");
1990
1
  ASSERT_TRUE(s.IsTimedOut());
1991
1
  s = txn2->Put("B", "b4");
1992
1
  ASSERT_TRUE(s.IsTimedOut());
1993
1994
1
  txn1->Rollback();
1995
1
  delete txn1;
1996
1997
  // Verify that A and B are no longer locked
1998
1
  s = txn2->Put("A", "a5");
1999
1
  ASSERT_OK(s);
2000
1
  s = txn2->Put("B", "b5");
2001
1
  ASSERT_OK(s);
2002
1
  s = txn2->Commit();
2003
1
  delete txn2;
2004
1
  ASSERT_OK(s);
2005
2006
1
  txn1 = db->BeginTransaction(write_options, txn_options);
2007
2008
1
  s = txn1->GetForUpdate(read_options, "A", &value);
2009
1
  ASSERT_OK(s);
2010
1
  s = txn1->GetForUpdate(read_options, "A", &value);
2011
1
  ASSERT_OK(s);
2012
1
  s = txn1->GetForUpdate(read_options, "C", &value);
2013
1
  ASSERT_TRUE(s.IsNotFound());
2014
1
  s = txn1->GetForUpdate(read_options, "A", &value);
2015
1
  ASSERT_OK(s);
2016
1
  s = txn1->GetForUpdate(read_options, "C", &value);
2017
1
  ASSERT_TRUE(s.IsNotFound());
2018
1
  s = txn1->GetForUpdate(read_options, "B", &value);
2019
1
  ASSERT_OK(s);
2020
1
  s = txn1->Put("B", "b5");
2021
1
  s = txn1->GetForUpdate(read_options, "B", &value);
2022
1
  ASSERT_OK(s);
2023
2024
1
  txn1->UndoGetForUpdate("A");
2025
1
  txn1->UndoGetForUpdate("B");
2026
1
  txn1->UndoGetForUpdate("C");
2027
1
  txn1->UndoGetForUpdate("X");
2028
2029
  // Verify A,B,C are locked
2030
1
  txn2 = db->BeginTransaction(write_options, txn_options);
2031
1
  s = txn2->Put("A", "a6");
2032
1
  ASSERT_TRUE(s.IsTimedOut());
2033
1
  s = txn2->Delete("B");
2034
1
  ASSERT_TRUE(s.IsTimedOut());
2035
1
  s = txn2->Put("C", "c6");
2036
1
  ASSERT_TRUE(s.IsTimedOut());
2037
1
  s = txn2->Put("X", "x6");
2038
1
  ASSERT_OK(s);
2039
2040
1
  txn1->UndoGetForUpdate("A");
2041
1
  txn1->UndoGetForUpdate("B");
2042
1
  txn1->UndoGetForUpdate("C");
2043
1
  txn1->UndoGetForUpdate("X");
2044
2045
  // Verify A,B are locked and C is not
2046
1
  s = txn2->Put("A", "a6");
2047
1
  ASSERT_TRUE(s.IsTimedOut());
2048
1
  s = txn2->Delete("B");
2049
1
  ASSERT_TRUE(s.IsTimedOut());
2050
1
  s = txn2->Put("C", "c6");
2051
1
  ASSERT_OK(s);
2052
1
  s = txn2->Put("X", "x6");
2053
1
  ASSERT_OK(s);
2054
2055
1
  txn1->UndoGetForUpdate("A");
2056
1
  txn1->UndoGetForUpdate("B");
2057
1
  txn1->UndoGetForUpdate("C");
2058
1
  txn1->UndoGetForUpdate("X");
2059
2060
  // Verify B is locked and A and C are not
2061
1
  s = txn2->Put("A", "a7");
2062
1
  ASSERT_OK(s);
2063
1
  s = txn2->Delete("B");
2064
1
  ASSERT_TRUE(s.IsTimedOut());
2065
1
  s = txn2->Put("C", "c7");
2066
1
  ASSERT_OK(s);
2067
1
  s = txn2->Put("X", "x7");
2068
1
  ASSERT_OK(s);
2069
2070
1
  s = txn2->Commit();
2071
1
  ASSERT_OK(s);
2072
1
  delete txn2;
2073
2074
1
  s = txn1->Commit();
2075
1
  ASSERT_OK(s);
2076
1
  delete txn1;
2077
1
}
2078
2079
1
TEST_F(TransactionTest, UndoGetForUpdateTest2) {
2080
1
  WriteOptions write_options;
2081
1
  ReadOptions read_options;
2082
1
  TransactionOptions txn_options;
2083
1
  string value;
2084
1
  Status s;
2085
2086
1
  s = db->Put(write_options, "A", "");
2087
1
  ASSERT_OK(s);
2088
2089
1
  txn_options.lock_timeout = 1;  // 1 ms
2090
1
  Transaction* txn1 = db->BeginTransaction(write_options, txn_options);
2091
1
  ASSERT_TRUE(txn1);
2092
2093
1
  s = txn1->GetForUpdate(read_options, "A", &value);
2094
1
  ASSERT_OK(s);
2095
1
  s = txn1->GetForUpdate(read_options, "B", &value);
2096
1
  ASSERT_TRUE(s.IsNotFound());
2097
2098
1
  s = txn1->Put("F", "f");
2099
1
  ASSERT_OK(s);
2100
2101
1
  txn1->SetSavePoint();  // 1
2102
2103
1
  txn1->UndoGetForUpdate("A");
2104
2105
1
  s = txn1->GetForUpdate(read_options, "C", &value);
2106
1
  ASSERT_TRUE(s.IsNotFound());
2107
1
  s = txn1->GetForUpdate(read_options, "D", &value);
2108
1
  ASSERT_TRUE(s.IsNotFound());
2109
2110
1
  s = txn1->Put("E", "e");
2111
1
  ASSERT_OK(s);
2112
1
  s = txn1->GetForUpdate(read_options, "E", &value);
2113
1
  ASSERT_OK(s);
2114
2115
1
  s = txn1->GetForUpdate(read_options, "F", &value);
2116
1
  ASSERT_OK(s);
2117
2118
  // Verify A,B,C,D,E,F are still locked
2119
1
  Transaction* txn2 = db->BeginTransaction(write_options, txn_options);
2120
1
  s = txn2->Put("A", "a1");
2121
1
  ASSERT_TRUE(s.IsTimedOut());
2122
1
  s = txn2->Put("B", "b1");
2123
1
  ASSERT_TRUE(s.IsTimedOut());
2124
1
  s = txn2->Put("C", "c1");
2125
1
  ASSERT_TRUE(s.IsTimedOut());
2126
1
  s = txn2->Put("D", "d1");
2127
1
  ASSERT_TRUE(s.IsTimedOut());
2128
1
  s = txn2->Put("E", "e1");
2129
1
  ASSERT_TRUE(s.IsTimedOut());
2130
1
  s = txn2->Put("F", "f1");
2131
1
  ASSERT_TRUE(s.IsTimedOut());
2132
2133
1
  txn1->UndoGetForUpdate("C");
2134
1
  txn1->UndoGetForUpdate("E");
2135
2136
  // Verify A,B,D,E,F are still locked and C is not.
2137
1
  s = txn2->Put("A", "a2");
2138
1
  ASSERT_TRUE(s.IsTimedOut());
2139
1
  s = txn2->Put("B", "b2");
2140
1
  ASSERT_TRUE(s.IsTimedOut());
2141
1
  s = txn2->Put("D", "d2");
2142
1
  ASSERT_TRUE(s.IsTimedOut());
2143
1
  s = txn2->Put("E", "e2");
2144
1
  ASSERT_TRUE(s.IsTimedOut());
2145
1
  s = txn2->Put("F", "f2");
2146
1
  ASSERT_TRUE(s.IsTimedOut());
2147
1
  s = txn2->Put("C", "c2");
2148
1
  ASSERT_OK(s);
2149
2150
1
  txn1->SetSavePoint();  // 2
2151
2152
1
  s = txn1->Put("H", "h");
2153
1
  ASSERT_OK(s);
2154
2155
1
  txn1->UndoGetForUpdate("A");
2156
1
  txn1->UndoGetForUpdate("B");
2157
1
  txn1->UndoGetForUpdate("C");
2158
1
  txn1->UndoGetForUpdate("D");
2159
1
  txn1->UndoGetForUpdate("E");
2160
1
  txn1->UndoGetForUpdate("F");
2161
1
  txn1->UndoGetForUpdate("G");
2162
1
  txn1->UndoGetForUpdate("H");
2163
2164
  // Verify A,B,D,E,F,H are still locked and C,G are not.
2165
1
  s = txn2->Put("A", "a3");
2166
1
  ASSERT_TRUE(s.IsTimedOut());
2167
1
  s = txn2->Put("B", "b3");
2168
1
  ASSERT_TRUE(s.IsTimedOut());
2169
1
  s = txn2->Put("D", "d3");
2170
1
  ASSERT_TRUE(s.IsTimedOut());
2171
1
  s = txn2->Put("E", "e3");
2172
1
  ASSERT_TRUE(s.IsTimedOut());
2173
1
  s = txn2->Put("F", "f3");
2174
1
  ASSERT_TRUE(s.IsTimedOut());
2175
1
  s = txn2->Put("H", "h3");
2176
1
  ASSERT_TRUE(s.IsTimedOut());
2177
1
  s = txn2->Put("C", "c3");
2178
1
  ASSERT_OK(s);
2179
1
  s = txn2->Put("G", "g3");
2180
1
  ASSERT_OK(s);
2181
2182
1
  ASSERT_OK(txn1->RollbackToSavePoint());  // rollback to 2
2183
2184
  // Verify A,B,D,E,F are still locked and C,G,H are not.
2185
1
  s = txn2->Put("A", "a3");
2186
1
  ASSERT_TRUE(s.IsTimedOut());
2187
1
  s = txn2->Put("B", "b3");
2188
1
  ASSERT_TRUE(s.IsTimedOut());
2189
1
  s = txn2->Put("D", "d3");
2190
1
  ASSERT_TRUE(s.IsTimedOut());
2191
1
  s = txn2->Put("E", "e3");
2192
1
  ASSERT_TRUE(s.IsTimedOut());
2193
1
  s = txn2->Put("F", "f3");
2194
1
  ASSERT_TRUE(s.IsTimedOut());
2195
1
  s = txn2->Put("C", "c3");
2196
1
  ASSERT_OK(s);
2197
1
  s = txn2->Put("G", "g3");
2198
1
  ASSERT_OK(s);
2199
1
  s = txn2->Put("H", "h3");
2200
1
  ASSERT_OK(s);
2201
2202
1
  txn1->UndoGetForUpdate("A");
2203
1
  txn1->UndoGetForUpdate("B");
2204
1
  txn1->UndoGetForUpdate("C");
2205
1
  txn1->UndoGetForUpdate("D");
2206
1
  txn1->UndoGetForUpdate("E");
2207
1
  txn1->UndoGetForUpdate("F");
2208
1
  txn1->UndoGetForUpdate("G");
2209
1
  txn1->UndoGetForUpdate("H");
2210
2211
  // Verify A,B,E,F are still locked and C,D,G,H are not.
2212
1
  s = txn2->Put("A", "a3");
2213
1
  ASSERT_TRUE(s.IsTimedOut());
2214
1
  s = txn2->Put("B", "b3");
2215
1
  ASSERT_TRUE(s.IsTimedOut());
2216
1
  s = txn2->Put("E", "e3");
2217
1
  ASSERT_TRUE(s.IsTimedOut());
2218
1
  s = txn2->Put("F", "f3");
2219
1
  ASSERT_TRUE(s.IsTimedOut());
2220
1
  s = txn2->Put("C", "c3");
2221
1
  ASSERT_OK(s);
2222
1
  s = txn2->Put("D", "d3");
2223
1
  ASSERT_OK(s);
2224
1
  s = txn2->Put("G", "g3");
2225
1
  ASSERT_OK(s);
2226
1
  s = txn2->Put("H", "h3");
2227
1
  ASSERT_OK(s);
2228
2229
1
  ASSERT_OK(txn1->RollbackToSavePoint());  // rollback to 1
2230
2231
  // Verify A,B,F are still locked and C,D,E,G,H are not.
2232
1
  s = txn2->Put("A", "a3");
2233
1
  ASSERT_TRUE(s.IsTimedOut());
2234
1
  s = txn2->Put("B", "b3");
2235
1
  ASSERT_TRUE(s.IsTimedOut());
2236
1
  s = txn2->Put("F", "f3");
2237
1
  ASSERT_TRUE(s.IsTimedOut());
2238
1
  s = txn2->Put("C", "c3");
2239
1
  ASSERT_OK(s);
2240
1
  s = txn2->Put("D", "d3");
2241
1
  ASSERT_OK(s);
2242
1
  s = txn2->Put("E", "e3");
2243
1
  ASSERT_OK(s);
2244
1
  s = txn2->Put("G", "g3");
2245
1
  ASSERT_OK(s);
2246
1
  s = txn2->Put("H", "h3");
2247
1
  ASSERT_OK(s);
2248
2249
1
  txn1->UndoGetForUpdate("A");
2250
1
  txn1->UndoGetForUpdate("B");
2251
1
  txn1->UndoGetForUpdate("C");
2252
1
  txn1->UndoGetForUpdate("D");
2253
1
  txn1->UndoGetForUpdate("E");
2254
1
  txn1->UndoGetForUpdate("F");
2255
1
  txn1->UndoGetForUpdate("G");
2256
1
  txn1->UndoGetForUpdate("H");
2257
2258
  // Verify F is still locked and A,B,C,D,E,G,H are not.
2259
1
  s = txn2->Put("F", "f3");
2260
1
  ASSERT_TRUE(s.IsTimedOut());
2261
1
  s = txn2->Put("A", "a3");
2262
1
  ASSERT_OK(s);
2263
1
  s = txn2->Put("B", "b3");
2264
1
  ASSERT_OK(s);
2265
1
  s = txn2->Put("C", "c3");
2266
1
  ASSERT_OK(s);
2267
1
  s = txn2->Put("D", "d3");
2268
1
  ASSERT_OK(s);
2269
1
  s = txn2->Put("E", "e3");
2270
1
  ASSERT_OK(s);
2271
1
  s = txn2->Put("G", "g3");
2272
1
  ASSERT_OK(s);
2273
1
  s = txn2->Put("H", "h3");
2274
1
  ASSERT_OK(s);
2275
2276
1
  s = txn1->Commit();
2277
1
  ASSERT_OK(s);
2278
1
  s = txn2->Commit();
2279
1
  ASSERT_OK(s);
2280
2281
1
  delete txn1;
2282
1
  delete txn2;
2283
1
}
2284
2285
1
TEST_F(TransactionTest, TimeoutTest) {
2286
1
  WriteOptions write_options;
2287
1
  ReadOptions read_options;
2288
1
  string value;
2289
1
  Status s;
2290
2291
1
  delete db;
2292
2293
  // transaction writes have an infinite timeout,
2294
  // but we will override this when we start a txn
2295
  // db writes have infinite timeout
2296
1
  txn_db_options.transaction_lock_timeout = -1;
2297
1
  txn_db_options.default_lock_timeout = -1;
2298
2299
1
  s = TransactionDB::Open(options, txn_db_options, dbname, &db);
2300
1
  ASSERT_OK(s);
2301
2302
1
  s = db->Put(write_options, "aaa", "aaa");
2303
1
  ASSERT_OK(s);
2304
2305
1
  TransactionOptions txn_options0;
2306
1
  txn_options0.expiration = 100;  // 100ms
2307
1
  txn_options0.lock_timeout = 50;  // txn timeout no longer infinite
2308
1
  Transaction* txn1 = db->BeginTransaction(write_options, txn_options0);
2309
2310
1
  s = txn1->GetForUpdate(read_options, "aaa", nullptr);
2311
1
  ASSERT_OK(s);
2312
2313
  // Conflicts with previous GetForUpdate.
2314
  // Since db writes do not have a timeout, this should eventually succeed when
2315
  // the transaction expires.
2316
1
  s = db->Put(write_options, "aaa", "xxx");
2317
1
  ASSERT_OK(s);
2318
2319
1
  ASSERT_GE(txn1->GetElapsedTime(),
2320
1
            static_cast<uint64_t>(txn_options0.expiration));
2321
2322
1
  s = txn1->Commit();
2323
1
  ASSERT_TRUE(s.IsExpired());  // expired!
2324
2325
1
  s = db->Get(read_options, "aaa", &value);
2326
1
  ASSERT_OK(s);
2327
1
  ASSERT_EQ("xxx", value);
2328
2329
1
  delete txn1;
2330
1
  delete db;
2331
2332
  // transaction writes have 10ms timeout,
2333
  // db writes have infinite timeout
2334
1
  txn_db_options.transaction_lock_timeout = 50;
2335
1
  txn_db_options.default_lock_timeout = -1;
2336
2337
1
  s = TransactionDB::Open(options, txn_db_options, dbname, &db);
2338
1
  ASSERT_OK(s);
2339
2340
1
  s = db->Put(write_options, "aaa", "aaa");
2341
1
  ASSERT_OK(s);
2342
2343
1
  TransactionOptions txn_options;
2344
1
  txn_options.expiration = 100;  // 100ms
2345
1
  txn1 = db->BeginTransaction(write_options, txn_options);
2346
2347
1
  s = txn1->GetForUpdate(read_options, "aaa", nullptr);
2348
1
  ASSERT_OK(s);
2349
2350
  // Conflicts with previous GetForUpdate.
2351
  // Since db writes do not have a timeout, this should eventually succeed when
2352
  // the transaction expires.
2353
1
  s = db->Put(write_options, "aaa", "xxx");
2354
1
  ASSERT_OK(s);
2355
2356
1
  s = txn1->Commit();
2357
1
  ASSERT_NOK(s);  // expired!
2358
2359
1
  s = db->Get(read_options, "aaa", &value);
2360
1
  ASSERT_OK(s);
2361
1
  ASSERT_EQ("xxx", value);
2362
2363
1
  delete txn1;
2364
1
  txn_options.expiration = 6000000;  // 100 minutes
2365
1
  txn_options.lock_timeout = 1;      // 1ms
2366
1
  txn1 = db->BeginTransaction(write_options, txn_options);
2367
1
  txn1->SetLockTimeout(100);
2368
2369
1
  TransactionOptions txn_options2;
2370
1
  txn_options2.expiration = 10;  // 10ms
2371
1
  Transaction* txn2 = db->BeginTransaction(write_options, txn_options2);
2372
1
  ASSERT_OK(s);
2373
2374
1
  s = txn2->Put("a", "2");
2375
1
  ASSERT_OK(s);
2376
2377
  // txn1 has a lock timeout longer than txn2's expiration, so it will win
2378
1
  s = txn1->Delete("a");
2379
1
  ASSERT_OK(s);
2380
2381
1
  s = txn1->Commit();
2382
1
  ASSERT_OK(s);
2383
2384
  // txn2 should be expired out since txn1 waiting until its timeout expired.
2385
1
  s = txn2->Commit();
2386
1
  ASSERT_TRUE(s.IsExpired());
2387
2388
1
  delete txn1;
2389
1
  delete txn2;
2390
1
  txn_options.expiration = 6000000;  // 100 minutes
2391
1
  txn1 = db->BeginTransaction(write_options, txn_options);
2392
1
  txn_options2.expiration = 100000000;
2393
1
  txn2 = db->BeginTransaction(write_options, txn_options2);
2394
2395
1
  s = txn1->Delete("asdf");
2396
1
  ASSERT_OK(s);
2397
2398
  // txn2 has a smaller lock timeout than txn1's expiration, so it will time out
2399
1
  s = txn2->Delete("asdf");
2400
1
  ASSERT_TRUE(s.IsTimedOut());
2401
1
  ASSERT_EQ("Timed out: Timeout waiting to lock key (timeout 1)", s.ToString(false));
2402
2403
1
  s = txn1->Commit();
2404
1
  ASSERT_OK(s);
2405
2406
1
  s = txn2->Put("asdf", "asdf");
2407
1
  ASSERT_OK(s);
2408
2409
1
  s = txn2->Commit();
2410
1
  ASSERT_OK(s);
2411
2412
1
  s = db->Get(read_options, "asdf", &value);
2413
1
  ASSERT_OK(s);
2414
1
  ASSERT_EQ("asdf", value);
2415
2416
1
  delete txn1;
2417
1
  delete txn2;
2418
1
}
2419
2420
1
TEST_F(TransactionTest, SingleDeleteTest) {
2421
1
  WriteOptions write_options;
2422
1
  ReadOptions read_options;
2423
1
  string value;
2424
1
  Status s;
2425
2426
1
  Transaction* txn = db->BeginTransaction(write_options);
2427
1
  ASSERT_TRUE(txn);
2428
2429
1
  s = txn->SingleDelete("A");
2430
1
  ASSERT_OK(s);
2431
2432
1
  s = txn->Get(read_options, "A", &value);
2433
1
  ASSERT_TRUE(s.IsNotFound());
2434
2435
1
  s = txn->Commit();
2436
1
  ASSERT_OK(s);
2437
1
  delete txn;
2438
2439
1
  txn = db->BeginTransaction(write_options);
2440
2441
1
  s = txn->SingleDelete("A");
2442
1
  ASSERT_OK(s);
2443
2444
1
  s = txn->Put("A", "a");
2445
1
  ASSERT_OK(s);
2446
2447
1
  s = txn->Get(read_options, "A", &value);
2448
1
  ASSERT_OK(s);
2449
1
  ASSERT_EQ("a", value);
2450
2451
1
  s = txn->Commit();
2452
1
  ASSERT_OK(s);
2453
1
  delete txn;
2454
2455
1
  s = db->Get(read_options, "A", &value);
2456
1
  ASSERT_OK(s);
2457
1
  ASSERT_EQ("a", value);
2458
2459
1
  txn = db->BeginTransaction(write_options);
2460
2461
1
  s = txn->SingleDelete("A");
2462
1
  ASSERT_OK(s);
2463
2464
1
  s = txn->Get(read_options, "A", &value);
2465
1
  ASSERT_TRUE(s.IsNotFound());
2466
2467
1
  s = txn->Commit();
2468
1
  ASSERT_OK(s);
2469
1
  delete txn;
2470
2471
1
  s = db->Get(read_options, "A", &value);
2472
1
  ASSERT_TRUE(s.IsNotFound());
2473
2474
1
  txn = db->BeginTransaction(write_options);
2475
1
  Transaction* txn2 = db->BeginTransaction(write_options);
2476
1
  txn2->SetSnapshot();
2477
2478
1
  s = txn->Put("A", "a");
2479
1
  ASSERT_OK(s);
2480
2481
1
  s = txn->Put("A", "a2");
2482
1
  ASSERT_OK(s);
2483
2484
1
  s = txn->SingleDelete("A");
2485
1
  ASSERT_OK(s);
2486
2487
1
  s = txn->SingleDelete("B");
2488
1
  ASSERT_OK(s);
2489
2490
  // According to db.h, doing a SingleDelete on a key that has been
2491
  // overwritten will have undefinied behavior.  So it is unclear what the
2492
  // result of fetching "A" should be. The current implementation will
2493
  // return NotFound in this case.
2494
1
  s = txn->Get(read_options, "A", &value);
2495
1
  ASSERT_TRUE(s.IsNotFound());
2496
2497
1
  s = txn2->Put("B", "b");
2498
1
  ASSERT_TRUE(s.IsTimedOut());
2499
1
  s = txn2->Commit();
2500
1
  ASSERT_OK(s);
2501
1
  delete txn2;
2502
2503
1
  s = txn->Commit();
2504
1
  ASSERT_OK(s);
2505
1
  delete txn;
2506
2507
  // According to db.h, doing a SingleDelete on a key that has been
2508
  // overwritten will have undefinied behavior.  So it is unclear what the
2509
  // result of fetching "A" should be. The current implementation will
2510
  // return NotFound in this case.
2511
1
  s = db->Get(read_options, "A", &value);
2512
1
  ASSERT_TRUE(s.IsNotFound());
2513
2514
1
  s = db->Get(read_options, "B", &value);
2515
1
  ASSERT_TRUE(s.IsNotFound());
2516
1
}
2517
2518
1
TEST_F(TransactionTest, MergeTest) {
2519
1
  WriteOptions write_options;
2520
1
  ReadOptions read_options;
2521
1
  string value;
2522
1
  Status s;
2523
2524
1
  Transaction* txn = db->BeginTransaction(write_options, TransactionOptions());
2525
1
  ASSERT_TRUE(txn);
2526
2527
1
  s = db->Put(write_options, "A", "a0");
2528
1
  ASSERT_OK(s);
2529
2530
1
  s = txn->Merge("A", "1");
2531
1
  ASSERT_OK(s);
2532
2533
1
  s = txn->Merge("A", "2");
2534
1
  ASSERT_OK(s);
2535
2536
1
  s = txn->Get(read_options, "A", &value);
2537
1
  ASSERT_TRUE(s.IsMergeInProgress());
2538
2539
1
  s = txn->Put("A", "a");
2540
1
  ASSERT_OK(s);
2541
2542
1
  s = txn->Get(read_options, "A", &value);
2543
1
  ASSERT_OK(s);
2544
1
  ASSERT_EQ("a", value);
2545
2546
1
  s = txn->Merge("A", "3");
2547
1
  ASSERT_OK(s);
2548
2549
1
  s = txn->Get(read_options, "A", &value);
2550
1
  ASSERT_TRUE(s.IsMergeInProgress());
2551
2552
1
  TransactionOptions txn_options;
2553
1
  txn_options.lock_timeout = 1;  // 1 ms
2554
1
  Transaction* txn2 = db->BeginTransaction(write_options, txn_options);
2555
1
  ASSERT_TRUE(txn2);
2556
2557
  // verify that txn has "A" locked
2558
1
  s = txn2->Merge("A", "4");
2559
1
  ASSERT_TRUE(s.IsTimedOut());
2560
2561
1
  s = txn2->Commit();
2562
1
  ASSERT_OK(s);
2563
1
  delete txn2;
2564
2565
1
  s = txn->Commit();
2566
1
  ASSERT_OK(s);
2567
1
  delete txn;
2568
2569
1
  s = db->Get(read_options, "A", &value);
2570
1
  ASSERT_OK(s);
2571
1
  ASSERT_EQ("a,3", value);
2572
1
}
2573
2574
1
TEST_F(TransactionTest, DeferSnapshotTest) {
2575
1
  WriteOptions write_options;
2576
1
  ReadOptions read_options;
2577
1
  string value;
2578
1
  Status s;
2579
2580
1
  s = db->Put(write_options, "A", "a0");
2581
1
  ASSERT_OK(s);
2582
2583
1
  Transaction* txn1 = db->BeginTransaction(write_options);
2584
1
  Transaction* txn2 = db->BeginTransaction(write_options);
2585
2586
1
  txn1->SetSnapshotOnNextOperation();
2587
1
  auto snapshot = txn1->GetSnapshot();
2588
1
  ASSERT_FALSE(snapshot);
2589
2590
1
  s = txn2->Put("A", "a2");
2591
1
  ASSERT_OK(s);
2592
1
  s = txn2->Commit();
2593
1
  ASSERT_OK(s);
2594
1
  delete txn2;
2595
2596
1
  s = txn1->GetForUpdate(read_options, "A", &value);
2597
  // Should not conflict with txn2 since snapshot wasn't set until
2598
  // GetForUpdate was called.
2599
1
  ASSERT_OK(s);
2600
1
  ASSERT_EQ("a2", value);
2601
2602
1
  s = txn1->Put("A", "a1");
2603
1
  ASSERT_OK(s);
2604
2605
1
  s = db->Put(write_options, "B", "b0");
2606
1
  ASSERT_OK(s);
2607
2608
  // Cannot lock B since it was written after the snapshot was set
2609
1
  s = txn1->Put("B", "b1");
2610
1
  ASSERT_TRUE(s.IsBusy());
2611
2612
1
  s = txn1->Commit();
2613
1
  ASSERT_OK(s);
2614
1
  delete txn1;
2615
2616
1
  s = db->Get(read_options, "A", &value);
2617
1
  ASSERT_OK(s);
2618
1
  ASSERT_EQ("a1", value);
2619
2620
1
  s = db->Get(read_options, "B", &value);
2621
1
  ASSERT_OK(s);
2622
1
  ASSERT_EQ("b0", value);
2623
1
}
2624
2625
1
TEST_F(TransactionTest, DeferSnapshotTest2) {
2626
1
  WriteOptions write_options;
2627
1
  ReadOptions read_options, snapshot_read_options;
2628
1
  string value;
2629
1
  Status s;
2630
2631
1
  Transaction* txn1 = db->BeginTransaction(write_options);
2632
2633
1
  txn1->SetSnapshot();
2634
2635
1
  s = txn1->Put("A", "a1");
2636
1
  ASSERT_OK(s);
2637
2638
1
  s = db->Put(write_options, "C", "c0");
2639
1
  ASSERT_OK(s);
2640
1
  s = db->Put(write_options, "D", "d0");
2641
1
  ASSERT_OK(s);
2642
2643
1
  snapshot_read_options.snapshot = txn1->GetSnapshot();
2644
2645
1
  txn1->SetSnapshotOnNextOperation();
2646
2647
1
  s = txn1->Get(snapshot_read_options, "C", &value);
2648
  // Snapshot was set before C was written
2649
1
  ASSERT_TRUE(s.IsNotFound());
2650
1
  s = txn1->Get(snapshot_read_options, "D", &value);
2651
  // Snapshot was set before D was written
2652
1
  ASSERT_TRUE(s.IsNotFound());
2653
2654
  // Snapshot should not have changed yet.
2655
1
  snapshot_read_options.snapshot = txn1->GetSnapshot();
2656
2657
1
  s = txn1->Get(snapshot_read_options, "C", &value);
2658
  // Snapshot was set before C was written
2659
1
  ASSERT_TRUE(s.IsNotFound());
2660
1
  s = txn1->Get(snapshot_read_options, "D", &value);
2661
  // Snapshot was set before D was written
2662
1
  ASSERT_TRUE(s.IsNotFound());
2663
2664
1
  s = txn1->GetForUpdate(read_options, "C", &value);
2665
1
  ASSERT_OK(s);
2666
1
  ASSERT_EQ("c0", value);
2667
2668
1
  s = db->Put(write_options, "D", "d00");
2669
1
  ASSERT_OK(s);
2670
2671
  // Snapshot is now set
2672
1
  snapshot_read_options.snapshot = txn1->GetSnapshot();
2673
1
  s = txn1->Get(snapshot_read_options, "D", &value);
2674
1
  ASSERT_OK(s);
2675
1
  ASSERT_EQ("d0", value);
2676
2677
1
  s = txn1->Commit();
2678
1
  ASSERT_OK(s);
2679
1
  delete txn1;
2680
1
}
2681
2682
1
TEST_F(TransactionTest, DeferSnapshotSavePointTest) {
2683
1
  WriteOptions write_options;
2684
1
  ReadOptions read_options, snapshot_read_options;
2685
1
  string value;
2686
1
  Status s;
2687
2688
1
  Transaction* txn1 = db->BeginTransaction(write_options);
2689
2690
1
  txn1->SetSavePoint();  // 1
2691
2692
1
  s = db->Put(write_options, "T", "1");
2693
1
  ASSERT_OK(s);
2694
2695
1
  txn1->SetSnapshotOnNextOperation();
2696
2697
1
  s = db->Put(write_options, "T", "2");
2698
1
  ASSERT_OK(s);
2699
2700
1
  txn1->SetSavePoint();  // 2
2701
2702
1
  s = db->Put(write_options, "T", "3");
2703
1
  ASSERT_OK(s);
2704
2705
1
  s = txn1->Put("A", "a");
2706
1
  ASSERT_OK(s);
2707
2708
1
  txn1->SetSavePoint();  // 3
2709
2710
1
  s = db->Put(write_options, "T", "4");
2711
1
  ASSERT_OK(s);
2712
2713
1
  txn1->SetSnapshot();
2714
1
  txn1->SetSnapshotOnNextOperation();
2715
2716
1
  txn1->SetSavePoint();  // 4
2717
2718
1
  s = db->Put(write_options, "T", "5");
2719
1
  ASSERT_OK(s);
2720
2721
1
  snapshot_read_options.snapshot = txn1->GetSnapshot();
2722
1
  s = txn1->Get(snapshot_read_options, "T", &value);
2723
1
  ASSERT_OK(s);
2724
1
  ASSERT_EQ("4", value);
2725
2726
1
  s = txn1->Put("A", "a1");
2727
1
  ASSERT_OK(s);
2728
2729
1
  snapshot_read_options.snapshot = txn1->GetSnapshot();
2730
1
  s = txn1->Get(snapshot_read_options, "T", &value);
2731
1
  ASSERT_OK(s);
2732
1
  ASSERT_EQ("5", value);
2733
2734
1
  s = txn1->RollbackToSavePoint();  // Rollback to 4
2735
1
  ASSERT_OK(s);
2736
2737
1
  snapshot_read_options.snapshot = txn1->GetSnapshot();
2738
1
  s = txn1->Get(snapshot_read_options, "T", &value);
2739
1
  ASSERT_OK(s);
2740
1
  ASSERT_EQ("4", value);
2741
2742
1
  s = txn1->RollbackToSavePoint();  // Rollback to 3
2743
1
  ASSERT_OK(s);
2744
2745
1
  snapshot_read_options.snapshot = txn1->GetSnapshot();
2746
1
  s = txn1->Get(snapshot_read_options, "T", &value);
2747
1
  ASSERT_OK(s);
2748
1
  ASSERT_EQ("3", value);
2749
2750
1
  s = txn1->Get(read_options, "T", &value);
2751
1
  ASSERT_OK(s);
2752
1
  ASSERT_EQ("5", value);
2753
2754
1
  s = txn1->RollbackToSavePoint();  // Rollback to 2
2755
1
  ASSERT_OK(s);
2756
2757
1
  snapshot_read_options.snapshot = txn1->GetSnapshot();
2758
1
  ASSERT_FALSE(snapshot_read_options.snapshot);
2759
1
  s = txn1->Get(snapshot_read_options, "T", &value);
2760
1
  ASSERT_OK(s);
2761
1
  ASSERT_EQ("5", value);
2762
2763
1
  s = txn1->Delete("A");
2764
1
  ASSERT_OK(s);
2765
2766
1
  snapshot_read_options.snapshot = txn1->GetSnapshot();
2767
1
  ASSERT_TRUE(snapshot_read_options.snapshot);
2768
1
  s = txn1->Get(snapshot_read_options, "T", &value);
2769
1
  ASSERT_OK(s);
2770
1
  ASSERT_EQ("5", value);
2771
2772
1
  s = txn1->RollbackToSavePoint();  // Rollback to 1
2773
1
  ASSERT_OK(s);
2774
2775
1
  s = txn1->Delete("A");
2776
1
  ASSERT_OK(s);
2777
2778
1
  snapshot_read_options.snapshot = txn1->GetSnapshot();
2779
1
  ASSERT_FALSE(snapshot_read_options.snapshot);
2780
1
  s = txn1->Get(snapshot_read_options, "T", &value);
2781
1
  ASSERT_OK(s);
2782
1
  ASSERT_EQ("5", value);
2783
2784
1
  s = txn1->Commit();
2785
1
  ASSERT_OK(s);
2786
2787
1
  delete txn1;
2788
1
}
2789
2790
1
TEST_F(TransactionTest, SetSnapshotOnNextOperationWithNotification) {
2791
1
  WriteOptions write_options;
2792
1
  ReadOptions read_options;
2793
1
  string value;
2794
2795
1
  class Notifier : public TransactionNotifier {
2796
1
   private:
2797
1
    const Snapshot** snapshot_ptr_;
2798
2799
1
   public:
2800
1
    explicit Notifier(const Snapshot** snapshot_ptr)
2801
1
        : snapshot_ptr_(snapshot_ptr) {}
2802
2803
1
    void SnapshotCreated(const Snapshot* newSnapshot) {
2804
1
      *snapshot_ptr_ = newSnapshot;
2805
1
    }
2806
1
  };
2807
2808
1
  std::shared_ptr<Notifier> notifier =
2809
1
      std::make_shared<Notifier>(&read_options.snapshot);
2810
1
  Status s;
2811
2812
1
  s = db->Put(write_options, "B", "0");
2813
1
  ASSERT_OK(s);
2814
2815
1
  Transaction* txn1 = db->BeginTransaction(write_options);
2816
2817
1
  txn1->SetSnapshotOnNextOperation(notifier);
2818
1
  ASSERT_FALSE(read_options.snapshot);
2819
2820
1
  s = db->Put(write_options, "B", "1");
2821
1
  ASSERT_OK(s);
2822
2823
  // A Get does not generate the snapshot
2824
1
  s = txn1->Get(read_options, "B", &value);
2825
1
  ASSERT_OK(s);
2826
1
  ASSERT_FALSE(read_options.snapshot);
2827
1
  ASSERT_EQ(value, "1");
2828
2829
  // Any other operation does
2830
1
  s = txn1->Put("A", "0");
2831
1
  ASSERT_OK(s);
2832
2833
  // Now change "B".
2834
1
  s = db->Put(write_options, "B", "2");
2835
1
  ASSERT_OK(s);
2836
2837
  // The original value should still be read
2838
1
  s = txn1->Get(read_options, "B", &value);
2839
1
  ASSERT_OK(s);
2840
1
  ASSERT_TRUE(read_options.snapshot);
2841
1
  ASSERT_EQ(value, "1");
2842
2843
1
  s = txn1->Commit();
2844
1
  ASSERT_OK(s);
2845
2846
1
  delete txn1;
2847
1
}
2848
2849
1
TEST_F(TransactionTest, ClearSnapshotTest) {
2850
1
  WriteOptions write_options;
2851
1
  ReadOptions read_options, snapshot_read_options;
2852
1
  string value;
2853
1
  Status s;
2854
2855
1
  s = db->Put(write_options, "foo", "0");
2856
1
  ASSERT_OK(s);
2857
2858
1
  Transaction* txn = db->BeginTransaction(write_options);
2859
1
  ASSERT_TRUE(txn);
2860
2861
1
  s = db->Put(write_options, "foo", "1");
2862
1
  ASSERT_OK(s);
2863
2864
1
  snapshot_read_options.snapshot = txn->GetSnapshot();
2865
1
  ASSERT_FALSE(snapshot_read_options.snapshot);
2866
2867
  // No snapshot created yet
2868
1
  s = txn->Get(snapshot_read_options, "foo", &value);
2869
1
  ASSERT_EQ(value, "1");
2870
2871
1
  txn->SetSnapshot();
2872
1
  snapshot_read_options.snapshot = txn->GetSnapshot();
2873
1
  ASSERT_TRUE(snapshot_read_options.snapshot);
2874
2875
1
  s = db->Put(write_options, "foo", "2");
2876
1
  ASSERT_OK(s);
2877
2878
  // Snapshot was created before change to '2'
2879
1
  s = txn->Get(snapshot_read_options, "foo", &value);
2880
1
  ASSERT_EQ(value, "1");
2881
2882
1
  txn->ClearSnapshot();
2883
1
  snapshot_read_options.snapshot = txn->GetSnapshot();
2884
1
  ASSERT_FALSE(snapshot_read_options.snapshot);
2885
2886
  // Snapshot has now been cleared
2887
1
  s = txn->Get(snapshot_read_options, "foo", &value);
2888
1
  ASSERT_EQ(value, "2");
2889
2890
1
  s = txn->Commit();
2891
1
  ASSERT_OK(s);
2892
2893
1
  delete txn;
2894
1
}
2895
2896
1
TEST_F(TransactionTest, ToggleAutoCompactionTest) {
2897
1
  Status s;
2898
2899
1
  TransactionOptions txn_options;
2900
1
  ColumnFamilyHandle *cfa, *cfb;
2901
1
  ColumnFamilyOptions cf_options;
2902
2903
  // Create 2 new column families
2904
1
  s = db->CreateColumnFamily(cf_options, "CFA", &cfa);
2905
1
  ASSERT_OK(s);
2906
1
  s = db->CreateColumnFamily(cf_options, "CFB", &cfb);
2907
1
  ASSERT_OK(s);
2908
2909
1
  delete cfa;
2910
1
  delete cfb;
2911
1
  delete db;
2912
2913
  // open DB with three column families
2914
1
  std::vector<ColumnFamilyDescriptor> column_families;
2915
  // have to open default column family
2916
1
  column_families.push_back(
2917
1
      ColumnFamilyDescriptor(kDefaultColumnFamilyName, ColumnFamilyOptions()));
2918
  // open the new column families
2919
1
  column_families.push_back(
2920
1
      ColumnFamilyDescriptor("CFA", ColumnFamilyOptions()));
2921
1
  column_families.push_back(
2922
1
      ColumnFamilyDescriptor("CFB", ColumnFamilyOptions()));
2923
2924
1
  ColumnFamilyOptions* cf_opt_default = &column_families[0].options;
2925
1
  ColumnFamilyOptions* cf_opt_cfa = &column_families[1].options;
2926
1
  ColumnFamilyOptions* cf_opt_cfb = &column_families[2].options;
2927
1
  cf_opt_default->disable_auto_compactions = false;
2928
1
  cf_opt_cfa->disable_auto_compactions = true;
2929
1
  cf_opt_cfb->disable_auto_compactions = false;
2930
2931
1
  std::vector<ColumnFamilyHandle*> handles;
2932
2933
1
  s = TransactionDB::Open(options, txn_db_options, dbname, column_families,
2934
1
                          &handles, &db);
2935
1
  ASSERT_OK(s);
2936
2937
1
  auto cfh_default = reinterpret_cast<ColumnFamilyHandleImpl*>(handles[0]);
2938
1
  auto opt_default = *cfh_default->cfd()->GetLatestMutableCFOptions();
2939
2940
1
  auto cfh_a = reinterpret_cast<ColumnFamilyHandleImpl*>(handles[1]);
2941
1
  auto opt_a = *cfh_a->cfd()->GetLatestMutableCFOptions();
2942
2943
1
  auto cfh_b = reinterpret_cast<ColumnFamilyHandleImpl*>(handles[2]);
2944
1
  auto opt_b = *cfh_b->cfd()->GetLatestMutableCFOptions();
2945
2946
1
  ASSERT_EQ(opt_default.disable_auto_compactions, false);
2947
1
  ASSERT_EQ(opt_a.disable_auto_compactions, true);
2948
1
  ASSERT_EQ(opt_b.disable_auto_compactions, false);
2949
2950
3
  for (auto handle : handles) {
2951
3
    delete handle;
2952
3
  }
2953
1
}
2954
2955
1
TEST_F(TransactionTest, ExpiredTransactionDataRace1) {
2956
  // In this test, txn1 should succeed committing,
2957
  // as the callback is called after txn1 starts committing.
2958
1
  rocksdb::SyncPoint::GetInstance()->LoadDependency(
2959
1
      {{"TransactionTest::ExpirableTransactionDataRace:1"}});
2960
1
  rocksdb::SyncPoint::GetInstance()->SetCallBack(
2961
1
      "TransactionTest::ExpirableTransactionDataRace:1", [&](void* arg) {
2962
1
        WriteOptions write_options;
2963
1
        TransactionOptions txn_options;
2964
2965
        // Force txn1 to expire
2966
        /* sleep override */
2967
1
        std::this_thread::sleep_for(std::chrono::milliseconds(150));
2968
2969
1
        Transaction* txn2 = db->BeginTransaction(write_options, txn_options);
2970
1
        Status s;
2971
1
        s = txn2->Put("X", "2");
2972
1
        ASSERT_TRUE(s.IsTimedOut());
2973
1
        s = txn2->Commit();
2974
1
        ASSERT_OK(s);
2975
1
        delete txn2;
2976
1
      });
2977
2978
1
  rocksdb::SyncPoint::GetInstance()->EnableProcessing();
2979
2980
1
  WriteOptions write_options;
2981
1
  TransactionOptions txn_options;
2982
2983
1
  txn_options.expiration = 100;
2984
1
  Transaction* txn1 = db->BeginTransaction(write_options, txn_options);
2985
2986
1
  Status s;
2987
1
  s = txn1->Put("X", "1");
2988
1
  ASSERT_OK(s);
2989
1
  s = txn1->Commit();
2990
1
  ASSERT_OK(s);
2991
2992
1
  ReadOptions read_options;
2993
1
  string value;
2994
1
  s = db->Get(read_options, "X", &value);
2995
1
  ASSERT_EQ("1", value);
2996
2997
1
  delete txn1;
2998
1
  rocksdb::SyncPoint::GetInstance()->DisableProcessing();
2999
1
}
3000
3001
}  // namespace rocksdb
3002
3003
13.2k
int main(int argc, char** argv) {
3004
13.2k
  ::testing::InitGoogleTest(&argc, argv);
3005
13.2k
  return RUN_ALL_TESTS();
3006
13.2k
}
3007
3008
#else
3009
#include <stdio.h>
3010
3011
int main(int argc, char** argv) {
3012
  fprintf(stderr,
3013
          "SKIPPED as Transactions are not supported in ROCKSDB_LITE\n");
3014
  return 0;
3015
}
3016
3017
#endif  // ROCKSDB_LITE