blob_db_test.cc 38.1 KB
Newer Older
A
Anirban Rahut 已提交
1
//  Copyright (c) 2011-present, Facebook, Inc.  All rights reserved.
S
Siying Dong 已提交
2 3 4
//  This source code is licensed under both the GPLv2 (found in the
//  COPYING file in the root directory) and Apache 2.0 License
//  (found in the LICENSE.Apache file in the root directory).
Y
Yi Wu 已提交
5

6 7
#ifndef ROCKSDB_LITE

Y
Yi Wu 已提交
8
#include <algorithm>
A
Anirban Rahut 已提交
9
#include <cstdlib>
Y
Yi Wu 已提交
10 11 12
#include <map>
#include <memory>
#include <string>
Y
Yi Wu 已提交
13 14
#include <vector>

A
Anirban Rahut 已提交
15
#include "db/db_test_util.h"
Y
Yi Wu 已提交
16
#include "port/port.h"
Y
Yi Wu 已提交
17
#include "rocksdb/utilities/debug.h"
18
#include "util/cast_util.h"
19
#include "util/random.h"
Y
Yi Wu 已提交
20
#include "util/string_util.h"
21
#include "util/sync_point.h"
22
#include "util/testharness.h"
Y
Yi Wu 已提交
23
#include "utilities/blob_db/blob_db.h"
Y
Yi Wu 已提交
24
#include "utilities/blob_db/blob_db_impl.h"
Y
Yi Wu 已提交
25
#include "utilities/blob_db/blob_index.h"
26 27

namespace rocksdb {
A
Anirban Rahut 已提交
28 29
namespace blob_db {

30 31
class BlobDBTest : public testing::Test {
 public:
Y
Yi Wu 已提交
32 33
  const int kMaxBlobSize = 1 << 14;

Y
Yi Wu 已提交
34 35 36 37 38 39
  struct BlobRecord {
    std::string key;
    std::string value;
    uint64_t expiration = 0;
  };

Y
Yi Wu 已提交
40 41
  BlobDBTest()
      : dbname_(test::TmpDir() + "/blob_db_test"),
Y
Yi Wu 已提交
42
        mock_env_(new MockTimeEnv(Env::Default())),
Y
Yi Wu 已提交
43
        blob_db_(nullptr) {
Y
Yi Wu 已提交
44 45
    Status s = DestroyBlobDB(dbname_, Options(), BlobDBOptions());
    assert(s.ok());
A
Anirban Rahut 已提交
46 47
  }

Y
Yi Wu 已提交
48 49
  ~BlobDBTest() { Destroy(); }

Y
Yi Wu 已提交
50
  void Open(BlobDBOptions bdb_options = BlobDBOptions(),
Y
Yi Wu 已提交
51 52 53 54
            Options options = Options()) {
    options.create_if_missing = true;
    ASSERT_OK(BlobDB::Open(options, bdb_options, dbname_, &blob_db_));
  }
A
Anirban Rahut 已提交
55

Y
Yi Wu 已提交
56 57 58 59 60 61 62 63
  void Destroy() {
    if (blob_db_) {
      Options options = blob_db_->GetOptions();
      BlobDBOptions bdb_options = blob_db_->GetBlobDBOptions();
      delete blob_db_;
      ASSERT_OK(DestroyBlobDB(dbname_, options, bdb_options));
      blob_db_ = nullptr;
    }
A
Anirban Rahut 已提交
64 65
  }

Y
Yi Wu 已提交
66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81
  BlobDBImpl *blob_db_impl() {
    return reinterpret_cast<BlobDBImpl *>(blob_db_);
  }

  Status Put(const Slice &key, const Slice &value) {
    return blob_db_->Put(WriteOptions(), key, value);
  }

  void Delete(const std::string &key,
              std::map<std::string, std::string> *data = nullptr) {
    ASSERT_OK(blob_db_->Delete(WriteOptions(), key));
    if (data != nullptr) {
      data->erase(key);
    }
  }

82
  void PutRandomWithTTL(const std::string &key, uint64_t ttl, Random *rnd,
Y
Yi Wu 已提交
83 84 85
                        std::map<std::string, std::string> *data = nullptr) {
    int len = rnd->Next() % kMaxBlobSize + 1;
    std::string value = test::RandomHumanReadableString(rnd, len);
Y
Yi Wu 已提交
86 87
    ASSERT_OK(
        blob_db_->PutWithTTL(WriteOptions(), Slice(key), Slice(value), ttl));
Y
Yi Wu 已提交
88 89 90 91
    if (data != nullptr) {
      (*data)[key] = value;
    }
  }
A
Anirban Rahut 已提交
92

93
  void PutRandomUntil(const std::string &key, uint64_t expiration, Random *rnd,
Y
Yi Wu 已提交
94 95 96 97 98 99 100 101 102 103
                      std::map<std::string, std::string> *data = nullptr) {
    int len = rnd->Next() % kMaxBlobSize + 1;
    std::string value = test::RandomHumanReadableString(rnd, len);
    ASSERT_OK(blob_db_->PutUntil(WriteOptions(), Slice(key), Slice(value),
                                 expiration));
    if (data != nullptr) {
      (*data)[key] = value;
    }
  }

Y
Yi Wu 已提交
104 105
  void PutRandom(const std::string &key, Random *rnd,
                 std::map<std::string, std::string> *data = nullptr) {
106 107 108 109 110
    PutRandom(blob_db_, key, rnd, data);
  }

  void PutRandom(DB *db, const std::string &key, Random *rnd,
                 std::map<std::string, std::string> *data = nullptr) {
Y
Yi Wu 已提交
111 112
    int len = rnd->Next() % kMaxBlobSize + 1;
    std::string value = test::RandomHumanReadableString(rnd, len);
113
    ASSERT_OK(db->Put(WriteOptions(), Slice(key), Slice(value)));
Y
Yi Wu 已提交
114 115 116
    if (data != nullptr) {
      (*data)[key] = value;
    }
Y
Yi Wu 已提交
117
  }
A
Anirban Rahut 已提交
118

Y
Yi Wu 已提交
119 120 121 122 123 124 125 126 127 128 129
  void PutRandomToWriteBatch(
      const std::string &key, Random *rnd, WriteBatch *batch,
      std::map<std::string, std::string> *data = nullptr) {
    int len = rnd->Next() % kMaxBlobSize + 1;
    std::string value = test::RandomHumanReadableString(rnd, len);
    ASSERT_OK(batch->Put(key, value));
    if (data != nullptr) {
      (*data)[key] = value;
    }
  }

Y
Yi Wu 已提交
130 131
  // Verify blob db contain expected data and nothing more.
  void VerifyDB(const std::map<std::string, std::string> &data) {
132 133 134 135 136
    VerifyDB(blob_db_, data);
  }

