@ -6,6 +6,9 @@
// Copyright (c) 2011 The LevelDB Authors. All rights reserved.
// Copyright (c) 2011 The LevelDB Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// 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.
// found in the LICENSE file. See the AUTHORS file for names of contributors.
# include <limits>
# include <string>
# include "db/db_test_util.h"
# include "db/db_test_util.h"
# include "port/stack_trace.h"
# include "port/stack_trace.h"
# include "util/sync_point.h"
# include "util/sync_point.h"
@ -20,80 +23,101 @@ class DBOptionsTest : public DBTestBase {
// RocksDB lite don't support dynamic options.
// RocksDB lite don't support dynamic options.
# ifndef ROCKSDB_LITE
# ifndef ROCKSDB_LITE
// When write stalls, user can enable auto compaction to unblock writes.
TEST_F ( DBOptionsTest , EnableAutoCompactionAndTriggerStall ) {
// However, we had an issue where the stalled write thread blocks the attempt
const std : : string kValue ( 1024 , ' v ' ) ;
// to persist auto compaction option, thus creating a deadlock. The test
for ( int method_type = 0 ; method_type < 2 ; method_type + + ) {
// verifies the issue is fixed.
for ( int option_type = 0 ; option_type < 4 ; option_type + + ) {
TEST_F ( DBOptionsTest , EnableAutoCompactionToUnblockWrites ) {
Options options ;
Options options ;
options . create_if_missing = true ;
options . disable_auto_compactions = true ;
options . disable_auto_compactions = true ;
options . write_buffer_size = 1000 * 1000 ; // 1M
options . write_buffer_size = 1024 * 1024 ;
options . level0_file_num_compaction_trigger = 1 ;
options . compression = CompressionType : : kNoCompression ;
options . level0_slowdown_writes_trigger = 1 ;
options . level0_file_num_compaction_trigger = 1 ;
options . level0_stop_writes_trigger = 1 ;
options . level0_stop_writes_trigger = std : : numeric_limits < int > : : max ( ) ;
options . compression = kNoCompression ;
options . level0_slowdown_writes_trigger = std : : numeric_limits < int > : : max ( ) ;
options . hard_pending_compaction_bytes_limit =
std : : numeric_limits < uint64_t > : : max ( ) ;
options . soft_pending_compaction_bytes_limit =
std : : numeric_limits < uint64_t > : : max ( ) ;
SyncPoint : : GetInstance ( ) - > LoadDependency (
DestroyAndReopen ( options ) ;
{ { " DBImpl::DelayWrite:Wait " ,
for ( int i = 0 ; i < 1024 * 2 ; i + + ) {
" DBOptionsTest::EnableAutoCompactionToUnblockWrites:1 " } ,
Put ( Key ( i ) , kValue ) ;
{ " DBImpl::BackgroundCompaction:Finish " ,
}
" DBOptionsTest::EnableAutoCompactionToUnblockWrites:1 " } } ) ;
dbfull ( ) - > TEST_WaitForFlushMemTable ( ) ;
SyncPoint : : GetInstance ( ) - > EnableProcessing ( ) ;
ASSERT_EQ ( 2 , NumTableFilesAtLevel ( 0 ) ) ;
uint64_t l0_size = SizeAtLevel ( 0 ) ;
// Stall writes.
switch ( option_type ) {
Reopen ( options ) ;
case 0 :
env_ - > StartThread (
// test with level0_stop_writes_trigger
[ ] ( void * arg ) {
options . level0_stop_writes_trigger = 2 ;
std : : string value ( 1000 , ' v ' ) ;
options . level0_slowdown_writes_trigger = 2 ;
auto * t = static_cast < DBOptionsTest * > ( arg ) ;
break ;
for ( int i = 0 ; i < 2000 ; i + + ) {
case 1 :
ASSERT_OK ( t - > Put ( t - > Key ( i ) , value ) ) ;
options . level0_slowdown_writes_trigger = 2 ;
}
break ;
} ,
case 2 :
this ) ;
options . hard_pending_compaction_bytes_limit = l0_size ;
TEST_SYNC_POINT ( " DBOptionsTest::EnableAutoCompactionToUnblockWrites:1 " ) ;
options . soft_pending_compaction_bytes_limit = l0_size ;
ASSERT_TRUE ( dbfull ( ) - > TEST_write_controler ( ) . IsStopped ( ) ) ;
break ;
ColumnFamilyHandle * handle = dbfull ( ) - > DefaultColumnFamily ( ) ;
case 3 :
// We will get a deadlock here if we hit the issue.
options . soft_pending_compaction_bytes_limit = l0_size ;
ASSERT_OK ( dbfull ( ) - > EnableAutoCompaction ( { handle } ) ) ;
break ;
env_ - > WaitForJoin ( ) ;
}
}
Reopen ( options ) ;
dbfull ( ) - > TEST_WaitForCompact ( ) ;
ASSERT_FALSE ( dbfull ( ) - > TEST_write_controler ( ) . IsStopped ( ) ) ;
ASSERT_FALSE ( dbfull ( ) - > TEST_write_controler ( ) . NeedsDelay ( ) ) ;
SyncPoint : : GetInstance ( ) - > LoadDependency (
{ { " DBOptionsTest::EnableAutoCompactionAndTriggerStall:1 " ,
" BackgroundCallCompaction:0 " } ,
{ " DBImpl::BackgroundCompaction():BeforePickCompaction " ,
" DBOptionsTest::EnableAutoCompactionAndTriggerStall:2 " } ,
{ " DBOptionsTest::EnableAutoCompactionAndTriggerStall:3 " ,
" DBImpl::BackgroundCompaction():AfterPickCompaction " } } ) ;
// Block background compaction.
SyncPoint : : GetInstance ( ) - > EnableProcessing ( ) ;
// Similar to EnableAutoCompactionAfterStallDeadlock. See comments there.
switch ( method_type ) {
TEST_F ( DBOptionsTest , ToggleStopTriggerToUnblockWrites ) {
case 0 :
Options options ;
ASSERT_OK (
options . disable_auto_compactions = true ;
dbfull ( ) - > SetOptions ( { { " disable_auto_compactions " , " false " } } ) ) ;
options . write_buffer_size = 1000 * 1000 ; // 1M
break ;
options . level0_file_num_compaction_trigger = 1 ;
case 1 :
options . level0_slowdown_writes_trigger = 1 ;
ASSERT_OK ( dbfull ( ) - > EnableAutoCompaction (
options . level0_stop_writes_trigger = 1 ;
{ dbfull ( ) - > DefaultColumnFamily ( ) } ) ) ;
options . compression = kNoCompression ;
break ;
}
TEST_SYNC_POINT ( " DBOptionsTest::EnableAutoCompactionAndTriggerStall:1 " ) ;
// Wait for stall condition recalculate.
TEST_SYNC_POINT ( " DBOptionsTest::EnableAutoCompactionAndTriggerStall:2 " ) ;
SyncPoint : : GetInstance ( ) - > LoadDependency (
switch ( option_type ) {
{ { " DBImpl::DelayWrite:Wait " ,
case 0 :
" DBOptionsTest::ToggleStopTriggerToUnblockWrites:1 " } ,
ASSERT_TRUE ( dbfull ( ) - > TEST_write_controler ( ) . IsStopped ( ) ) ;
{ " DBImpl::BackgroundCompaction:Finish " ,
break ;
" DBOptionsTest::ToggleStopTriggerToUnblockWrites:1 " } } ) ;
case 1 :
SyncPoint : : GetInstance ( ) - > EnableProcessing ( ) ;
ASSERT_FALSE ( dbfull ( ) - > TEST_write_controler ( ) . IsStopped ( ) ) ;
ASSERT_TRUE ( dbfull ( ) - > TEST_write_controler ( ) . NeedsDelay ( ) ) ;
break ;
case 2 :
ASSERT_TRUE ( dbfull ( ) - > TEST_write_controler ( ) . IsStopped ( ) ) ;
break ;
case 3 :
ASSERT_FALSE ( dbfull ( ) - > TEST_write_controler ( ) . IsStopped ( ) ) ;
ASSERT_TRUE ( dbfull ( ) - > TEST_write_controler ( ) . NeedsDelay ( ) ) ;
break ;
}
TEST_SYNC_POINT ( " DBOptionsTest::EnableAutoCompactionAndTriggerStall:3 " ) ;
// Stall writes.
// Background compaction executed.
Reopen ( options ) ;
dbfull ( ) - > TEST_WaitForCompact ( ) ;
env_ - > StartThread (
ASSERT_FALSE ( dbfull ( ) - > TEST_write_controler ( ) . IsStopped ( ) ) ;
[ ] ( void * arg ) {
ASSERT_FALSE ( dbfull ( ) - > TEST_write_controler ( ) . NeedsDelay ( ) ) ;
std : : string value ( 1000 , ' v ' ) ;
}
auto * t = static_cast < DBOptionsTest * > ( arg ) ;
}
for ( int i = 0 ; i < 2000 ; i + + ) {
ASSERT_OK ( t - > Put ( t - > Key ( i ) , value ) ) ;
}
} ,
this ) ;
TEST_SYNC_POINT ( " DBOptionsTest::ToggleStopTriggerToUnblockWrites:1 " ) ;
ASSERT_TRUE ( dbfull ( ) - > TEST_write_controler ( ) . IsStopped ( ) ) ;
// We will get a deadlock here if we hit the issue.
ASSERT_OK (
dbfull ( ) - > SetOptions ( { { " level0_stop_writes_trigger " , " 1000000 " } ,
{ " level0_slowdown_writes_trigger " , " 1000000 " } } ) ) ;
env_ - > WaitForJoin ( ) ;
}
}
# endif // ROCKSDB_LITE
# endif // ROCKSDB_LITE