Skip to content

Commit

Permalink
Fix[MQB]: respect protocol file size limits
Browse files Browse the repository at this point in the history
Signed-off-by: Evgeny Malygin <[email protected]>
  • Loading branch information
678098 committed Dec 6, 2024
1 parent 94d9784 commit 9789a59
Show file tree
Hide file tree
Showing 5 changed files with 245 additions and 35 deletions.
36 changes: 35 additions & 1 deletion src/groups/mqb/mqbblp/mqbblp_storagemanager.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1320,7 +1320,10 @@ int StorageManager::start(bsl::ostream& errorDescription)
rc_RECOVERY_MANAGER_FAILURE = -2,
rc_FILE_STORE_OPEN_FAILURE = -3,
rc_FILE_STORE_RECOVERY_FAILURE = -4,
rc_NOT_ENOUGH_DISK_SPACE = -5
rc_NOT_ENOUGH_DISK_SPACE = -5,
rc_OVERFLOW_MAX_DATA_FILE_SIZE = -6,
rc_OVERFLOW_MAX_JOURNAL_FILE_SIZE = -7,
rc_OVERFLOW_MAX_QLIST_FILE_SIZE = -8
};

BALL_LOG_INFO << d_clusterData_p->identity().description()
Expand All @@ -1330,6 +1333,37 @@ int StorageManager::start(bsl::ostream& errorDescription)
const mqbcfg::PartitionConfig& partitionCfg =
d_clusterConfig.partitionConfig();

// Validate file size limits before checking the available disk space
if (partitionCfg.maxDataFileSize() >
mqbs::FileStoreProtocol::k_MAX_DATA_FILE_SIZE_HARD) {
BALL_LOG_ERROR << "Configured maxDataFileSize ("
<< partitionCfg.maxDataFileSize()
<< ") exceeds the protocol limit ("
<< mqbs::FileStoreProtocol::k_MAX_DATA_FILE_SIZE_HARD
<< ")";
return rc_OVERFLOW_MAX_DATA_FILE_SIZE;
}

if (partitionCfg.maxJournalFileSize() >
mqbs::FileStoreProtocol::k_MAX_JOURNAL_FILE_SIZE_HARD) {
BALL_LOG_ERROR << "Configured maxJournalFileSize ("
<< partitionCfg.maxJournalFileSize()
<< ") exceeds the protocol limit ("
<< mqbs::FileStoreProtocol::k_MAX_JOURNAL_FILE_SIZE_HARD
<< ")";
return rc_OVERFLOW_MAX_JOURNAL_FILE_SIZE;
}

if (partitionCfg.maxQlistFileSize() >
mqbs::FileStoreProtocol::k_MAX_QLIST_FILE_SIZE_HARD) {
BALL_LOG_ERROR << "Configured maxQlistFileSize ("
<< partitionCfg.maxQlistFileSize()
<< ") exceeds the protocol limit ("
<< mqbs::FileStoreProtocol::k_MAX_QLIST_FILE_SIZE_HARD
<< ")";
return rc_OVERFLOW_MAX_QLIST_FILE_SIZE;
}

