@ -561,6 +561,199 @@ TEST_F(BlobSourceTest, GetCompressedBlobs) {
}
}
}
}
TEST_F ( BlobSourceTest , MultiGetBlobsFromMultiFiles ) {
options_ . cf_paths . emplace_back (
test : : PerThreadDBPath ( env_ , " BlobSourceTest_MultiGetBlobsFromMultiFiles " ) ,
0 ) ;
options_ . statistics = CreateDBStatistics ( ) ;
Statistics * statistics = options_ . statistics . get ( ) ;
assert ( statistics ) ;
DestroyAndReopen ( options_ ) ;
ImmutableOptions immutable_options ( options_ ) ;
constexpr uint32_t column_family_id = 1 ;
constexpr bool has_ttl = false ;
constexpr ExpirationRange expiration_range ;
constexpr uint64_t blob_files = 2 ;
constexpr size_t num_blobs = 32 ;
std : : vector < std : : string > key_strs ;
std : : vector < std : : string > blob_strs ;
for ( size_t i = 0 ; i < num_blobs ; + + i ) {
key_strs . push_back ( " key " + std : : to_string ( i ) ) ;
blob_strs . push_back ( " blob " + std : : to_string ( i ) ) ;
}
std : : vector < Slice > keys ;
std : : vector < Slice > blobs ;
uint64_t file_size = BlobLogHeader : : kSize ;
uint64_t blob_value_bytes = 0 ;
for ( size_t i = 0 ; i < num_blobs ; + + i ) {
keys . push_back ( { key_strs [ i ] } ) ;
blobs . push_back ( { blob_strs [ i ] } ) ;
blob_value_bytes + = blobs [ i ] . size ( ) ;
file_size + = BlobLogRecord : : kHeaderSize + keys [ i ] . size ( ) + blobs [ i ] . size ( ) ;
}
file_size + = BlobLogFooter : : kSize ;
const uint64_t blob_records_bytes =
file_size - BlobLogHeader : : kSize - BlobLogFooter : : kSize ;
std : : vector < uint64_t > blob_offsets ( keys . size ( ) ) ;
std : : vector < uint64_t > blob_sizes ( keys . size ( ) ) ;
{
// Write key/blob pairs to multiple blob files.
for ( size_t i = 0 ; i < blob_files ; + + i ) {
const uint64_t file_number = i + 1 ;
WriteBlobFile ( immutable_options , column_family_id , has_ttl ,
expiration_range , expiration_range , file_number , keys ,
blobs , kNoCompression , blob_offsets , blob_sizes ) ;
}
}
constexpr size_t capacity = 10 ;
std : : shared_ptr < Cache > backing_cache =
NewLRUCache ( capacity ) ; // Blob file cache
FileOptions file_options ;
constexpr HistogramImpl * blob_file_read_hist = nullptr ;
std : : unique_ptr < BlobFileCache > blob_file_cache ( new BlobFileCache (
backing_cache . get ( ) , & immutable_options , & file_options , column_family_id ,
blob_file_read_hist , nullptr /*IOTracer*/ ) ) ;
BlobSource blob_source ( & immutable_options , db_id_ , db_session_id_ ,
blob_file_cache . get ( ) ) ;
ReadOptions read_options ;
read_options . verify_checksums = true ;
uint64_t bytes_read = 0 ;
{
// MultiGetBlob
read_options . fill_cache = true ;
read_options . read_tier = ReadTier : : kReadAllTier ;
autovector < BlobFileReadRequests > blob_reqs ;
std : : array < autovector < BlobReadRequest > , blob_files > blob_reqs_in_file ;
std : : array < PinnableSlice , num_blobs * blob_files > value_buf ;
std : : array < Status , num_blobs * blob_files > statuses_buf ;
for ( size_t i = 0 ; i < blob_files ; + + i ) {
const uint64_t file_number = i + 1 ;
for ( size_t j = 0 ; j < num_blobs ; + + j ) {
blob_reqs_in_file [ i ] . emplace_back (
keys [ j ] , blob_offsets [ j ] , blob_sizes [ j ] , kNoCompression ,
& value_buf [ i * num_blobs + j ] , & statuses_buf [ i * num_blobs + j ] ) ;
}
blob_reqs . emplace_back ( file_number , file_size , blob_reqs_in_file [ i ] ) ;
}
get_perf_context ( ) - > Reset ( ) ;
statistics - > Reset ( ) . PermitUncheckedError ( ) ;
blob_source . MultiGetBlob ( read_options , blob_reqs , & bytes_read ) ;
for ( size_t i = 0 ; i < blob_files ; + + i ) {
const uint64_t file_number = i + 1 ;
for ( size_t j = 0 ; j < num_blobs ; + + j ) {
ASSERT_OK ( statuses_buf [ i * num_blobs + j ] ) ;
ASSERT_EQ ( value_buf [ i * num_blobs + j ] , blobs [ j ] ) ;
ASSERT_TRUE ( blob_source . TEST_BlobInCache ( file_number , file_size ,
blob_offsets [ j ] ) ) ;
}
}
// Retrieved all blobs from 2 blob files twice via MultiGetBlob and
// TEST_BlobInCache.
ASSERT_EQ ( ( int ) get_perf_context ( ) - > blob_cache_hit_count ,
num_blobs * blob_files ) ;
ASSERT_EQ ( ( int ) get_perf_context ( ) - > blob_read_count ,
num_blobs * blob_files ) ; // blocking i/o
ASSERT_EQ ( ( int ) get_perf_context ( ) - > blob_read_byte ,
blob_records_bytes * blob_files ) ; // blocking i/o
ASSERT_GE ( ( int ) get_perf_context ( ) - > blob_checksum_time , 0 ) ;
ASSERT_EQ ( ( int ) get_perf_context ( ) - > blob_decompress_time , 0 ) ;
ASSERT_EQ ( statistics - > getTickerCount ( BLOB_DB_CACHE_MISS ) ,
num_blobs * blob_files ) ; // MultiGetBlob
ASSERT_EQ ( statistics - > getTickerCount ( BLOB_DB_CACHE_HIT ) ,
num_blobs * blob_files ) ; // TEST_BlobInCache
ASSERT_EQ ( statistics - > getTickerCount ( BLOB_DB_CACHE_ADD ) ,
num_blobs * blob_files ) ; // MultiGetBlob
ASSERT_EQ ( statistics - > getTickerCount ( BLOB_DB_CACHE_BYTES_READ ) ,
blob_value_bytes * blob_files ) ; // TEST_BlobInCache
ASSERT_EQ ( statistics - > getTickerCount ( BLOB_DB_CACHE_BYTES_WRITE ) ,
blob_value_bytes * blob_files ) ; // MultiGetBlob
get_perf_context ( ) - > Reset ( ) ;
statistics - > Reset ( ) . PermitUncheckedError ( ) ;
autovector < BlobReadRequest > fake_blob_reqs_in_file ;
std : : array < PinnableSlice , num_blobs > fake_value_buf ;
std : : array < Status , num_blobs > fake_statuses_buf ;
const uint64_t fake_file_number = 100 ;
for ( size_t i = 0 ; i < num_blobs ; + + i ) {
fake_blob_reqs_in_file . emplace_back (
keys [ i ] , blob_offsets [ i ] , blob_sizes [ i ] , kNoCompression ,
& fake_value_buf [ i ] , & fake_statuses_buf [ i ] ) ;
}
// Add a fake multi-get blob request.
blob_reqs . emplace_back ( fake_file_number , file_size , fake_blob_reqs_in_file ) ;
blob_source . MultiGetBlob ( read_options , blob_reqs , & bytes_read ) ;
// Check the real blob read requests.
for ( size_t i = 0 ; i < blob_files ; + + i ) {
const uint64_t file_number = i + 1 ;
for ( size_t j = 0 ; j < num_blobs ; + + j ) {
ASSERT_OK ( statuses_buf [ i * num_blobs + j ] ) ;
ASSERT_EQ ( value_buf [ i * num_blobs + j ] , blobs [ j ] ) ;
ASSERT_TRUE ( blob_source . TEST_BlobInCache ( file_number , file_size ,
blob_offsets [ j ] ) ) ;
}
}
// Check the fake blob request.
for ( size_t i = 0 ; i < num_blobs ; + + i ) {
ASSERT_TRUE ( fake_statuses_buf [ i ] . IsIOError ( ) ) ;
ASSERT_TRUE ( fake_value_buf [ i ] . empty ( ) ) ;
ASSERT_FALSE ( blob_source . TEST_BlobInCache ( fake_file_number , file_size ,
blob_offsets [ i ] ) ) ;
}
// Retrieved all blobs from 3 blob files (including the fake one) twice
// via MultiGetBlob and TEST_BlobInCache.
ASSERT_EQ ( ( int ) get_perf_context ( ) - > blob_cache_hit_count ,
num_blobs * blob_files * 2 ) ;
ASSERT_EQ ( ( int ) get_perf_context ( ) - > blob_read_count ,
0 ) ; // blocking i/o
ASSERT_EQ ( ( int ) get_perf_context ( ) - > blob_read_byte ,
0 ) ; // blocking i/o
ASSERT_GE ( ( int ) get_perf_context ( ) - > blob_checksum_time , 0 ) ;
ASSERT_EQ ( ( int ) get_perf_context ( ) - > blob_decompress_time , 0 ) ;
// Fake blob requests: MultiGetBlob and TEST_BlobInCache
ASSERT_EQ ( statistics - > getTickerCount ( BLOB_DB_CACHE_MISS ) , num_blobs * 2 ) ;
// Real blob requests: MultiGetBlob and TEST_BlobInCache
ASSERT_EQ ( statistics - > getTickerCount ( BLOB_DB_CACHE_HIT ) ,
num_blobs * blob_files * 2 ) ;
ASSERT_EQ ( statistics - > getTickerCount ( BLOB_DB_CACHE_ADD ) , 0 ) ;
// Real blob requests: MultiGetBlob and TEST_BlobInCache
ASSERT_EQ ( statistics - > getTickerCount ( BLOB_DB_CACHE_BYTES_READ ) ,
blob_value_bytes * blob_files * 2 ) ;
ASSERT_EQ ( statistics - > getTickerCount ( BLOB_DB_CACHE_BYTES_WRITE ) , 0 ) ;
}
}
TEST_F ( BlobSourceTest , MultiGetBlobsFromCache ) {
TEST_F ( BlobSourceTest , MultiGetBlobsFromCache ) {
options_ . cf_paths . emplace_back (
options_ . cf_paths . emplace_back (
test : : PerThreadDBPath ( env_ , " BlobSourceTest_MultiGetBlobsFromCache " ) , 0 ) ;
test : : PerThreadDBPath ( env_ , " BlobSourceTest_MultiGetBlobsFromCache " ) , 0 ) ;
@ -625,23 +818,15 @@ TEST_F(BlobSourceTest, MultiGetBlobsFromCache) {
constexpr FilePrefetchBuffer * prefetch_buffer = nullptr ;
constexpr FilePrefetchBuffer * prefetch_buffer = nullptr ;
{
{
// MultiGetBlob
// MultiGetBlobFromOneFile
uint64_t bytes_read = 0 ;
uint64_t bytes_read = 0 ;
autovector < std : : reference_wrapper < const Slice > > key_refs ;
autovector < uint64_t > offsets ;
autovector < uint64_t > sizes ;
std : : array < Status , num_blobs > statuses_buf ;
std : : array < Status , num_blobs > statuses_buf ;
autovector < Status * > statuses ;
std : : array < PinnableSlice , num_blobs > value_buf ;
std : : array < PinnableSlice , num_blobs > value_buf ;
autovector < PinnableSlice * > value s;
autovector < BlobReadRequest > blob_reqs ;
for ( size_t i = 0 ; i < num_blobs ; i + = 2 ) { // even index
for ( size_t i = 0 ; i < num_blobs ; i + = 2 ) { // even index
key_refs . emplace_back ( std : : cref ( keys [ i ] ) ) ;
blob_reqs . emplace_back ( keys [ i ] , blob_offsets [ i ] , blob_sizes [ i ] ,
offsets . push_back ( blob_offsets [ i ] ) ;
kNoCompression , & value_buf [ i ] , & statuses_buf [ i ] ) ;
sizes . push_back ( blob_sizes [ i ] ) ;
statuses . push_back ( & statuses_buf [ i ] ) ;
values . push_back ( & value_buf [ i ] ) ;
ASSERT_FALSE ( blob_source . TEST_BlobInCache ( blob_file_number , file_size ,
ASSERT_FALSE ( blob_source . TEST_BlobInCache ( blob_file_number , file_size ,
blob_offsets [ i ] ) ) ;
blob_offsets [ i ] ) ) ;
}
}
@ -652,9 +837,8 @@ TEST_F(BlobSourceTest, MultiGetBlobsFromCache) {
statistics - > Reset ( ) . PermitUncheckedError ( ) ;
statistics - > Reset ( ) . PermitUncheckedError ( ) ;
// Get half of blobs
// Get half of blobs
blob_source . MultiGetBlob ( read_options , key_refs , blob_file_number ,
blob_source . MultiGetBlobFromOneFile ( read_options , blob_file_number ,
file_size , offsets , sizes , statuses , values ,
file_size , blob_reqs , & bytes_read ) ;
& bytes_read ) ;
uint64_t fs_read_bytes = 0 ;
uint64_t fs_read_bytes = 0 ;
uint64_t ca_read_bytes = 0 ;
uint64_t ca_read_bytes = 0 ;
@ -709,27 +893,19 @@ TEST_F(BlobSourceTest, MultiGetBlobsFromCache) {
blob_offsets [ i ] ) ) ;
blob_offsets [ i ] ) ) ;
}
}
// Cache-only MultiGetBlob
// Cache-only MultiGetBlobFromOneFile
read_options . read_tier = ReadTier : : kBlockCacheTier ;
read_options . read_tier = ReadTier : : kBlockCacheTier ;
get_perf_context ( ) - > Reset ( ) ;
get_perf_context ( ) - > Reset ( ) ;
statistics - > Reset ( ) . PermitUncheckedError ( ) ;
statistics - > Reset ( ) . PermitUncheckedError ( ) ;
key_refs . clear ( ) ;
blob_reqs . clear ( ) ;
offsets . clear ( ) ;
sizes . clear ( ) ;
statuses . clear ( ) ;
values . clear ( ) ;
for ( size_t i = 0 ; i < num_blobs ; + + i ) {
for ( size_t i = 0 ; i < num_blobs ; + + i ) {
key_refs . emplace_back ( std : : cref ( keys [ i ] ) ) ;
blob_reqs . emplace_back ( keys [ i ] , blob_offsets [ i ] , blob_sizes [ i ] ,
offsets . push_back ( blob_offsets [ i ] ) ;
kNoCompression , & value_buf [ i ] , & statuses_buf [ i ] ) ;
sizes . push_back ( blob_sizes [ i ] ) ;
statuses . push_back ( & statuses_buf [ i ] ) ;
values . push_back ( & value_buf [ i ] ) ;
}
}
blob_source . MultiGetBlob ( read_options , key_refs , blob_file_number ,
blob_source . MultiGetBlobFromOneFile ( read_options , blob_file_number ,
file_size , offsets , sizes , statuses , values ,
file_size , blob_reqs , & bytes_read ) ;
& bytes_read ) ;
uint64_t blob_bytes = 0 ;
uint64_t blob_bytes = 0 ;
for ( size_t i = 0 ; i < num_blobs ; + + i ) {
for ( size_t i = 0 ; i < num_blobs ; + + i ) {
@ -759,24 +935,17 @@ TEST_F(BlobSourceTest, MultiGetBlobsFromCache) {
options_ . blob_cache - > EraseUnRefEntries ( ) ;
options_ . blob_cache - > EraseUnRefEntries ( ) ;
{
{
// Cache-only MultiGetBlob
// Cache-only MultiGetBlobFromOneFile
uint64_t bytes_read = 0 ;
uint64_t bytes_read = 0 ;
read_options . read_tier = ReadTier : : kBlockCacheTier ;
read_options . read_tier = ReadTier : : kBlockCacheTier ;
autovector < std : : reference_wrapper < const Slice > > key_refs ;
autovector < uint64_t > offsets ;
autovector < uint64_t > sizes ;
std : : array < Status , num_blobs > statuses_buf ;
std : : array < Status , num_blobs > statuses_buf ;
autovector < Status * > statuses ;
std : : array < PinnableSlice , num_blobs > value_buf ;
std : : array < PinnableSlice , num_blobs > value_buf ;
autovector < PinnableSlice * > value s;
autovector < BlobReadRequest > blob_reqs ;
for ( size_t i = 0 ; i < num_blobs ; i + + ) {
for ( size_t i = 0 ; i < num_blobs ; i + + ) {
key_refs . emplace_back ( std : : cref ( keys [ i ] ) ) ;
blob_reqs . emplace_back ( keys [ i ] , blob_offsets [ i ] , blob_sizes [ i ] ,
offsets . push_back ( blob_offsets [ i ] ) ;
kNoCompression , & value_buf [ i ] , & statuses_buf [ i ] ) ;
sizes . push_back ( blob_sizes [ i ] ) ;
statuses . push_back ( & statuses_buf [ i ] ) ;
values . push_back ( & value_buf [ i ] ) ;
ASSERT_FALSE ( blob_source . TEST_BlobInCache ( blob_file_number , file_size ,
ASSERT_FALSE ( blob_source . TEST_BlobInCache ( blob_file_number , file_size ,
blob_offsets [ i ] ) ) ;
blob_offsets [ i ] ) ) ;
}
}
@ -784,9 +953,8 @@ TEST_F(BlobSourceTest, MultiGetBlobsFromCache) {
get_perf_context ( ) - > Reset ( ) ;
get_perf_context ( ) - > Reset ( ) ;
statistics - > Reset ( ) . PermitUncheckedError ( ) ;
statistics - > Reset ( ) . PermitUncheckedError ( ) ;
blob_source . MultiGetBlob ( read_options , key_refs , blob_file_number ,
blob_source . MultiGetBlobFromOneFile ( read_options , blob_file_number ,
file_size , offsets , sizes , statuses , values ,
file_size , blob_reqs , & bytes_read ) ;
& bytes_read ) ;
for ( size_t i = 0 ; i < num_blobs ; + + i ) {
for ( size_t i = 0 ; i < num_blobs ; + + i ) {
ASSERT_TRUE ( statuses_buf [ i ] . IsIncomplete ( ) ) ;
ASSERT_TRUE ( statuses_buf [ i ] . IsIncomplete ( ) ) ;
@ -809,40 +977,33 @@ TEST_F(BlobSourceTest, MultiGetBlobsFromCache) {
}
}
{
{
// MultiGetBlob from non-existing file
// MultiGetBlobFromOneFile from non-existing file
uint64_t bytes_read = 0 ;
uint64_t bytes_read = 0 ;
uint64_t file_number = 100 ; // non-existing file
uint64_t non_existing_ file_number = 100 ;
read_options . read_tier = ReadTier : : kReadAllTier ;
read_options . read_tier = ReadTier : : kReadAllTier ;
autovector < std : : reference_wrapper < const Slice > > key_refs ;
autovector < uint64_t > offsets ;
autovector < uint64_t > sizes ;
std : : array < Status , num_blobs > statuses_buf ;
std : : array < Status , num_blobs > statuses_buf ;
autovector < Status * > statuses ;
std : : array < PinnableSlice , num_blobs > value_buf ;
std : : array < PinnableSlice , num_blobs > value_buf ;
autovector < PinnableSlice * > value s;
autovector < BlobReadRequest > blob_reqs ;
for ( size_t i = 0 ; i < num_blobs ; i + + ) {
for ( size_t i = 0 ; i < num_blobs ; i + + ) {
key_refs . emplace_back ( std : : cref ( keys [ i ] ) ) ;
blob_reqs . emplace_back ( keys [ i ] , blob_offsets [ i ] , blob_sizes [ i ] ,
offsets . push_back ( blob_offsets [ i ] ) ;
kNoCompression , & value_buf [ i ] , & statuses_buf [ i ] ) ;
sizes . push_back ( blob_sizes [ i ] ) ;
ASSERT_FALSE ( blob_source . TEST_BlobInCache ( non_existing_file_number ,
statuses . push_back ( & statuses_buf [ i ] ) ;
file_size , blob_offsets [ i ] ) ) ;
values . push_back ( & value_buf [ i ] ) ;
ASSERT_FALSE ( blob_source . TEST_BlobInCache ( file_number , file_size ,
blob_offsets [ i ] ) ) ;
}
}
get_perf_context ( ) - > Reset ( ) ;
get_perf_context ( ) - > Reset ( ) ;
statistics - > Reset ( ) . PermitUncheckedError ( ) ;
statistics - > Reset ( ) . PermitUncheckedError ( ) ;
blob_source . MultiGetBlob ( read_options , key_refs , file_number , file_size ,
blob_source . MultiGetBlobFromOneFile ( read_options , non_existing_file_number ,
offsets , sizes , statuses , value s, & bytes_read ) ;
file_size , blob_req s , & bytes_read ) ;
for ( size_t i = 0 ; i < num_blobs ; + + i ) {
for ( size_t i = 0 ; i < num_blobs ; + + i ) {
ASSERT_TRUE ( statuses_buf [ i ] . IsIOError ( ) ) ;
ASSERT_TRUE ( statuses_buf [ i ] . IsIOError ( ) ) ;
ASSERT_TRUE ( value_buf [ i ] . empty ( ) ) ;
ASSERT_TRUE ( value_buf [ i ] . empty ( ) ) ;
ASSERT_FALSE ( blob_source . TEST_BlobInCache ( file_number , file_size ,
ASSERT_FALSE ( blob_source . TEST_BlobInCache ( non_existing_ file_number,
blob_offsets [ i ] ) ) ;
file_size , blob_offsets [ i ] ) ) ;
}
}
ASSERT_EQ ( ( int ) get_perf_context ( ) - > blob_cache_hit_count , 0 ) ;
ASSERT_EQ ( ( int ) get_perf_context ( ) - > blob_cache_hit_count , 0 ) ;