]>
Commit | Line | Data |
---|---|---|
20effc67 TL |
1 | // Copyright (c) 2011-present, Facebook, Inc. All rights reserved. |
2 | // This source code is licensed under both the GPLv2 (found in the | |
3 | // COPYING file in the root directory) and Apache 2.0 License | |
4 | // (found in the LICENSE.Apache file in the root directory). | |
5 | ||
6 | #include "db/blob/blob_index.h" | |
7 | #include "db/db_test_util.h" | |
8 | #include "port/stack_trace.h" | |
9 | #include "test_util/sync_point.h" | |
10 | #include "utilities/fault_injection_env.h" | |
11 | ||
12 | namespace ROCKSDB_NAMESPACE { | |
13 | ||
14 | class DBBlobBasicTest : public DBTestBase { | |
15 | protected: | |
16 | DBBlobBasicTest() | |
17 | : DBTestBase("/db_blob_basic_test", /* env_do_fsync */ false) {} | |
18 | }; | |
19 | ||
20 | TEST_F(DBBlobBasicTest, GetBlob) { | |
21 | Options options = GetDefaultOptions(); | |
22 | options.enable_blob_files = true; | |
23 | options.min_blob_size = 0; | |
24 | ||
25 | Reopen(options); | |
26 | ||
27 | constexpr char key[] = "key"; | |
28 | constexpr char blob_value[] = "blob_value"; | |
29 | ||
30 | ASSERT_OK(Put(key, blob_value)); | |
31 | ||
32 | ASSERT_OK(Flush()); | |
33 | ||
34 | ASSERT_EQ(Get(key), blob_value); | |
35 | ||
36 | // Try again with no I/O allowed. The table and the necessary blocks should | |
37 | // already be in their respective caches; however, the blob itself can only be | |
38 | // read from the blob file, so the read should return Incomplete. | |
39 | ReadOptions read_options; | |
40 | read_options.read_tier = kBlockCacheTier; | |
41 | ||
42 | PinnableSlice result; | |
43 | ASSERT_TRUE(db_->Get(read_options, db_->DefaultColumnFamily(), key, &result) | |
44 | .IsIncomplete()); | |
45 | } | |
46 | ||
47 | TEST_F(DBBlobBasicTest, GetBlob_CorruptIndex) { | |
48 | Options options = GetDefaultOptions(); | |
49 | options.enable_blob_files = true; | |
50 | options.min_blob_size = 0; | |
51 | ||
52 | Reopen(options); | |
53 | ||
54 | constexpr char key[] = "key"; | |
55 | ||
56 | // Fake a corrupt blob index. | |
57 | const std::string blob_index("foobar"); | |
58 | ||
59 | WriteBatch batch; | |
60 | ASSERT_OK(WriteBatchInternal::PutBlobIndex(&batch, 0, key, blob_index)); | |
61 | ASSERT_OK(db_->Write(WriteOptions(), &batch)); | |
62 | ||
63 | ASSERT_OK(Flush()); | |
64 | ||
65 | PinnableSlice result; | |
66 | ASSERT_TRUE(db_->Get(ReadOptions(), db_->DefaultColumnFamily(), key, &result) | |
67 | .IsCorruption()); | |
68 | } | |
69 | ||
70 | TEST_F(DBBlobBasicTest, GetBlob_InlinedTTLIndex) { | |
71 | constexpr uint64_t min_blob_size = 10; | |
72 | ||
73 | Options options = GetDefaultOptions(); | |
74 | options.enable_blob_files = true; | |
75 | options.min_blob_size = min_blob_size; | |
76 | ||
77 | Reopen(options); | |
78 | ||
79 | constexpr char key[] = "key"; | |
80 | constexpr char blob[] = "short"; | |
81 | static_assert(sizeof(short) - 1 < min_blob_size, | |
82 | "Blob too long to be inlined"); | |
83 | ||
84 | // Fake an inlined TTL blob index. | |
85 | std::string blob_index; | |
86 | ||
87 | constexpr uint64_t expiration = 1234567890; | |
88 | ||
89 | BlobIndex::EncodeInlinedTTL(&blob_index, expiration, blob); | |
90 | ||
91 | WriteBatch batch; | |
92 | ASSERT_OK(WriteBatchInternal::PutBlobIndex(&batch, 0, key, blob_index)); | |
93 | ASSERT_OK(db_->Write(WriteOptions(), &batch)); | |
94 | ||
95 | ASSERT_OK(Flush()); | |
96 | ||
97 | PinnableSlice result; | |
98 | ASSERT_TRUE(db_->Get(ReadOptions(), db_->DefaultColumnFamily(), key, &result) | |
99 | .IsCorruption()); | |
100 | } | |
101 | ||
102 | TEST_F(DBBlobBasicTest, GetBlob_IndexWithInvalidFileNumber) { | |
103 | Options options = GetDefaultOptions(); | |
104 | options.enable_blob_files = true; | |
105 | options.min_blob_size = 0; | |
106 | ||
107 | Reopen(options); | |
108 | ||
109 | constexpr char key[] = "key"; | |
110 | ||
111 | // Fake a blob index referencing a non-existent blob file. | |
112 | std::string blob_index; | |
113 | ||
114 | constexpr uint64_t blob_file_number = 1000; | |
115 | constexpr uint64_t offset = 1234; | |
116 | constexpr uint64_t size = 5678; | |
117 | ||
118 | BlobIndex::EncodeBlob(&blob_index, blob_file_number, offset, size, | |
119 | kNoCompression); | |
120 | ||
121 | WriteBatch batch; | |
122 | ASSERT_OK(WriteBatchInternal::PutBlobIndex(&batch, 0, key, blob_index)); | |
123 | ASSERT_OK(db_->Write(WriteOptions(), &batch)); | |
124 | ||
125 | ASSERT_OK(Flush()); | |
126 | ||
127 | PinnableSlice result; | |
128 | ASSERT_TRUE(db_->Get(ReadOptions(), db_->DefaultColumnFamily(), key, &result) | |
129 | .IsCorruption()); | |
130 | } | |
131 | ||
132 | class DBBlobBasicIOErrorTest : public DBBlobBasicTest, | |
133 | public testing::WithParamInterface<std::string> { | |
134 | protected: | |
135 | DBBlobBasicIOErrorTest() : sync_point_(GetParam()) { | |
136 | fault_injection_env_.reset(new FaultInjectionTestEnv(env_)); | |
137 | } | |
138 | ~DBBlobBasicIOErrorTest() { Close(); } | |
139 | ||
140 | std::unique_ptr<FaultInjectionTestEnv> fault_injection_env_; | |
141 | std::string sync_point_; | |
142 | }; | |
143 | ||
144 | INSTANTIATE_TEST_CASE_P(DBBlobBasicTest, DBBlobBasicIOErrorTest, | |
145 | ::testing::ValuesIn(std::vector<std::string>{ | |
146 | "BlobFileReader::OpenFile:NewRandomAccessFile", | |
147 | "BlobFileReader::GetBlob:ReadFromFile"})); | |
148 | ||
149 | TEST_P(DBBlobBasicIOErrorTest, GetBlob_IOError) { | |
150 | Options options; | |
151 | options.env = fault_injection_env_.get(); | |
152 | options.enable_blob_files = true; | |
153 | options.min_blob_size = 0; | |
154 | ||
155 | Reopen(options); | |
156 | ||
157 | constexpr char key[] = "key"; | |
158 | constexpr char blob_value[] = "blob_value"; | |
159 | ||
160 | ASSERT_OK(Put(key, blob_value)); | |
161 | ||
162 | ASSERT_OK(Flush()); | |
163 | ||
164 | SyncPoint::GetInstance()->SetCallBack(sync_point_, [this](void* /* arg */) { | |
165 | fault_injection_env_->SetFilesystemActive(false, | |
166 | Status::IOError(sync_point_)); | |
167 | }); | |
168 | SyncPoint::GetInstance()->EnableProcessing(); | |
169 | ||
170 | PinnableSlice result; | |
171 | ASSERT_TRUE(db_->Get(ReadOptions(), db_->DefaultColumnFamily(), key, &result) | |
172 | .IsIOError()); | |
173 | ||
174 | SyncPoint::GetInstance()->DisableProcessing(); | |
175 | SyncPoint::GetInstance()->ClearAllCallBacks(); | |
176 | } | |
177 | ||
178 | } // namespace ROCKSDB_NAMESPACE | |
179 | ||
180 | int main(int argc, char** argv) { | |
181 | ROCKSDB_NAMESPACE::port::InstallStackTraceHandler(); | |
182 | ::testing::InitGoogleTest(&argc, argv); | |
183 | return RUN_ALL_TESTS(); | |
184 | } |