cb8a7302e4
Summary: I added max_size option in blobstore. Since we now know the maximum number of buckets we'll ever use, we can allocate an array of buckets and access its elements without use of any locks! Common case Get doesn't lock anything now. Benchmarks on 16KB block size show no impact on speed, though. Test Plan: unittests + benchmark Reviewers: dhruba, haobo, kailiu Reviewed By: dhruba CC: leveldb Differential Revision: https://reviews.facebook.net/D13641
201 lines
5.4 KiB
C++
201 lines
5.4 KiB
C++
// Copyright (c) 2011 The LevelDB Authors. All rights reserved.
|
|
// Use of this source code is governed by a BSD-style license that can be
|
|
// found in the LICENSE file. See the AUTHORS file for names of contributors.
|
|
|
|
#include "util/blob_store.h"
|
|
|
|
#include "util/testharness.h"
|
|
#include "util/testutil.h"
|
|
#include "util/random.h"
|
|
|
|
#include <cstdlib>
|
|
#include <string>
|
|
|
|
namespace rocksdb {
|
|
|
|
using namespace std;
|
|
|
|
class BlobStoreTest { };
|
|
|
|
TEST(BlobStoreTest, RangeParseTest) {
|
|
Blob e;
|
|
for (int i = 0; i < 5; ++i) {
|
|
e.chunks.push_back(BlobChunk(rand(), rand(), rand()));
|
|
}
|
|
string x = e.ToString();
|
|
Blob nx(x);
|
|
|
|
ASSERT_EQ(nx.ToString(), x);
|
|
}
|
|
|
|
// make sure we're reusing the freed space
|
|
TEST(BlobStoreTest, SanityTest) {
|
|
const uint64_t block_size = 10;
|
|
const uint32_t blocks_per_file = 20;
|
|
Random random(5);
|
|
|
|
BlobStore blob_store(test::TmpDir() + "/blob_store_test",
|
|
block_size,
|
|
blocks_per_file,
|
|
1000,
|
|
Env::Default());
|
|
|
|
string buf;
|
|
|
|
// put string of size 170
|
|
test::RandomString(&random, 170, &buf);
|
|
Blob r1;
|
|
ASSERT_OK(blob_store.Put(Slice(buf), &r1));
|
|
// use the first file
|
|
for (size_t i = 0; i < r1.chunks.size(); ++i) {
|
|
ASSERT_EQ(r1.chunks[0].bucket_id, 0u);
|
|
}
|
|
|
|
// put string of size 30
|
|
test::RandomString(&random, 30, &buf);
|
|
Blob r2;
|
|
ASSERT_OK(blob_store.Put(Slice(buf), &r2));
|
|
// use the first file
|
|
for (size_t i = 0; i < r2.chunks.size(); ++i) {
|
|
ASSERT_EQ(r2.chunks[0].bucket_id, 0u);
|
|
}
|
|
|
|
// delete blob of size 170
|
|
ASSERT_OK(blob_store.Delete(r1));
|
|
|
|
// put a string of size 100
|
|
test::RandomString(&random, 100, &buf);
|
|
Blob r3;
|
|
ASSERT_OK(blob_store.Put(Slice(buf), &r3));
|
|
// use the first file
|
|
for (size_t i = 0; i < r3.chunks.size(); ++i) {
|
|
ASSERT_EQ(r3.chunks[0].bucket_id, 0u);
|
|
}
|
|
|
|
// put a string of size 70
|
|
test::RandomString(&random, 70, &buf);
|
|
Blob r4;
|
|
ASSERT_OK(blob_store.Put(Slice(buf), &r4));
|
|
// use the first file
|
|
for (size_t i = 0; i < r4.chunks.size(); ++i) {
|
|
ASSERT_EQ(r4.chunks[0].bucket_id, 0u);
|
|
}
|
|
|
|
// put a string of size 5
|
|
test::RandomString(&random, 5, &buf);
|
|
Blob r5;
|
|
ASSERT_OK(blob_store.Put(Slice(buf), &r5));
|
|
// now you get to use the second file
|
|
for (size_t i = 0; i < r5.chunks.size(); ++i) {
|
|
ASSERT_EQ(r5.chunks[0].bucket_id, 1u);
|
|
}
|
|
}
|
|
|
|
TEST(BlobStoreTest, FragmentedChunksTest) {
|
|
const uint64_t block_size = 10;
|
|
const uint32_t blocks_per_file = 20;
|
|
Random random(5);
|
|
|
|
BlobStore blob_store(test::TmpDir() + "/blob_store_test",
|
|
block_size,
|
|
blocks_per_file,
|
|
1000,
|
|
Env::Default());
|
|
|
|
string buf;
|
|
|
|
vector <Blob> r(4);
|
|
|
|
// put 4 strings of size 50
|
|
for (int k = 0; k < 4; ++k) {
|
|
test::RandomString(&random, 50, &buf);
|
|
ASSERT_OK(blob_store.Put(Slice(buf), &r[k]));
|
|
// use the first file
|
|
for (size_t i = 0; i < r[k].chunks.size(); ++i) {
|
|
ASSERT_EQ(r[k].chunks[0].bucket_id, 0u);
|
|
}
|
|
}
|
|
|
|
// delete the first and third
|
|
ASSERT_OK(blob_store.Delete(r[0]));
|
|
ASSERT_OK(blob_store.Delete(r[2]));
|
|
|
|
// put string of size 100. it should reuse space that we deleting
|
|
// by deleting first and third strings of size 50
|
|
test::RandomString(&random, 100, &buf);
|
|
Blob r2;
|
|
ASSERT_OK(blob_store.Put(Slice(buf), &r2));
|
|
// use the first file
|
|
for (size_t i = 0; i < r2.chunks.size(); ++i) {
|
|
ASSERT_EQ(r2.chunks[0].bucket_id, 0u);
|
|
}
|
|
}
|
|
|
|
TEST(BlobStoreTest, CreateAndStoreTest) {
|
|
const uint64_t block_size = 10;
|
|
const uint32_t blocks_per_file = 1000;
|
|
const int max_blurb_size = 300;
|
|
Random random(5);
|
|
|
|
BlobStore blob_store(test::TmpDir() + "/blob_store_test",
|
|
block_size,
|
|
blocks_per_file,
|
|
10000,
|
|
Env::Default());
|
|
vector<pair<Blob, string>> ranges;
|
|
|
|
for (int i = 0; i < 20000; ++i) {
|
|
int decision = rand() % 5;
|
|
if (decision <= 2 || ranges.size() == 0) {
|
|
string buf;
|
|
int size_blocks = (rand() % max_blurb_size + 1);
|
|
int string_size = size_blocks * block_size - (rand() % block_size);
|
|
test::RandomString(&random, string_size, &buf);
|
|
Blob r;
|
|
ASSERT_OK(blob_store.Put(Slice(buf), &r));
|
|
ranges.push_back(make_pair(r, buf));
|
|
} else if (decision == 3) {
|
|
int ti = rand() % ranges.size();
|
|
string out_buf;
|
|
ASSERT_OK(blob_store.Get(ranges[ti].first, &out_buf));
|
|
ASSERT_EQ(ranges[ti].second, out_buf);
|
|
} else {
|
|
int ti = rand() % ranges.size();
|
|
ASSERT_OK(blob_store.Delete(ranges[ti].first));
|
|
ranges.erase(ranges.begin() + ti);
|
|
}
|
|
}
|
|
ASSERT_OK(blob_store.Sync());
|
|
}
|
|
|
|
TEST(BlobStoreTest, MaxSizeTest) {
|
|
const uint64_t block_size = 10;
|
|
const uint32_t blocks_per_file = 100;
|
|
const int max_buckets = 10;
|
|
Random random(5);
|
|
|
|
BlobStore blob_store(test::TmpDir() + "/blob_store_test",
|
|
block_size,
|
|
blocks_per_file,
|
|
max_buckets,
|
|
Env::Default());
|
|
string buf;
|
|
for (int i = 0; i < max_buckets; ++i) {
|
|
test::RandomString(&random, 1000, &buf);
|
|
Blob r;
|
|
ASSERT_OK(blob_store.Put(Slice(buf), &r));
|
|
}
|
|
|
|
test::RandomString(&random, 1000, &buf);
|
|
Blob r;
|
|
// should fail because max size
|
|
Status s = blob_store.Put(Slice(buf), &r);
|
|
ASSERT_EQ(s.ok(), false);
|
|
}
|
|
|
|
} // namespace rocksdb
|
|
|
|
int main(int argc, char** argv) {
|
|
return rocksdb::test::RunAllTests();
|
|
}
|