Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

FCBIS v7.0 #1345

Draft
wants to merge 26 commits into
base: v7.0
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
26 commits
Select commit Hold shift + click to select a range
701fe57
PSMDB-1284 implement $_backupFile aggregation stage
igorsol Jan 31, 2024
a0ccbc0
PSMDB-1284 specify read concern capability of $backupCursor & $backup…
igorsol Feb 2, 2024
554ad1e
PSMDB-1284 Clone InitialSyncer to InitialSyncerFCB with fileCopyBased…
igorsol Apr 12, 2024
4d4cd94
PSMDB-1284 do not set initial sync flag
igorsol Apr 22, 2024
6289219
PSMDB-1284 remove most parts related to logical sync
igorsol May 12, 2024
a5903c4
PSMDB-1284 get list of local files via $backupCursor
igorsol May 28, 2024
d86f9e7
PSMDB-1284 Invoke $backupCursor on the sync source
igorsol May 31, 2024
b905f2e
PSMDB-1284 implement file cloning from the sync source
igorsol Jun 10, 2024
01603b1
PSMDB-1284 Implement switching storage locations and files moving
igorsol Jun 11, 2024
613b0db
PSMDB-1284 cleanup 'local' db
igorsol Jun 17, 2024
108fd0f
PSMDB-1284 cleanup 'local' db part 2
igorsol Jun 18, 2024
501d7b9
PSMDB-1284 cleanup comments about reconstruct transactions
igorsol Jun 18, 2024
ea20d65
PSMDB-1284 kill backup cursor on the sync source
igorsol Jun 21, 2024
6281d25
PSMDB_1284 kill local backup cursor
igorsol Jun 24, 2024
7228c33
PSMDB-1284 make FCBIS pro-build feature
igorsol Jul 1, 2024
f77bf4e
PSMDB-1284 make logv2 ids unique
igorsol Jul 29, 2024
20f6edb
PSMDB-1477 Do not assert on initial sync attempt failure
igorsol Jul 29, 2024
be543b7
PSMDB-1477 Ensure fallback to logical by returning InvalidSyncSource
igorsol Aug 14, 2024
5e1c7fd
PSMDB-1477 fix method name in file copy-based initial syncer
igorsol Aug 14, 2024
103c78e
PSMDB-1479 Do all transformations under single global lock
igorsol Aug 26, 2024
2ce857e
PSMDB-1478 use correct recovery mode after changing storage location
igorsol Sep 18, 2024
750052d
PSMDB-1478 add missing NotifyStartupComplete after changing storage l…
igorsol Sep 18, 2024
7158ffb
PSMDB-1478 Execute recovery on first start from backup
igorsol Sep 20, 2024
cb9ce67
PSMDB-1522 Prevent storage change deadlock by getInitialSyncProgress
igorsol Oct 12, 2024
a3259f3
PSMDB-1522 In case of terminal shutdown wait for end of storage change
igorsol Oct 12, 2024
f9487f4
PSMDB-1542 Fix initial sync method name in statistics log message
igorsol Oct 17, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 10 additions & 0 deletions SConstruct
Original file line number Diff line number Diff line change
Expand Up @@ -296,6 +296,12 @@ add_option(
nargs=0,
)

add_option(
'enable-fcbis',
help='Enable file copy-based initial sync',
nargs=0,
)