  void VerifyDB(DB *db, const std::map<std::string, std::string> &data) {
    Iterator *iter = db->NewIterator(ReadOptions());
Y
Yi Wu 已提交
137 138 139 140 141 142 143 144 145 146 147
    iter->SeekToFirst();
    for (auto &p : data) {
      ASSERT_TRUE(iter->Valid());
      ASSERT_EQ(p.first, iter->key().ToString());
      ASSERT_EQ(p.second, iter->value().ToString());
      iter->Next();
    }
    ASSERT_FALSE(iter->Valid());
    ASSERT_OK(iter->status());
    delete iter;
  }
A
Anirban Rahut 已提交
148

Y
Yi Wu 已提交
149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174
  void VerifyBaseDB(
      const std::map<std::string, KeyVersion> &expected_versions) {
    auto *bdb_impl = static_cast<BlobDBImpl *>(blob_db_);
    DB *db = blob_db_->GetRootDB();
    std::vector<KeyVersion> versions;
    GetAllKeyVersions(db, "", "", &versions);
    ASSERT_EQ(expected_versions.size(), versions.size());
    size_t i = 0;
    for (auto &key_version : expected_versions) {
      const KeyVersion &expected_version = key_version.second;
      ASSERT_EQ(expected_version.user_key, versions[i].user_key);
      ASSERT_EQ(expected_version.sequence, versions[i].sequence);
      ASSERT_EQ(expected_version.type, versions[i].type);
      if (versions[i].type == kTypeValue) {
        ASSERT_EQ(expected_version.value, versions[i].value);
      } else {
        ASSERT_EQ(kTypeBlobIndex, versions[i].type);
        PinnableSlice value;
        ASSERT_OK(bdb_impl->TEST_GetBlobValue(versions[i].user_key,
                                              versions[i].value, &value));
        ASSERT_EQ(expected_version.value, value.ToString());
      }
      i++;
    }
  }

Y
Yi Wu 已提交
175 176 177
  void InsertBlobs() {
    WriteOptions wo;
    std::string value;
A
Anirban Rahut 已提交
178

Y
Yi Wu 已提交
179 180
    Random rnd(301);
    for (size_t i = 0; i < 100000; i++) {
181
      uint64_t ttl = rnd.Next() % 86400;
Y
Yi Wu 已提交
182
      PutRandomWithTTL("key" + ToString(i % 500), ttl, &rnd, nullptr);
A
Anirban Rahut 已提交
183 184 185
    }

    for (size_t i = 0; i < 10; i++) {
Y
Yi Wu 已提交
186
      Delete("key" + ToString(i % 500));
A
Anirban Rahut 已提交
187 188
    }
  }
189

Y
Yi Wu 已提交
190
  const std::string dbname_;
Y
Yi Wu 已提交
191
  std::unique_ptr<MockTimeEnv> mock_env_;
Y
Yi Wu 已提交
192
  std::shared_ptr<TTLExtractor> ttl_extractor_;
Y
Yi Wu 已提交
193
  BlobDB *blob_db_;
194 195
};  // class BlobDBTest

Y
Yi Wu 已提交
196
TEST_F(BlobDBTest, Put) {
A
Anirban Rahut 已提交
197
  Random rnd(301);
Y
Yi Wu 已提交
198
  BlobDBOptions bdb_options;
Y
Yi Wu 已提交
199
  bdb_options.min_blob_size = 0;
Y
Yi Wu 已提交
200 201 202
  bdb_options.disable_background_tasks = true;
  Open(bdb_options);
  std::map<std::string, std::string> data;
A
Anirban Rahut 已提交
203
  for (size_t i = 0; i < 100; i++) {
Y
Yi Wu 已提交
204
    PutRandom("key" + ToString(i), &rnd, &data);
A
Anirban Rahut 已提交
205
  }
Y
Yi Wu 已提交
206
  VerifyDB(data);
207 208
}

Y
Yi Wu 已提交
209 210 211 212
TEST_F(BlobDBTest, PutWithTTL) {
  Random rnd(301);
  Options options;
  options.env = mock_env_.get();
Y
Yi Wu 已提交
213
  BlobDBOptions bdb_options;
Y
Yi Wu 已提交
214
  bdb_options.ttl_range_secs = 1000;
Y
Yi Wu 已提交
215
  bdb_options.min_blob_size = 0;
Y
Yi Wu 已提交
216 217 218 219
  bdb_options.blob_file_size = 256 * 1000 * 1000;
  bdb_options.disable_background_tasks = true;
  Open(bdb_options, options);
  std::map<std::string, std::string> data;
Y
Yi Wu 已提交
220
  mock_env_->set_current_time(50);
Y
Yi Wu 已提交
221
  for (size_t i = 0; i < 100; i++) {
222
    uint64_t ttl = rnd.Next() % 100;
Y
Yi Wu 已提交
223
    PutRandomWithTTL("key" + ToString(i), ttl, &rnd,
224
                     (ttl <= 50 ? nullptr : &data));
Y
Yi Wu 已提交
225
  }
Y
Yi Wu 已提交
226
  mock_env_->set_current_time(100);
Y
Yi Wu 已提交
227 228 229 230
  auto *bdb_impl = static_cast<BlobDBImpl *>(blob_db_);
  auto blob_files = bdb_impl->TEST_GetBlobFiles();
  ASSERT_EQ(1, blob_files.size());
  ASSERT_TRUE(blob_files[0]->HasTTL());
Y
Yi Wu 已提交
231
  ASSERT_OK(bdb_impl->TEST_CloseBlobFile(blob_files[0]));
Y
Yi Wu 已提交
232 233 234
  GCStats gc_stats;
  ASSERT_OK(bdb_impl->TEST_GCFileAndUpdateLSM(blob_files[0], &gc_stats));
  ASSERT_EQ(100 - data.size(), gc_stats.num_deletes);
235
  ASSERT_EQ(data.size(), gc_stats.num_relocate);
Y
Yi Wu 已提交
236 237 238 239 240 241 242
  VerifyDB(data);
}

TEST_F(BlobDBTest, PutUntil) {
  Random rnd(301);
  Options options;
  options.env = mock_env_.get();
Y
Yi Wu 已提交
243
  BlobDBOptions bdb_options;
Y
Yi Wu 已提交
244
  bdb_options.ttl_range_secs = 1000;
Y
Yi Wu 已提交
245
  bdb_options.min_blob_size = 0;
Y
Yi Wu 已提交
246 247 248 249
  bdb_options.blob_file_size = 256 * 1000 * 1000;
  bdb_options.disable_background_tasks = true;
  Open(bdb_options, options);
  std::map<std::string, std::string> data;
Y
Yi Wu 已提交
250
  mock_env_->set_current_time(50);
Y
Yi Wu 已提交
251
  for (size_t i = 0; i < 100; i++) {
252
    uint64_t expiration = rnd.Next() % 100 + 50;
Y
Yi Wu 已提交
253
    PutRandomUntil("key" + ToString(i), expiration, &rnd,
254
                   (expiration <= 100 ? nullptr : &data));
Y
Yi Wu 已提交
255
  }
Y
Yi Wu 已提交
256
  mock_env_->set_current_time(100);
Y
Yi Wu 已提交
257 258 259 260
  auto *bdb_impl = static_cast<BlobDBImpl *>(blob_db_);
  auto blob_files = bdb_impl->TEST_GetBlobFiles();
  ASSERT_EQ(1, blob_files.size());
  ASSERT_TRUE(blob_files[0]->HasTTL());
Y
Yi Wu 已提交
261
  ASSERT_OK(bdb_impl->TEST_CloseBlobFile(blob_files[0]));
Y
Yi Wu 已提交
262 263 264
  GCStats gc_stats;
  ASSERT_OK(bdb_impl->TEST_GCFileAndUpdateLSM(blob_files[0], &gc_stats));
  ASSERT_EQ(100 - data.size(), gc_stats.num_deletes);
265
  ASSERT_EQ(data.size(), gc_stats.num_relocate);
Y
Yi Wu 已提交
266 267 268 269 270 271 272 273 274
  VerifyDB(data);
}

TEST_F(BlobDBTest, TTLExtrator_NoTTL) {
  // The default ttl extractor return no ttl for every key.
  ttl_extractor_.reset(new TTLExtractor());
  Random rnd(301);
  Options options;
  options.env = mock_env_.get();
Y
Yi Wu 已提交
275
  BlobDBOptions bdb_options;
Y
Yi Wu 已提交
276
  bdb_options.ttl_range_secs = 1000;
Y
Yi Wu 已提交
277
  bdb_options.min_blob_size = 0;
Y
Yi Wu 已提交
278 279 280 281 282
  bdb_options.blob_file_size = 256 * 1000 * 1000;
  bdb_options.ttl_extractor = ttl_extractor_;
  bdb_options.disable_background_tasks = true;
  Open(bdb_options, options);
  std::map<std::string, std::string> data;
Y
Yi Wu 已提交
283
  mock_env_->set_current_time(0);
Y
Yi Wu 已提交
284 285 286 287
  for (size_t i = 0; i < 100; i++) {
    PutRandom("key" + ToString(i), &rnd, &data);
  }
  // very far in the future..
Y
Yi Wu 已提交
288 289
  mock_env_->set_current_time(std::numeric_limits<uint64_t>::max() / 1000000 -
                              10);
Y
Yi Wu 已提交
290 291 292 293
  auto *bdb_impl = static_cast<BlobDBImpl *>(blob_db_);
  auto blob_files = bdb_impl->TEST_GetBlobFiles();
  ASSERT_EQ(1, blob_files.size());
  ASSERT_FALSE(blob_files[0]->HasTTL());
Y
Yi Wu 已提交
294
  ASSERT_OK(bdb_impl->TEST_CloseBlobFile(blob_files[0]));
Y
Yi Wu 已提交
295 296 297
  GCStats gc_stats;
  ASSERT_OK(bdb_impl->TEST_GCFileAndUpdateLSM(blob_files[0], &gc_stats));
  ASSERT_EQ(0, gc_stats.num_deletes);
298
  ASSERT_EQ(100, gc_stats.num_relocate);
Y
Yi Wu 已提交
299 300 301 302 303 304 305 306 307 308 309 310 311
  VerifyDB(data);
}

TEST_F(BlobDBTest, TTLExtractor_ExtractTTL) {
  Random rnd(301);
  class TestTTLExtractor : public TTLExtractor {
   public:
    explicit TestTTLExtractor(Random *r) : rnd(r) {}

    virtual bool ExtractTTL(const Slice &key, const Slice &value, uint64_t *ttl,
                            std::string * /*new_value*/,
                            bool * /*value_changed*/) override {
      *ttl = rnd->Next() % 100;
312
      if (*ttl > 50) {
Y
Yi Wu 已提交
313 314 315 316 317 318 319 320 321 322 323
        data[key.ToString()] = value.ToString();
      }
      return true;
    }

    Random *rnd;
    std::map<std::string, std::string> data;
  };
  ttl_extractor_.reset(new TestTTLExtractor(&rnd));
  Options options;
  options.env = mock_env_.get();
Y
Yi Wu 已提交
324
  BlobDBOptions bdb_options;
Y
Yi Wu 已提交
325
  bdb_options.ttl_range_secs = 1000;
Y
Yi Wu 已提交
326
  bdb_options.min_blob_size = 0;
Y
Yi Wu 已提交
327 328 329 330
  bdb_options.blob_file_size = 256 * 1000 * 1000;
  bdb_options.ttl_extractor = ttl_extractor_;
  bdb_options.disable_background_tasks = true;
  Open(bdb_options, options);
Y
Yi Wu 已提交
331
  mock_env_->set_current_time(50);
Y
Yi Wu 已提交
332 333 334
  for (size_t i = 0; i < 100; i++) {
    PutRandom("key" + ToString(i), &rnd);
  }
Y
Yi Wu 已提交
335
  mock_env_->set_current_time(100);
Y
Yi Wu 已提交
336 337 338 339
  auto *bdb_impl = static_cast<BlobDBImpl *>(blob_db_);
  auto blob_files = bdb_impl->TEST_GetBlobFiles();
  ASSERT_EQ(1, blob_files.size());
  ASSERT_TRUE(blob_files[0]->HasTTL());
Y
Yi Wu 已提交
340
  ASSERT_OK(bdb_impl->TEST_CloseBlobFile(blob_files[0]));
Y
Yi Wu 已提交
341 342 343 344
  GCStats gc_stats;
  ASSERT_OK(bdb_impl->TEST_GCFileAndUpdateLSM(blob_files[0], &gc_stats));
  auto &data = static_cast<TestTTLExtractor *>(ttl_extractor_.get())->data;
  ASSERT_EQ(100 - data.size(), gc_stats.num_deletes);
345
  ASSERT_EQ(data.size(), gc_stats.num_relocate);
Y
Yi Wu 已提交
346 347 348 349 350 351 352 353 354 355 356 357 358 359
  VerifyDB(data);
}

TEST_F(BlobDBTest, TTLExtractor_ExtractExpiration) {
  Random rnd(301);
  class TestTTLExtractor : public TTLExtractor {
   public:
    explicit TestTTLExtractor(Random *r) : rnd(r) {}

    virtual bool ExtractExpiration(const Slice &key, const Slice &value,
                                   uint64_t /*now*/, uint64_t *expiration,
                                   std::string * /*new_value*/,
                                   bool * /*value_changed*/) override {
      *expiration = rnd->Next() % 100 + 50;
360
      if (*expiration > 100) {
Y
Yi Wu 已提交
361 362 363 364 365 366 367 368 369 370 371
        data[key.ToString()] = value.ToString();
      }
      return true;
    }

    Random *rnd;
    std::map<std::string, std::string> data;
  };
  ttl_extractor_.reset(new TestTTLExtractor(&rnd));
  Options options;
  options.env = mock_env_.get();
Y
Yi Wu 已提交
372
  BlobDBOptions bdb_options;
Y
Yi Wu 已提交
373
  bdb_options.ttl_range_secs = 1000;
Y
Yi Wu 已提交
374
  bdb_options.min_blob_size = 0;
Y
Yi Wu 已提交
375 376 377 378
  bdb_options.blob_file_size = 256 * 1000 * 1000;
  bdb_options.ttl_extractor = ttl_extractor_;
  bdb_options.disable_background_tasks = true;
  Open(bdb_options, options);
Y
Yi Wu 已提交
379
  mock_env_->set_current_time(50);
Y
Yi Wu 已提交
380 381 382
  for (size_t i = 0; i < 100; i++) {
    PutRandom("key" + ToString(i), &rnd);
  }
Y
Yi Wu 已提交
383
  mock_env_->set_current_time(100);
Y
Yi Wu 已提交
384 385 386 387
  auto *bdb_impl = static_cast<BlobDBImpl *>(blob_db_);
  auto blob_files = bdb_impl->TEST_GetBlobFiles();
  ASSERT_EQ(1, blob_files.size());
  ASSERT_TRUE(blob_files[0]->HasTTL());
Y
Yi Wu 已提交
388
  ASSERT_OK(bdb_impl->TEST_CloseBlobFile(blob_files[0]));
Y
Yi Wu 已提交
389 390 391 392
  GCStats gc_stats;
  ASSERT_OK(bdb_impl->TEST_GCFileAndUpdateLSM(blob_files[0], &gc_stats));
  auto &data = static_cast<TestTTLExtractor *>(ttl_extractor_.get())->data;
  ASSERT_EQ(100 - data.size(), gc_stats.num_deletes);
393
  ASSERT_EQ(data.size(), gc_stats.num_relocate);
Y
Yi Wu 已提交
394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419
  VerifyDB(data);
}

TEST_F(BlobDBTest, TTLExtractor_ChangeValue) {
  class TestTTLExtractor : public TTLExtractor {
   public:
    const Slice kTTLSuffix = Slice("ttl:");

    bool ExtractTTL(const Slice & /*key*/, const Slice &value, uint64_t *ttl,
                    std::string *new_value, bool *value_changed) override {
      if (value.size() < 12) {
        return false;
      }
      const char *p = value.data() + value.size() - 12;
      if (kTTLSuffix != Slice(p, 4)) {
        return false;
      }
      *ttl = DecodeFixed64(p + 4);
      *new_value = Slice(value.data(), value.size() - 12).ToString();
      *value_changed = true;
      return true;
    }
  };
  Random rnd(301);
  Options options;
  options.env = mock_env_.get();
Y
Yi Wu 已提交
420
  BlobDBOptions bdb_options;
Y
Yi Wu 已提交
421
  bdb_options.ttl_range_secs = 1000;
Y
Yi Wu 已提交
422
  bdb_options.min_blob_size = 0;
Y
Yi Wu 已提交
423 424 425 426 427
  bdb_options.blob_file_size = 256 * 1000 * 1000;
  bdb_options.ttl_extractor = std::make_shared<TestTTLExtractor>();
  bdb_options.disable_background_tasks = true;
  Open(bdb_options, options);
  std::map<std::string, std::string> data;
Y
Yi Wu 已提交
428
  mock_env_->set_current_time(50);
Y
Yi Wu 已提交
429 430 431 432 433 434 435 436
  for (size_t i = 0; i < 100; i++) {
    int len = rnd.Next() % kMaxBlobSize + 1;
    std::string key = "key" + ToString(i);
    std::string value = test::RandomHumanReadableString(&rnd, len);
    uint64_t ttl = rnd.Next() % 100;
    std::string value_ttl = value + "ttl:";
    PutFixed64(&value_ttl, ttl);
    ASSERT_OK(blob_db_->Put(WriteOptions(), Slice(key), Slice(value_ttl)));
437
    if (ttl > 50) {
Y
Yi Wu 已提交
438 439 440
      data[key] = value;
    }
  }
Y
Yi Wu 已提交
441
  mock_env_->set_current_time(100);
Y
Yi Wu 已提交
442 443 444 445
  auto *bdb_impl = static_cast<BlobDBImpl *>(blob_db_);
  auto blob_files = bdb_impl->TEST_GetBlobFiles();
  ASSERT_EQ(1, blob_files.size());
  ASSERT_TRUE(blob_files[0]->HasTTL());
Y
Yi Wu 已提交
446
  ASSERT_OK(bdb_impl->TEST_CloseBlobFile(blob_files[0]));
Y
Yi Wu 已提交
447 448 449
  GCStats gc_stats;
  ASSERT_OK(bdb_impl->TEST_GCFileAndUpdateLSM(blob_files[0], &gc_stats));
  ASSERT_EQ(100 - data.size(), gc_stats.num_deletes);
450
  ASSERT_EQ(data.size(), gc_stats.num_relocate);
Y
Yi Wu 已提交
451 452 453
  VerifyDB(data);
}

454 455
TEST_F(BlobDBTest, StackableDBGet) {
  Random rnd(301);
Y
Yi Wu 已提交
456
  BlobDBOptions bdb_options;
Y
Yi Wu 已提交
457
  bdb_options.min_blob_size = 0;
458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476
  bdb_options.disable_background_tasks = true;
  Open(bdb_options);
  std::map<std::string, std::string> data;
  for (size_t i = 0; i < 100; i++) {
    PutRandom("key" + ToString(i), &rnd, &data);
  }
  for (size_t i = 0; i < 100; i++) {
    StackableDB *db = blob_db_;
    ColumnFamilyHandle *column_family = db->DefaultColumnFamily();
    std::string key = "key" + ToString(i);
    PinnableSlice pinnable_value;
    ASSERT_OK(db->Get(ReadOptions(), column_family, key, &pinnable_value));
    std::string string_value;
    ASSERT_OK(db->Get(ReadOptions(), column_family, key, &string_value));
    ASSERT_EQ(string_value, pinnable_value.ToString());
    ASSERT_EQ(string_value, data[key]);
  }
}

Y
Yi Wu 已提交
477
TEST_F(BlobDBTest, WriteBatch) {
A
Anirban Rahut 已提交
478
  Random rnd(301);
Y
Yi Wu 已提交
479
  BlobDBOptions bdb_options;
Y
Yi Wu 已提交
480
  bdb_options.min_blob_size = 0;
Y
Yi Wu 已提交
481 482
  bdb_options.disable_background_tasks = true;
  Open(bdb_options);
Y
Yi Wu 已提交
483
  std::map<std::string, std::string> data;
Y
Yi Wu 已提交
484 485 486 487 488 489
  for (size_t i = 0; i < 100; i++) {
    WriteBatch batch;
    for (size_t j = 0; j < 10; j++) {
      PutRandomToWriteBatch("key" + ToString(j * 100 + i), &rnd, &batch, &data);
    }
    blob_db_->Write(WriteOptions(), &batch);
A
Anirban Rahut 已提交
490
  }
Y
Yi Wu 已提交
491
  VerifyDB(data);
A
Anirban Rahut 已提交
492 493
}

Y
Yi Wu 已提交
494
TEST_F(BlobDBTest, Delete) {
A
Anirban Rahut 已提交
495
  Random rnd(301);
Y
Yi Wu 已提交
496
  BlobDBOptions bdb_options;
Y
Yi Wu 已提交
497
  bdb_options.min_blob_size = 0;
Y
Yi Wu 已提交
498 499
  bdb_options.disable_background_tasks = true;
  Open(bdb_options);
Y
Yi Wu 已提交
500
  std::map<std::string, std::string> data;
A
Anirban Rahut 已提交
501
  for (size_t i = 0; i < 100; i++) {
Y
Yi Wu 已提交
502
    PutRandom("key" + ToString(i), &rnd, &data);
A
Anirban Rahut 已提交
503 504
  }
  for (size_t i = 0; i < 100; i += 5) {
Y
Yi Wu 已提交
505
    Delete("key" + ToString(i), &data);
A
Anirban Rahut 已提交
506
  }
Y
Yi Wu 已提交
507
  VerifyDB(data);
A
Anirban Rahut 已提交
508 509
}

Y
Yi Wu 已提交
510
TEST_F(BlobDBTest, DeleteBatch) {
A
Anirban Rahut 已提交
511
  Random rnd(301);
Y
Yi Wu 已提交
512
  BlobDBOptions bdb_options;
Y
Yi Wu 已提交
513
  bdb_options.min_blob_size = 0;
Y
Yi Wu 已提交
514 515
  bdb_options.disable_background_tasks = true;
  Open(bdb_options);
A
Anirban Rahut 已提交
516
  for (size_t i = 0; i < 100; i++) {
Y
Yi Wu 已提交
517
    PutRandom("key" + ToString(i), &rnd);
A
Anirban Rahut 已提交
518
  }
Y
Yi Wu 已提交
519
  WriteBatch batch;
A
Anirban Rahut 已提交
520
  for (size_t i = 0; i < 100; i++) {
Y
Yi Wu 已提交
521
    batch.Delete("key" + ToString(i));
A
Anirban Rahut 已提交
522
  }
Y
Yi Wu 已提交
523 524 525
  ASSERT_OK(blob_db_->Write(WriteOptions(), &batch));
  // DB should be empty.
  VerifyDB({});
A
Anirban Rahut 已提交
526 527
}

Y
Yi Wu 已提交
528
TEST_F(BlobDBTest, Override) {
A
Anirban Rahut 已提交
529
  Random rnd(301);
Y
Yi Wu 已提交
530
  BlobDBOptions bdb_options;
Y
Yi Wu 已提交
531
  bdb_options.min_blob_size = 0;
Y
Yi Wu 已提交
532 533 534 535 536
  bdb_options.disable_background_tasks = true;
  Open(bdb_options);
  std::map<std::string, std::string> data;
  for (int i = 0; i < 10000; i++) {
    PutRandom("key" + ToString(i), &rnd, nullptr);
A
Anirban Rahut 已提交
537
  }
Y
Yi Wu 已提交
538 539 540 541 542
  // override all the keys
  for (int i = 0; i < 10000; i++) {
    PutRandom("key" + ToString(i), &rnd, &data);
  }
  VerifyDB(data);
A
Anirban Rahut 已提交
543 544
}

Y
Yi Wu 已提交
545
#ifdef SNAPPY
Y
Yi Wu 已提交
546
TEST_F(BlobDBTest, Compression) {
A
Anirban Rahut 已提交
547
  Random rnd(301);
Y
Yi Wu 已提交
548
  BlobDBOptions bdb_options;
Y
Yi Wu 已提交
549
  bdb_options.min_blob_size = 0;
Y
Yi Wu 已提交
550 551 552 553
  bdb_options.disable_background_tasks = true;
  bdb_options.compression = CompressionType::kSnappyCompression;
  Open(bdb_options);
  std::map<std::string, std::string> data;
A
Anirban Rahut 已提交
554
  for (size_t i = 0; i < 100; i++) {
Y
Yi Wu 已提交
555
    PutRandom("put-key" + ToString(i), &rnd, &data);
A
Anirban Rahut 已提交
556
  }
Y
Yi Wu 已提交
557 558 559 560 561 562 563 564 565
  for (int i = 0; i < 100; i++) {
    WriteBatch batch;
    for (size_t j = 0; j < 10; j++) {
      PutRandomToWriteBatch("write-batch-key" + ToString(j * 100 + i), &rnd,
                            &batch, &data);
    }
    blob_db_->Write(WriteOptions(), &batch);
  }
  VerifyDB(data);
A
Anirban Rahut 已提交
566
}
Y
Yi Wu 已提交
567
#endif
A
Anirban Rahut 已提交
568

Y
Yi Wu 已提交
569 570
TEST_F(BlobDBTest, MultipleWriters) {
  Open(BlobDBOptions());
A
Anirban Rahut 已提交
571

572
  std::vector<port::Thread> workers;
Y
Yi Wu 已提交
573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591
  std::vector<std::map<std::string, std::string>> data_set(10);
  for (uint32_t i = 0; i < 10; i++)
    workers.push_back(port::Thread(
        [&](uint32_t id) {
          Random rnd(301 + id);
          for (int j = 0; j < 100; j++) {
            std::string key = "key" + ToString(id) + "_" + ToString(j);
            if (id < 5) {
              PutRandom(key, &rnd, &data_set[id]);
            } else {
              WriteBatch batch;
              PutRandomToWriteBatch(key, &rnd, &batch, &data_set[id]);
              blob_db_->Write(WriteOptions(), &batch);
            }
          }
        },
        i));
  std::map<std::string, std::string> data;
  for (size_t i = 0; i < 10; i++) {
592
    workers[i].join();
Y
Yi Wu 已提交
593
    data.insert(data_set[i].begin(), data_set[i].end());
A
Anirban Rahut 已提交
594
  }
Y
Yi Wu 已提交
595
  VerifyDB(data);
596 597
}

598
TEST_F(BlobDBTest, GCAfterOverwriteKeys) {
599 600
  Random rnd(301);
  BlobDBOptions bdb_options;
Y
Yi Wu 已提交
601
  bdb_options.min_blob_size = 0;
602 603 604 605 606 607 608
  bdb_options.disable_background_tasks = true;
  Open(bdb_options);
  DBImpl *db_impl = static_cast_with_check<DBImpl, DB>(blob_db_->GetBaseDB());
  std::map<std::string, std::string> data;
  for (int i = 0; i < 200; i++) {
    PutRandom("key" + ToString(i), &rnd, &data);
  }
Y
Yi Wu 已提交
609
  auto blob_files = blob_db_impl()->TEST_GetBlobFiles();
610
  ASSERT_EQ(1, blob_files.size());
Y
Yi Wu 已提交
611
  ASSERT_OK(blob_db_impl()->TEST_CloseBlobFile(blob_files[0]));
612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628
  // Test for data in SST
  size_t new_keys = 0;
  for (int i = 0; i < 100; i++) {
    if (rnd.Next() % 2 == 1) {
      new_keys++;
      PutRandom("key" + ToString(i), &rnd, &data);
    }
  }
  db_impl->TEST_FlushMemTable(true /*wait*/);
  // Test for data in memtable
  for (int i = 100; i < 200; i++) {
    if (rnd.Next() % 2 == 1) {
      new_keys++;
      PutRandom("key" + ToString(i), &rnd, &data);
    }
  }
  GCStats gc_stats;
Y
Yi Wu 已提交
629
  ASSERT_OK(blob_db_impl()->TEST_GCFileAndUpdateLSM(blob_files[0], &gc_stats));
630
  ASSERT_EQ(200, gc_stats.blob_count);
631
  ASSERT_EQ(0, gc_stats.num_deletes);
632
  ASSERT_EQ(200 - new_keys, gc_stats.num_relocate);
633 634 635
  VerifyDB(data);
}

F
follitude 已提交
636
TEST_F(BlobDBTest, GCRelocateKeyWhileOverwriting) {
637 638
  Random rnd(301);
  BlobDBOptions bdb_options;
Y
Yi Wu 已提交
639
  bdb_options.min_blob_size = 0;
640 641 642
  bdb_options.disable_background_tasks = true;
  Open(bdb_options);
  ASSERT_OK(blob_db_->Put(WriteOptions(), "foo", "v1"));
Y
Yi Wu 已提交
643
  auto blob_files = blob_db_impl()->TEST_GetBlobFiles();
644
  ASSERT_EQ(1, blob_files.size());
Y
Yi Wu 已提交
645
  ASSERT_OK(blob_db_impl()->TEST_CloseBlobFile(blob_files[0]));
646 647

  SyncPoint::GetInstance()->LoadDependency(
648
      {{"BlobDBImpl::GCFileAndUpdateLSM:AfterGetFromBaseDB",
649 650
        "BlobDBImpl::PutUntil:Start"},
       {"BlobDBImpl::PutUntil:Finish",
651 652 653 654 655 656 657
        "BlobDBImpl::GCFileAndUpdateLSM:BeforeRelocate"}});
  SyncPoint::GetInstance()->EnableProcessing();

  auto writer = port::Thread(
      [this]() { ASSERT_OK(blob_db_->Put(WriteOptions(), "foo", "v2")); });

  GCStats gc_stats;
Y
Yi Wu 已提交
658
  ASSERT_OK(blob_db_impl()->TEST_GCFileAndUpdateLSM(blob_files[0], &gc_stats));
659 660 661 662 663 664 665 666 667
  ASSERT_EQ(1, gc_stats.blob_count);
  ASSERT_EQ(0, gc_stats.num_deletes);
  ASSERT_EQ(1, gc_stats.num_relocate);
  ASSERT_EQ(0, gc_stats.relocate_succeeded);
  ASSERT_EQ(1, gc_stats.overwritten_while_relocate);
  writer.join();
  VerifyDB({{"foo", "v2"}});
}

F
follitude 已提交
668
TEST_F(BlobDBTest, GCExpiredKeyWhileOverwriting) {
669 670 671 672
  Random rnd(301);
  Options options;
  options.env = mock_env_.get();
  BlobDBOptions bdb_options;
Y
Yi Wu 已提交
673
  bdb_options.min_blob_size = 0;
674 675
  bdb_options.disable_background_tasks = true;
  Open(bdb_options, options);
Y
Yi Wu 已提交
676
  mock_env_->set_current_time(100);
677
  ASSERT_OK(blob_db_->PutUntil(WriteOptions(), "foo", "v1", 200));
Y
Yi Wu 已提交
678
  auto blob_files = blob_db_impl()->TEST_GetBlobFiles();
679
  ASSERT_EQ(1, blob_files.size());
Y
Yi Wu 已提交
680
  ASSERT_OK(blob_db_impl()->TEST_CloseBlobFile(blob_files[0]));
Y
Yi Wu 已提交
681
  mock_env_->set_current_time(300);
682 683

  SyncPoint::GetInstance()->LoadDependency(
684
      {{"BlobDBImpl::GCFileAndUpdateLSM:AfterGetFromBaseDB",
685 686
        "BlobDBImpl::PutUntil:Start"},
       {"BlobDBImpl::PutUntil:Finish",
687 688 689 690 691 692 693 694
        "BlobDBImpl::GCFileAndUpdateLSM:BeforeDelete"}});
  SyncPoint::GetInstance()->EnableProcessing();

  auto writer = port::Thread([this]() {
    ASSERT_OK(blob_db_->PutUntil(WriteOptions(), "foo", "v2", 400));
  });

  GCStats gc_stats;
Y
Yi Wu 已提交
695
  ASSERT_OK(blob_db_impl()->TEST_GCFileAndUpdateLSM(blob_files[0], &gc_stats));
696 697 698 699 700 701 702 703 704
  ASSERT_EQ(1, gc_stats.blob_count);
  ASSERT_EQ(1, gc_stats.num_deletes);
  ASSERT_EQ(0, gc_stats.delete_succeeded);
  ASSERT_EQ(1, gc_stats.overwritten_while_delete);
  ASSERT_EQ(0, gc_stats.num_relocate);
  writer.join();
  VerifyDB({{"foo", "v2"}});
}

705 706 707 708 709
// This test is no longer valid since we now return an error when we go
// over the configured blob_dir_size.
// The test needs to be re-written later in such a way that writes continue
// after a GC happens.
TEST_F(BlobDBTest, DISABLED_GCOldestSimpleBlobFileWhenOutOfSpace) {
710 711 712 713 714 715
  // Use mock env to stop wall clock.
  Options options;
  options.env = mock_env_.get();
  BlobDBOptions bdb_options;
  bdb_options.blob_dir_size = 100;
  bdb_options.blob_file_size = 100;
Y
Yi Wu 已提交
716
  bdb_options.min_blob_size = 0;
717 718 719 720 721 722 723
  bdb_options.disable_background_tasks = true;
  Open(bdb_options);
  std::string value(100, 'v');
  ASSERT_OK(blob_db_->PutWithTTL(WriteOptions(), "key_with_ttl", value, 60));
  for (int i = 0; i < 10; i++) {
    ASSERT_OK(blob_db_->Put(WriteOptions(), "key" + ToString(i), value));
  }
Y
Yi Wu 已提交
724
  auto blob_files = blob_db_impl()->TEST_GetBlobFiles();
725 726 727 728 729 730 731 732 733
  ASSERT_EQ(11, blob_files.size());
  ASSERT_TRUE(blob_files[0]->HasTTL());
  ASSERT_TRUE(blob_files[0]->Immutable());
  for (int i = 1; i <= 10; i++) {
    ASSERT_FALSE(blob_files[i]->HasTTL());
    if (i < 10) {
      ASSERT_TRUE(blob_files[i]->Immutable());
    }
  }
Y
Yi Wu 已提交
734
  blob_db_impl()->TEST_RunGC();
735
  // The oldest simple blob file (i.e. blob_files[1]) has been selected for GC.
Y
Yi Wu 已提交
736
  auto obsolete_files = blob_db_impl()->TEST_GetObsoleteFiles();
737 738 739 740 741
  ASSERT_EQ(1, obsolete_files.size());
  ASSERT_EQ(blob_files[1]->BlobFileNumber(),
            obsolete_files[0]->BlobFileNumber());
}

742 743
TEST_F(BlobDBTest, ReadWhileGC) {
  // run the same test for Get(), MultiGet() and Iterator each.
744
  for (int i = 0; i < 2; i++) {
745
    BlobDBOptions bdb_options;
Y
Yi Wu 已提交
746
    bdb_options.min_blob_size = 0;
747 748 749
    bdb_options.disable_background_tasks = true;
    Open(bdb_options);
    blob_db_->Put(WriteOptions(), "foo", "bar");
Y
Yi Wu 已提交
750
    auto blob_files = blob_db_impl()->TEST_GetBlobFiles();
751 752 753
    ASSERT_EQ(1, blob_files.size());
    std::shared_ptr<BlobFile> bfile = blob_files[0];
    uint64_t bfile_number = bfile->BlobFileNumber();
Y
Yi Wu 已提交
754
    ASSERT_OK(blob_db_impl()->TEST_CloseBlobFile(bfile));
755 756 757 758 759 760 761 762 763 764 765

    switch (i) {
      case 0:
        SyncPoint::GetInstance()->LoadDependency(
            {{"BlobDBImpl::Get:AfterIndexEntryGet:1",
              "BlobDBTest::ReadWhileGC:1"},
             {"BlobDBTest::ReadWhileGC:2",
              "BlobDBImpl::Get:AfterIndexEntryGet:2"}});
        break;
      case 1:
        SyncPoint::GetInstance()->LoadDependency(
766
            {{"BlobDBIterator::UpdateBlobValue:Start:1",
767 768
              "BlobDBTest::ReadWhileGC:1"},
             {"BlobDBTest::ReadWhileGC:2",
769
              "BlobDBIterator::UpdateBlobValue:Start:2"}});
770 771 772 773 774 775 776 777 778 779 780 781 782 783 784 785 786 787 788 789 790 791
        break;
    }
    SyncPoint::GetInstance()->EnableProcessing();

    auto reader = port::Thread([this, i]() {
      std::string value;
      std::vector<std::string> values;
      std::vector<Status> statuses;
      switch (i) {
        case 0:
          ASSERT_OK(blob_db_->Get(ReadOptions(), "foo", &value));
          ASSERT_EQ("bar", value);
          break;
        case 1:
          // VerifyDB use iterator to scan the DB.
          VerifyDB({{"foo", "bar"}});
          break;
      }
    });

    TEST_SYNC_POINT("BlobDBTest::ReadWhileGC:1");
    GCStats gc_stats;
Y
Yi Wu 已提交
792
    ASSERT_OK(blob_db_impl()->TEST_GCFileAndUpdateLSM(bfile, &gc_stats));
793 794 795
    ASSERT_EQ(1, gc_stats.blob_count);
    ASSERT_EQ(1, gc_stats.num_relocate);
    ASSERT_EQ(1, gc_stats.relocate_succeeded);
Y
Yi Wu 已提交
796
    blob_db_impl()->TEST_DeleteObsoleteFiles();
797
    // The file shouln't be deleted
Y
Yi Wu 已提交
798
    blob_files = blob_db_impl()->TEST_GetBlobFiles();
799 800
    ASSERT_EQ(2, blob_files.size());
    ASSERT_EQ(bfile_number, blob_files[0]->BlobFileNumber());
Y
Yi Wu 已提交
801
    auto obsolete_files = blob_db_impl()->TEST_GetObsoleteFiles();
802 803 804 805 806 807 808
    ASSERT_EQ(1, obsolete_files.size());
    ASSERT_EQ(bfile_number, obsolete_files[0]->BlobFileNumber());
    TEST_SYNC_POINT("BlobDBTest::ReadWhileGC:2");
    reader.join();
    SyncPoint::GetInstance()->DisableProcessing();

    // The file is deleted this time
Y
Yi Wu 已提交
809 810
    blob_db_impl()->TEST_DeleteObsoleteFiles();
    blob_files = blob_db_impl()->TEST_GetBlobFiles();
811 812
    ASSERT_EQ(1, blob_files.size());
    ASSERT_NE(bfile_number, blob_files[0]->BlobFileNumber());
Y
Yi Wu 已提交
813
    ASSERT_EQ(0, blob_db_impl()->TEST_GetObsoleteFiles().size());
814 815 816 817 818
    VerifyDB({{"foo", "bar"}});
    Destroy();
  }
}

Y
Yi Wu 已提交
819 820 821 822 823 824 825 826 827 828 829 830 831 832 833 834 835 836 837 838 839 840 841 842 843 844 845 846 847 848 849 850 851 852 853 854 855 856 857 858 859 860 861 862 863 864 865 866 867 868 869 870 871 872 873 874 875 876 877 878 879 880 881 882 883 884 885 886 887
TEST_F(BlobDBTest, SnapshotAndGarbageCollection) {
  BlobDBOptions bdb_options;
  bdb_options.min_blob_size = 0;
  bdb_options.disable_background_tasks = true;
  // i = when to take snapshot
  for (int i = 0; i < 4; i++) {
    for (bool delete_key : {true, false}) {
      const Snapshot *snapshot = nullptr;
      Destroy();
      Open(bdb_options);
      // First file
      ASSERT_OK(Put("key1", "value"));
      if (i == 0) {
        snapshot = blob_db_->GetSnapshot();
      }
      auto blob_files = blob_db_impl()->TEST_GetBlobFiles();
      ASSERT_EQ(1, blob_files.size());
      ASSERT_OK(blob_db_impl()->TEST_CloseBlobFile(blob_files[0]));
      // Second file
      ASSERT_OK(Put("key2", "value"));
      if (i == 1) {
        snapshot = blob_db_->GetSnapshot();
      }
      blob_files = blob_db_impl()->TEST_GetBlobFiles();
      ASSERT_EQ(2, blob_files.size());
      auto bfile = blob_files[1];
      ASSERT_FALSE(bfile->Immutable());
      ASSERT_OK(blob_db_impl()->TEST_CloseBlobFile(bfile));
      // Third file
      ASSERT_OK(Put("key3", "value"));
      if (i == 2) {
        snapshot = blob_db_->GetSnapshot();
      }
      if (delete_key) {
        Delete("key2");
      }
      GCStats gc_stats;
      ASSERT_OK(blob_db_impl()->TEST_GCFileAndUpdateLSM(bfile, &gc_stats));
      ASSERT_TRUE(bfile->Obsolete());
      ASSERT_EQ(1, gc_stats.blob_count);
      if (delete_key) {
        ASSERT_EQ(0, gc_stats.num_relocate);
        ASSERT_EQ(bfile->GetSequenceRange().second + 1,
                  bfile->GetObsoleteSequence());
      } else {
        ASSERT_EQ(1, gc_stats.num_relocate);
        ASSERT_EQ(blob_db_->GetLatestSequenceNumber(),
                  bfile->GetObsoleteSequence());
      }
      if (i == 3) {
        snapshot = blob_db_->GetSnapshot();
      }
      size_t num_files = delete_key ? 3 : 4;
      ASSERT_EQ(num_files, blob_db_impl()->TEST_GetBlobFiles().size());
      blob_db_impl()->TEST_DeleteObsoleteFiles();
      if (i == 0 || i == 3 || (i == 2 && delete_key)) {
        // The snapshot shouldn't see data in bfile
        ASSERT_EQ(num_files - 1, blob_db_impl()->TEST_GetBlobFiles().size());
      } else {
        // The snapshot will see data in bfile, so the file shouldn't be deleted
        ASSERT_EQ(num_files, blob_db_impl()->TEST_GetBlobFiles().size());
        blob_db_->ReleaseSnapshot(snapshot);
        blob_db_impl()->TEST_DeleteObsoleteFiles();
        ASSERT_EQ(num_files - 1, blob_db_impl()->TEST_GetBlobFiles().size());
      }
    }
  }
}

888 889 890
TEST_F(BlobDBTest, ColumnFamilyNotSupported) {
  Options options;
  options.env = mock_env_.get();
Y
Yi Wu 已提交
891
  mock_env_->set_current_time(0);
892 893 894 895 896 897 898 899 900 901 902 903 904 905 906 907 908 909 910 911 912 913 914 915 916 917 918 919 920
  Open(BlobDBOptions(), options);
  ColumnFamilyHandle *default_handle = blob_db_->DefaultColumnFamily();
  ColumnFamilyHandle *handle = nullptr;
  std::string value;
  std::vector<std::string> values;
  // The call simply pass through to base db. It should succeed.
  ASSERT_OK(
      blob_db_->CreateColumnFamily(ColumnFamilyOptions(), "foo", &handle));
  ASSERT_TRUE(blob_db_->Put(WriteOptions(), handle, "k", "v").IsNotSupported());
  ASSERT_TRUE(blob_db_->PutWithTTL(WriteOptions(), handle, "k", "v", 60)
                  .IsNotSupported());
  ASSERT_TRUE(blob_db_->PutUntil(WriteOptions(), handle, "k", "v", 100)
                  .IsNotSupported());
  WriteBatch batch;
  batch.Put("k1", "v1");
  batch.Put(handle, "k2", "v2");
  ASSERT_TRUE(blob_db_->Write(WriteOptions(), &batch).IsNotSupported());
  ASSERT_TRUE(blob_db_->Get(ReadOptions(), "k1", &value).IsNotFound());
  ASSERT_TRUE(
      blob_db_->Get(ReadOptions(), handle, "k", &value).IsNotSupported());
  auto statuses = blob_db_->MultiGet(ReadOptions(), {default_handle, handle},
                                     {"k1", "k2"}, &values);
  ASSERT_EQ(2, statuses.size());
  ASSERT_TRUE(statuses[0].IsNotSupported());
  ASSERT_TRUE(statuses[1].IsNotSupported());
  ASSERT_EQ(nullptr, blob_db_->NewIterator(ReadOptions(), handle));
  delete handle;
}

921 922 923
TEST_F(BlobDBTest, GetLiveFilesMetaData) {
  Random rnd(301);
  BlobDBOptions bdb_options;
Y
Yi Wu 已提交
924
  bdb_options.min_blob_size = 0;
925 926 927 928 929 930 931 932 933 934 935 936 937 938 939 940 941 942 943 944 945
  bdb_options.disable_background_tasks = true;
  Open(bdb_options);
  std::map<std::string, std::string> data;
  for (size_t i = 0; i < 100; i++) {
    PutRandom("key" + ToString(i), &rnd, &data);
  }
  auto *bdb_impl = static_cast<BlobDBImpl *>(blob_db_);
  std::vector<LiveFileMetaData> metadata;
  bdb_impl->GetLiveFilesMetaData(&metadata);
  ASSERT_EQ(1U, metadata.size());
  std::string filename = dbname_ + "/blob_dir/000001.blob";
  ASSERT_EQ(filename, metadata[0].name);
  ASSERT_EQ("default", metadata[0].column_family_name);
  std::vector<std::string> livefile;
  uint64_t mfs;
  bdb_impl->GetLiveFiles(livefile, &mfs, false);
  ASSERT_EQ(4U, livefile.size());
  ASSERT_EQ(filename, livefile[3]);
  VerifyDB(data);
}

946 947 948 949 950 951 952 953 954 955 956 957 958 959 960 961 962 963 964 965 966 967 968 969 970 971 972 973 974 975 976 977 978 979 980 981 982 983 984 985 986 987 988 989 990 991 992 993 994 995 996 997
TEST_F(BlobDBTest, MigrateFromPlainRocksDB) {
  constexpr size_t kNumKey = 20;
  constexpr size_t kNumIteration = 10;
  Random rnd(301);
  std::map<std::string, std::string> data;
  std::vector<bool> is_blob(kNumKey, false);

  // Write to plain rocksdb.
  Options options;
  options.create_if_missing = true;
  DB *db = nullptr;
  ASSERT_OK(DB::Open(options, dbname_, &db));
  for (size_t i = 0; i < kNumIteration; i++) {
    auto key_index = rnd.Next() % kNumKey;
    std::string key = "key" + ToString(key_index);
    PutRandom(db, key, &rnd, &data);
  }
  VerifyDB(db, data);
  delete db;
  db = nullptr;

  // Open as blob db. Verify it can read existing data.
  Open();
  VerifyDB(blob_db_, data);
  for (size_t i = 0; i < kNumIteration; i++) {
    auto key_index = rnd.Next() % kNumKey;
    std::string key = "key" + ToString(key_index);
    is_blob[key_index] = true;
    PutRandom(blob_db_, key, &rnd, &data);
  }
  VerifyDB(blob_db_, data);
  delete blob_db_;
  blob_db_ = nullptr;

  // Verify plain db return error for keys written by blob db.
  ASSERT_OK(DB::Open(options, dbname_, &db));
  std::string value;
  for (size_t i = 0; i < kNumKey; i++) {
    std::string key = "key" + ToString(i);
    Status s = db->Get(ReadOptions(), key, &value);
    if (data.count(key) == 0) {
      ASSERT_TRUE(s.IsNotFound());
    } else if (is_blob[i]) {
      ASSERT_TRUE(s.IsNotSupported());
    } else {
      ASSERT_OK(s);
      ASSERT_EQ(data[key], value);
    }
  }
  delete db;
}

998
// Test to verify that a NoSpace IOError Status is returned on reaching
999
// blob_dir_size limit.
1000 1001 1002 1003 1004 1005 1006 1007 1008 1009 1010 1011 1012 1013 1014 1015 1016 1017 1018 1019 1020
TEST_F(BlobDBTest, OutOfSpace) {
  // Use mock env to stop wall clock.
  Options options;
  options.env = mock_env_.get();
  BlobDBOptions bdb_options;
  bdb_options.blob_dir_size = 150;
  bdb_options.disable_background_tasks = true;
  Open(bdb_options);

  // Each stored blob has an overhead of about 42 bytes currently.
  // So a small key + a 100 byte blob should take up ~150 bytes in the db.
  std::string value(100, 'v');
  ASSERT_OK(blob_db_->PutWithTTL(WriteOptions(), "key1", value, 60));

  // Putting another blob should fail as ading it would exceed the blob_dir_size
  // limit.
  Status s = blob_db_->PutWithTTL(WriteOptions(), "key2", value, 60);
  ASSERT_TRUE(s.IsIOError());
  ASSERT_TRUE(s.IsNoSpace());
}

1021 1022 1023 1024 1025 1026 1027 1028 1029 1030
TEST_F(BlobDBTest, EvictOldestFileWhenCloseToSpaceLimit) {
  // Use mock env to stop wall clock.
  Options options;
  BlobDBOptions bdb_options;
  bdb_options.blob_dir_size = 270;
  bdb_options.blob_file_size = 100;
  bdb_options.disable_background_tasks = true;
  bdb_options.is_fifo = true;
  Open(bdb_options);

Y
Yi Wu 已提交
1031
  // Each stored blob has an overhead of 32 bytes currently.
1032 1033 1034 1035 1036 1037 1038 1039 1040 1041 1042 1043 1044 1045 1046 1047 1048 1049 1050 1051 1052 1053 1054 1055
  // So a 100 byte blob should take up 132 bytes.
  std::string value(100, 'v');
  ASSERT_OK(blob_db_->PutWithTTL(WriteOptions(), "key1", value, 10));

  auto *bdb_impl = static_cast<BlobDBImpl *>(blob_db_);
  auto blob_files = bdb_impl->TEST_GetBlobFiles();
  ASSERT_EQ(1, blob_files.size());

  // Adding another 100 byte blob would take the total size to 264 bytes
  // (2*132), which is more than 90% of blob_dir_size. So, the oldest file
  // should be evicted and put in obsolete files list.
  ASSERT_OK(blob_db_->PutWithTTL(WriteOptions(), "key2", value, 60));

  auto obsolete_files = bdb_impl->TEST_GetObsoleteFiles();
  ASSERT_EQ(1, obsolete_files.size());
  ASSERT_TRUE(obsolete_files[0]->Immutable());
  ASSERT_EQ(blob_files[0]->BlobFileNumber(),
            obsolete_files[0]->BlobFileNumber());

  bdb_impl->TEST_DeleteObsoleteFiles();
  obsolete_files = bdb_impl->TEST_GetObsoleteFiles();
  ASSERT_TRUE(obsolete_files.empty());
}

Y
Yi Wu 已提交
1056 1057 1058 1059 1060 1061 1062 1063 1064 1065 1066 1067 1068 1069 1070 1071 1072 1073 1074 1075 1076 1077 1078 1079 1080 1081 1082 1083 1084 1085 1086 1087 1088 1089 1090 1091 1092 1093 1094 1095 1096 1097 1098 1099 1100 1101 1102 1103 1104 1105 1106 1107 1108 1109 1110 1111 1112 1113 1114 1115 1116 1117
TEST_F(BlobDBTest, InlineSmallValues) {
  constexpr uint64_t kMaxExpiration = 1000;
  Random rnd(301);
  BlobDBOptions bdb_options;
  bdb_options.ttl_range_secs = kMaxExpiration;
  bdb_options.min_blob_size = 100;
  bdb_options.blob_file_size = 256 * 1000 * 1000;
  bdb_options.disable_background_tasks = true;
  Options options;
  options.env = mock_env_.get();
  mock_env_->set_current_time(0);
  Open(bdb_options, options);
  std::map<std::string, std::string> data;
  std::map<std::string, KeyVersion> versions;
  SequenceNumber first_non_ttl_seq = kMaxSequenceNumber;
  SequenceNumber first_ttl_seq = kMaxSequenceNumber;
  SequenceNumber last_non_ttl_seq = 0;
  SequenceNumber last_ttl_seq = 0;
  for (size_t i = 0; i < 1000; i++) {
    bool is_small_value = rnd.Next() % 2;
    bool has_ttl = rnd.Next() % 2;
    uint64_t expiration = rnd.Next() % kMaxExpiration;
    int len = is_small_value ? 50 : 200;
    std::string key = "key" + ToString(i);
    std::string value = test::RandomHumanReadableString(&rnd, len);
    std::string blob_index;
    data[key] = value;
    SequenceNumber sequence = blob_db_->GetLatestSequenceNumber() + 1;
    if (!has_ttl) {
      ASSERT_OK(blob_db_->Put(WriteOptions(), key, value));
    } else {
      ASSERT_OK(blob_db_->PutUntil(WriteOptions(), key, value, expiration));
    }
    ASSERT_EQ(blob_db_->GetLatestSequenceNumber(), sequence);
    versions[key] =
        KeyVersion(key, value, sequence,
                   (is_small_value && !has_ttl) ? kTypeValue : kTypeBlobIndex);
    if (!is_small_value) {
      if (!has_ttl) {
        first_non_ttl_seq = std::min(first_non_ttl_seq, sequence);
        last_non_ttl_seq = std::max(last_non_ttl_seq, sequence);
      } else {
        first_ttl_seq = std::min(first_ttl_seq, sequence);
        last_ttl_seq = std::max(last_ttl_seq, sequence);
      }
    }
  }
  VerifyDB(data);
  VerifyBaseDB(versions);
  auto *bdb_impl = static_cast<BlobDBImpl *>(blob_db_);
  auto blob_files = bdb_impl->TEST_GetBlobFiles();
  ASSERT_EQ(2, blob_files.size());
  std::shared_ptr<BlobFile> non_ttl_file;
  std::shared_ptr<BlobFile> ttl_file;
  if (blob_files[0]->HasTTL()) {
    ttl_file = blob_files[0];
    non_ttl_file = blob_files[1];
  } else {
    non_ttl_file = blob_files[0];
    ttl_file = blob_files[1];
  }
  ASSERT_FALSE(non_ttl_file->HasTTL());
Y
Yi Wu 已提交
1118 1119
  ASSERT_EQ(first_non_ttl_seq, non_ttl_file->GetSequenceRange().first);
  ASSERT_EQ(last_non_ttl_seq, non_ttl_file->GetSequenceRange().second);
Y
Yi Wu 已提交
1120
  ASSERT_TRUE(ttl_file->HasTTL());
Y
Yi Wu 已提交
1121 1122
  ASSERT_EQ(first_ttl_seq, ttl_file->GetSequenceRange().first);
  ASSERT_EQ(last_ttl_seq, ttl_file->GetSequenceRange().second);
Y
Yi Wu 已提交
1123 1124
}

A
Anirban Rahut 已提交
1125
}  //  namespace blob_db
1126 1127 1128 1129 1130 1131 1132 1133 1134 1135 1136 1137 1138 1139 1140 1141 1142
}  //  namespace rocksdb

// A black-box test for the ttl wrapper around rocksdb
int main(int argc, char** argv) {
  ::testing::InitGoogleTest(&argc, argv);
  return RUN_ALL_TESTS();
}

#else
#include <stdio.h>

int main(int argc, char** argv) {
  fprintf(stderr, "SKIPPED as BlobDB is not supported in ROCKSDB_LITE\n");
  return 0;
}

#endif  // !ROCKSDB_LITE