int rc = mqbc::StorageUtil::validatePartitionDirectory(partitionCfg,
errorDescription);
if (rc != rc_SUCCESS) {
Expand Down
48 changes: 45 additions & 3 deletions src/groups/mqb/mqbc/mqbc_storagemanager.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3453,12 +3453,23 @@ int StorageManager::start(bsl::ostream& errorDescription)
BSLS_ASSERT_SAFE(d_dispatcher_p->inDispatcherThread(d_cluster_p));

enum RcEnum {
// Value for the various RC error categories
// Value for the various RC error categories.
// RESERVED returned code is not used here, but kept for consistency
// with `mqbblp::StorageManager::start` return code:
// - rc_THREAD_POOL_START_FAILURE = rc_FILE_STORE_OPEN_FAILURE
// - rc_RESERVED = rc_FILE_STORE_RECOVERY_FAILURE
// TODO: `mqbblp::StorageManager` is used in non-FSM mode only, if/when
// we support only FSM, we can remove these RESERVED codes
// together with `mqbblp::StorageManager`.
rc_SUCCESS = 0,
rc_PARTITION_LOCATION_NONEXISTENT = -1,
rc_NOT_ENOUGH_DISK_SPACE = -2,
rc_RECOVERY_MANAGER_FAILURE = -2,
rc_THREAD_POOL_START_FAILURE = -3,
rc_RECOVERY_MANAGER_FAILURE = -4
rc_RESERVED = -4,
rc_NOT_ENOUGH_DISK_SPACE = -5,
rc_OVERFLOW_MAX_DATA_FILE_SIZE = -6,
rc_OVERFLOW_MAX_JOURNAL_FILE_SIZE = -7,
rc_OVERFLOW_MAX_QLIST_FILE_SIZE = -8
};

BALL_LOG_INFO << d_clusterData_p->identity().description()
Expand All @@ -3468,6 +3479,37 @@ int StorageManager::start(bsl::ostream& errorDescription)
const mqbcfg::PartitionConfig& partitionCfg =
d_clusterConfig.partitionConfig();

// Validate file size limits before checking the available disk space
if (partitionCfg.maxDataFileSize() >
mqbs::FileStoreProtocol::k_MAX_DATA_FILE_SIZE_HARD) {
BALL_LOG_ERROR << "Configured maxDataFileSize ("
<< partitionCfg.maxDataFileSize()
<< ") exceeds the protocol limit ("
<< mqbs::FileStoreProtocol::k_MAX_DATA_FILE_SIZE_HARD
<< ")";
return rc_OVERFLOW_MAX_DATA_FILE_SIZE;
}

if (partitionCfg.maxJournalFileSize() >
mqbs::FileStoreProtocol::k_MAX_JOURNAL_FILE_SIZE_HARD) {
BALL_LOG_ERROR << "Configured maxJournalFileSize ("
<< partitionCfg.maxJournalFileSize()
<< ") exceeds the protocol limit ("
<< mqbs::FileStoreProtocol::k_MAX_JOURNAL_FILE_SIZE_HARD
<< ")";
return rc_OVERFLOW_MAX_JOURNAL_FILE_SIZE;
}

if (partitionCfg.maxQlistFileSize() >
mqbs::FileStoreProtocol::k_MAX_QLIST_FILE_SIZE_HARD) {
BALL_LOG_ERROR << "Configured maxQlistFileSize ("
<< partitionCfg.maxQlistFileSize()
<< ") exceeds the protocol limit ("
<< mqbs::FileStoreProtocol::k_MAX_QLIST_FILE_SIZE_HARD
<< ")";
return rc_OVERFLOW_MAX_QLIST_FILE_SIZE;
}

int rc = StorageUtil::validatePartitionDirectory(partitionCfg,
errorDescription);
if (rc != rc_SUCCESS) {
Expand Down
118 changes: 110 additions & 8 deletions src/groups/mqb/mqbc/mqbc_storagemanager.t.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2892,6 +2892,109 @@ static void test18_primaryHealingStage1SelfHighestSendsDataChunks()
helper.d_cluster_mp->stop();
}

static void test19_fileSizesHardLimits()
// ------------------------------------------------------------------------
// FILE SIZES HARD LIMITS
//
// Concerns:
// Ensure StorageManager is able to early-detect overflow in the file sizes
// configuration and gracefully return an error code on start.
//
// Plan:
// Try to start mqbc::StorageManager with different partition configurations
// and check that it respects hard limits on the file sizes.
//
// ------------------------------------------------------------------------
{
bmqtst::TestHelper::printTestName("FILE SIZES HARD LIMITS");

TestHelper helper;

mqbs::DataStoreRecordHandle handle;
helper.initializeRecords(&handle, 1);

struct LocalFuncs {
static void testFileSizes(int line,
TestHelper& helper,
bsls::Types::Uint64 dataFileSize,
bsls::Types::Uint64 journalFileSize,
bsls::Types::Uint64 qlistFileSize,
bool expectFailure)
{
// Make a copy to allow modification
mqbcfg::ClusterDefinition clusterDef =
helper.d_cluster_mp->_clusterDefinition();
clusterDef.partitionConfig().maxDataFileSize() = dataFileSize;
clusterDef.partitionConfig().maxJournalFileSize() =
journalFileSize;
clusterDef.partitionConfig().maxQlistFileSize() = qlistFileSize;

mqbc::StorageManager storageManager(
clusterDef,
helper.d_cluster_mp.get(),
helper.d_cluster_mp->_clusterData(),
helper.d_cluster_mp->_state(),
helper.d_cluster_mp->_clusterData()->domainFactory(),
helper.d_cluster_mp->dispatcher(),
k_WATCHDOG_TIMEOUT_DURATION,
mockOnRecoveryStatus,
mockOnPartitionPrimaryStatus,
bmqtst::TestHelperUtil::allocator());

bmqu::MemOutStream errorDescription(
bmqtst::TestHelperUtil::allocator());
const int rc = storageManager.start(errorDescription);
if (rc == 0) {
storageManager.stop();
}
ASSERT_EQ_D("line: " << line << ", expected failure: "
<< expectFailure << ", rc: " << rc,
expectFailure,
(rc != 0));
}
};

LocalFuncs::testFileSizes(L_, helper, 1000ULL, 1000ULL, 1000ULL, false);
LocalFuncs::testFileSizes(
L_,
helper,
mqbs::FileStoreProtocol::k_MAX_DATA_FILE_SIZE_HARD,
mqbs::FileStoreProtocol::k_MAX_JOURNAL_FILE_SIZE_HARD,
mqbs::FileStoreProtocol::k_MAX_QLIST_FILE_SIZE_HARD,
false);
LocalFuncs::testFileSizes(
L_,
helper,
mqbs::FileStoreProtocol::k_MAX_DATA_FILE_SIZE_HARD + 1,
mqbs::FileStoreProtocol::k_MAX_JOURNAL_FILE_SIZE_HARD,
mqbs::FileStoreProtocol::k_MAX_QLIST_FILE_SIZE_HARD,
true);
LocalFuncs::testFileSizes(
L_,
helper,
mqbs::FileStoreProtocol::k_MAX_DATA_FILE_SIZE_HARD,
mqbs::FileStoreProtocol::k_MAX_JOURNAL_FILE_SIZE_HARD + 1,
mqbs::FileStoreProtocol::k_MAX_QLIST_FILE_SIZE_HARD,
true);
LocalFuncs::testFileSizes(
L_,
helper,
mqbs::FileStoreProtocol::k_MAX_DATA_FILE_SIZE_HARD,
mqbs::FileStoreProtocol::k_MAX_JOURNAL_FILE_SIZE_HARD,
mqbs::FileStoreProtocol::k_MAX_QLIST_FILE_SIZE_HARD + 1,
true);
LocalFuncs::testFileSizes(
L_,
helper,
mqbs::FileStoreProtocol::k_MAX_DATA_FILE_SIZE_HARD << 8,
mqbs::FileStoreProtocol::k_MAX_JOURNAL_FILE_SIZE_HARD << 8,
mqbs::FileStoreProtocol::k_MAX_QLIST_FILE_SIZE_HARD << 8,
true);

// Stop the cluster
helper.d_cluster_mp->stop();
}

// ============================================================================
// MAIN PROGRAM
// ----------------------------------------------------------------------------
Expand All @@ -2905,14 +3008,13 @@ int main(int argc, char* argv[])

switch (_testCase) {
case 0:
// case 23:
// test23_primaryHealingStage2SendsReplicaDataRqstPushDrop();
// break; case 22: test22_replicaHealingDetectSelfPrimary();
// break; case 21:
// test21_replicaHealingReceivesReplicaDataRqstDrop();
// break; case 20:
// test20_replicaHealingReceivesReplicaDataRqstPush();
// break; case 19: test19_primaryHealedSendsDataChunks(); break;
// TODO: overview the removed tests or remove this comment
// - test23_primaryHealingStage2SendsReplicaDataRqstPushDrop();
// - test22_replicaHealingDetectSelfPrimary();
// - test21_replicaHealingReceivesReplicaDataRqstDrop();
// - test20_replicaHealingReceivesReplicaDataRqstPush();
// - test19_primaryHealedSendsDataChunks();
case 19: test19_fileSizesHardLimits(); break;
case 18: test18_primaryHealingStage1SelfHighestSendsDataChunks(); break;
case 17: test17_replicaHealingReceivesReplicaDataRqstPull(); break;
case 16: test16_replicaHealingReceivesPrimaryStateRqst(); break;
Expand Down
30 changes: 24 additions & 6 deletions src/groups/mqb/mqbs/mqbs_filestoreprotocol.h
Original file line number Diff line number Diff line change
Expand Up @@ -59,12 +59,15 @@
// Max JournalFileHeader size.....................: 1020 bytes
// Max QlistFileHeader size.......................: 1020 bytes
// Max QueueRecordHeader size.....................: 1020 bytes
// Max Data file size.............................: 32GB
// (courtesy MessageRecord.d_messageOffsetDwords)
// Max Journal file size..........................: 16GB
// (courtesy bmqp::StorageHeader.d_journalOffsetWords)
// Max Qlist file size............................: 16GB
// (courtesy QueueOpRecord.d_queueUriRecordOffsetWords)
// Max Data file size.............................: 34359738360 bytes
// or 32GB - 8B
// (See also: mqbs::FileStoreProtocol::k_MAX_DATA_FILE_SIZE_HARD)
// Max Journal file size..........................: 17179869180 bytes
// or 16GB - 8B
// (See also: mqbs::FileStoreProtocol::k_MAX_JOURNAL_FILE_SIZE_HARD)
// Max Qlist file size............................: 17179869180 bytes
// or 16GB - 8B
// (See also: mqbs::FileStoreProtocol::k_MAX_QLIST_FILE_SIZE_HARD)
// Max Journal Record size........................: 1020 bytes
// Max Journal Record types.......................: 15
//
Expand Down Expand Up @@ -160,6 +163,21 @@ struct FileStoreProtocol {
static const int k_NUM_FILES_PER_PARTITION = 3;
// Number of files per partition (data, journal & qlist)

/// The unsigned 32-bit `mqbs::MessageRecord::d_messageOffsetDwords` allows
/// to address at most `((2 ^ 32) - 1) * 8` byte offset
static const bsls::Types::Uint64 k_MAX_DATA_FILE_SIZE_HARD =
34359738360ULL;

/// The unsigned 32-bit `bmqp::StorageHeader::d_journalOffsetWords` allows
/// to address at most `((2 ^ 32) - 1) * 4` byte offset
static const bsls::Types::Uint64 k_MAX_JOURNAL_FILE_SIZE_HARD =
17179869180ULL;

/// The unsigned 32-bit `mqbs::QueueOpRecord::d_queueUriRecordOffsetWords`
/// allows to address at most `((2 ^ 32) - 1) * 4` byte offset
static const bsls::Types::Uint64 k_MAX_QLIST_FILE_SIZE_HARD =
17179869180ULL;

static const char* k_DATA_FILE_EXTENSION;

static const char* k_JOURNAL_FILE_EXTENSION;
Expand Down
Loading

0 comments on commit 9789a59

Please sign in to comment.