add_option(
'full-featured',
help='Enable all optional features',
Expand Down Expand Up @@ -2716,6 +2722,10 @@ if has_option('enable-fipsmode') or has_option('full-featured'):
env.SetConfigHeaderDefine("PERCONA_FIPSMODE_ENABLED")
env['PSMDB_PRO_FEATURES'].append('FIPSMode')

if has_option('enable-fcbis') or has_option('full-featured'):
env.SetConfigHeaderDefine("PERCONA_FCBIS_ENABLED")
env['PSMDB_PRO_FEATURES'].append('FCBIS')

env.Tool('forceincludes')

# ---- other build setup -----
Expand Down
1 change: 1 addition & 0 deletions src/mongo/SConscript
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,7 @@ config_header_substs = (
('@mongo_config_wiredtiger_enabled@', 'MONGO_CONFIG_WIREDTIGER_ENABLED'),
('@mongo_config_grpc@', 'MONGO_CONFIG_GRPC'),
('@percona_fipsmode_enabled@', 'PERCONA_FIPSMODE_ENABLED'),
('@percona_fcbis_enabled@', 'PERCONA_FCBIS_ENABLED'),
)


Expand Down
3 changes: 3 additions & 0 deletions src/mongo/config.h.in
Original file line number Diff line number Diff line change
Expand Up @@ -112,3 +112,6 @@

// FIPSMode enabled
@percona_fipsmode_enabled@

// FCBIS enabled
@percona_fcbis_enabled@
1 change: 1 addition & 0 deletions src/mongo/db/SConscript
Original file line number Diff line number Diff line change
Expand Up @@ -2519,6 +2519,7 @@ env.Library(
'query/stats/stats',
'repl/drop_pending_collection_reaper',
'repl/initial_syncer',
'repl/initial_syncer_fcb' if has_option('enable-fcbis') or has_option('full-featured') else [],
'repl/repl_coordinator_impl',
'repl/replication_recovery',
'repl/serveronly_repl',
Expand Down
1 change: 1 addition & 0 deletions src/mongo/db/pipeline/SConscript
Original file line number Diff line number Diff line change
Expand Up @@ -244,6 +244,7 @@ env.Library(
source=[
'document_source_backup_cursor.cpp',
'document_source_backup_cursor_extend.cpp',
'document_source_backup_file.cpp',
],
LIBDEPS_PRIVATE=[
'$BUILD_DIR/mongo/db/server_base',
Expand Down
7 changes: 6 additions & 1 deletion src/mongo/db/pipeline/document_source_backup_cursor.h
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,12 @@ class DocumentSourceBackupCursor : public DocumentSource {
return true;
}

void assertSupportsMultiDocumentTransaction() const {
ReadConcernSupportResult supportsReadConcern(repl::ReadConcernLevel level,
bool isImplicitDefault) const final {
return onlyReadConcernLocalSupported(kStageName, level, isImplicitDefault);
}

void assertSupportsMultiDocumentTransaction() const final {
transactionNotSupported(kStageName);
}
};
Expand Down
7 changes: 6 additions & 1 deletion src/mongo/db/pipeline/document_source_backup_cursor_extend.h
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,12 @@ class DocumentSourceBackupCursorExtend : public DocumentSource {
return true;
}

void assertSupportsMultiDocumentTransaction() const {
ReadConcernSupportResult supportsReadConcern(repl::ReadConcernLevel level,
bool isImplicitDefault) const final {
return onlyReadConcernLocalSupported(kStageName, level, isImplicitDefault);
}

void assertSupportsMultiDocumentTransaction() const final {
transactionNotSupported(kStageName);
}
};
Expand Down
187 changes: 187 additions & 0 deletions src/mongo/db/pipeline/document_source_backup_file.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,187 @@
/*======
This file is part of Percona Server for MongoDB.

Copyright (C) 2024-present Percona and/or its affiliates. All rights reserved.

This program is free software: you can redistribute it and/or modify
it under the terms of the Server Side Public License, version 1,
as published by MongoDB, Inc.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
Server Side Public License for more details.

You should have received a copy of the Server Side Public License
along with this program. If not, see
<http://www.mongodb.com/licensing/server-side-public-license>.

As a special exception, the copyright holders give permission to link the
code of portions of this program with the OpenSSL library under certain
conditions as described in each individual source file and distribute
linked combinations including the program with the OpenSSL library. You
must comply with the Server Side Public License in all respects for
all of the code used other than as permitted herein. If you modify file(s)
with this exception, you may extend this exception to your version of the
file(s), but you are not obligated to do so. If you do not wish to do so,
delete this exception statement from your version. If you delete this
exception statement from all source files in the program, then also delete
it in the license file.
======= */

#define MONGO_LOGV2_DEFAULT_COMPONENT ::mongo::logv2::LogComponent::kQuery

#include "mongo/db/pipeline/document_source_backup_file.h"

#include <array>
#include <memory>
#include <string>
#include <utility>

#include "mongo/base/data_range.h"
#include "mongo/base/error_codes.h"
#include "mongo/base/string_data.h"
#include "mongo/bson/bsonmisc.h"
#include "mongo/bson/bsonobj.h"
#include "mongo/bson/bsontypes.h"
#include "mongo/db/exec/document_value/document.h"
#include "mongo/db/namespace_string.h"
#include "mongo/util/assert_util.h"
#include "mongo/util/intrusive_counter.h"
#include "mongo/util/str.h"
#include "mongo/util/uuid.h"

namespace mongo {

namespace {
constexpr StringData kBackupId = "backupId"_sd;
constexpr StringData kFile = "file"_sd;
constexpr StringData kByteOffset = "byteOffset"_sd;

// We only link this file into mongod so this stage doesn't exist in mongos
REGISTER_INTERNAL_DOCUMENT_SOURCE(_backupFile,
DocumentSourceBackupFile::LiteParsed::parse,
DocumentSourceBackupFile::createFromBson,
true);
} // namespace

using boost::intrusive_ptr;

std::unique_ptr<DocumentSourceBackupFile::LiteParsed> DocumentSourceBackupFile::LiteParsed::parse(
const NamespaceString& nss, const BSONElement& spec) {

return std::make_unique<DocumentSourceBackupFile::LiteParsed>(spec.fieldName());
}

const char* DocumentSourceBackupFile::getSourceName() const {
return kStageName.rawData();
}

Value DocumentSourceBackupFile::serialize(const SerializationOptions& opts) const {
return Value{Document{{getSourceName(),
Document{{kBackupId, Value(_backupId)},
{kFile, Value(_filePath)},
{kByteOffset, Value(_byteOffset)}}}}};
}

DocumentSource::GetNextResult DocumentSourceBackupFile::doGetNext() {
if (_file.eof()) {
return GetNextResult::makeEOF();
}

auto byteOffset = _file.tellg();
_file.read(_dataBuf.data(), kBlockSize);
uassert(ErrorCodes::FileStreamFailed,
str::stream() << "Error reading file " << _filePath << " at offset " << byteOffset,
!_file.bad());
auto bytesRead = _file.gcount();
auto eof = _file.eof();

Document doc;
doc = Document{{"byteOffset"_sd, static_cast<long long>(byteOffset)},
{"data"_sd, BSONBinData(_dataBuf.data(), bytesRead, BinDataGeneral)},
{"endOfFile"_sd, eof}};

return doc;
}

intrusive_ptr<DocumentSource> DocumentSourceBackupFile::createFromBson(
BSONElement spec, const intrusive_ptr<ExpressionContext>& pExpCtx) {
// This cursor is non-tailable so we don't touch pExpCtx->tailableMode here

uassert(ErrorCodes::FailedToParse,
str::stream() << kStageName << " parameters must be specified in an object, but found: "
<< typeName(spec.type()),
spec.type() == Object);

auto backupId = UUID::fromCDR(std::array<unsigned char, UUID::kNumBytes>{});
std::string filePath;
long long byteOffset = 0;

for (auto&& elem : spec.embeddedObject()) {
const auto fieldName = elem.fieldNameStringData();

if (fieldName == kBackupId) {
uassert(ErrorCodes::TypeMismatch,
str::stream() << "The '" << fieldName << "' parameter of the " << kStageName
<< " stage must be a binary data value, but found: "
<< typeName(elem.type()),
elem.type() == BSONType::BinData);
backupId = uassertStatusOK(UUID::parse(elem));
} else if (fieldName == kFile) {
uassert(ErrorCodes::TypeMismatch,
str::stream() << "The '" << fieldName << "' parameter of the " << kStageName
<< " stage must be a string value, but found: "
<< typeName(elem.type()),
elem.type() == BSONType::String);
filePath = elem.String();
} else if (fieldName == kByteOffset) {
uassert(ErrorCodes::TypeMismatch,
str::stream() << "The '" << fieldName << "' parameter of the " << kStageName
<< " stage must be a long integer value, but found: "
<< typeName(elem.type()),
elem.type() == BSONType::NumberLong);
byteOffset = elem.Long();
} else {
uasserted(ErrorCodes::FailedToParse,
str::stream() << "Unrecognized option '" << fieldName << "' in " << kStageName
<< " stage");
}
}

uassert(ErrorCodes::InvalidOptions,
str::stream() << "'" << kByteOffset << "' parameter cannot be less than zero",
byteOffset >= 0);

std::ifstream iFile(filePath, std::ios_base::in | std::ios_base::binary);
uassert(ErrorCodes::FileOpenFailed,
str::stream() << "Failed to open file " << filePath,
iFile.is_open());
iFile.seekg(byteOffset);
uassert(ErrorCodes::FileOpenFailed,
str::stream() << "Failed to set read position " << byteOffset << " in file "
<< filePath,
!iFile.fail());
invariant(byteOffset == iFile.tellg());

return make_intrusive<DocumentSourceBackupFile>(
pExpCtx, backupId, std::move(filePath), byteOffset, std::move(iFile));
}

DocumentSourceBackupFile::DocumentSourceBackupFile(const intrusive_ptr<ExpressionContext>& expCtx,
UUID backupId,
std::string filePath,
long long byteOffset,
std::ifstream file)
: DocumentSource(kStageName, expCtx),
_dataBuf(),
_backupId(backupId),
_filePath(std::move(filePath)),
_byteOffset(byteOffset),
_file(std::move(file)) {}

DocumentSourceBackupFile::~DocumentSourceBackupFile() {
_file.close();
}

} // namespace mongo
Loading