/**
* Copyright (C) 2014 MongoDB Inc.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License, version 3,
* as published by the Free Software Foundation.
*
* 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
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see .
*
* 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 GNU Affero General 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.
*/
#pragma once
#include
#include
#include
#include "mongo/base/disallow_copying.h"
#include "mongo/base/status.h"
#include "mongo/db/storage/snapshot.h"
#include "mongo/db/storage/snapshot_name.h"
namespace mongo {
class BSONObjBuilder;
class OperationContext;
/**
* A RecoveryUnit is responsible for ensuring that data is persisted.
* All on-disk information must be mutated through this interface.
*/
class RecoveryUnit {
MONGO_DISALLOW_COPYING(RecoveryUnit);
public:
virtual ~RecoveryUnit() {}
virtual void reportState(BSONObjBuilder* b) const {}
/**
* These should be called through WriteUnitOfWork rather than directly.
*
* A call to 'beginUnitOfWork' marks the beginning of a unit of work. Each call to
* 'beginUnitOfWork' must be matched with exactly one call to either 'commitUnitOfWork' or
* 'abortUnitOfWork'. When 'abortUnitOfWork' is called, all changes made since the begin
* of the unit of work will be rolled back.
*/
virtual void beginUnitOfWork(OperationContext* opCtx) = 0;
virtual void commitUnitOfWork() = 0;
virtual void abortUnitOfWork() = 0;
/**
* Waits until all commits that happened before this call are durable. Returns true, unless the
* storage engine cannot guarantee durability, which should never happen when isDurable()
* returned true. This cannot be called from inside a unit of work, and should fail if it is.
*/
virtual bool waitUntilDurable() = 0;
/**
* When this is called, if there is an open transaction, it is closed. On return no
* transaction is active. This cannot be called inside of a WriteUnitOfWork, and should
* fail if it is.
*/
virtual void abandonSnapshot() = 0;
/**
* Informs this RecoveryUnit that all future reads through it should be from a snapshot
* marked as Majority Committed. Snapshots should still be separately acquired and newer
* committed snapshots should be used if available whenever implementations would normally
* change snapshots.
*
* If no snapshot has yet been marked as Majority Committed, returns a status with error
* code ReadConcernMajorityNotAvailableYet. After this returns successfully, at any point where
* implementations attempt to acquire committed snapshot, if there are none available due to a
* call to SnapshotManager::dropAllSnapshots(), a UserException with the same code should be
* thrown.
*
* StorageEngines that don't support a SnapshotManager should use the default
* implementation.
*/
virtual Status setReadFromMajorityCommittedSnapshot() {
return {ErrorCodes::CommandNotSupported,
"Current storage engine does not support majority readConcerns"};
}
/**
* Returns true if setReadFromMajorityCommittedSnapshot() has been called.
*/
virtual bool isReadingFromMajorityCommittedSnapshot() const {
return false;
}
/**
* Returns the SnapshotName being used by this recovery unit or boost::none if not reading from
* a majority committed snapshot.
*
* It is possible for reads to occur from later snapshots, but they may not occur from earlier
* snapshots.
*/
virtual boost::optional getMajorityCommittedSnapshot() const {
dassert(!isReadingFromMajorityCommittedSnapshot());
return {};
}
/**
* Gets the local SnapshotId.
*
* It is only valid to compare SnapshotIds generated by a single RecoveryUnit.
*
* This is unrelated to SnapshotName which must be globally comparable.
*/
virtual SnapshotId getSnapshotId() const = 0;
/**
* A Change is an action that is registerChange()'d while a WriteUnitOfWork exists. The
* change is either rollback()'d or commit()'d when the WriteUnitOfWork goes out of scope.
*
* Neither rollback() nor commit() may fail or throw exceptions.
*
* Change implementors are responsible for handling their own locking, and must be aware
* that rollback() and commit() may be called after resources with a shorter lifetime than
* the WriteUnitOfWork have been freed. Each registered change will be committed or rolled
* back once.
*/
class Change {
public:
virtual ~Change() {}
virtual void rollback() = 0;
virtual void commit() = 0;
};
/**
* The RecoveryUnit takes ownership of the change. The commitUnitOfWork() method calls the
* commit() method of each registered change in order of registration. The endUnitOfWork()
* method calls the rollback() method of each registered Change in reverse order of
* registration. Either will unregister and delete the changes.
*
* The registerChange() method may only be called when a WriteUnitOfWork is active, and
* may not be called during commit or rollback.
*/
virtual void registerChange(Change* change) = 0;
/**
* Registers a callback to be called if the current WriteUnitOfWork rolls back.
*
* Be careful about the lifetimes of all variables captured by the callback!
*/
template
void onRollback(Callback callback) {
class OnRollbackChange final : public Change {
public:
OnRollbackChange(Callback&& callback) : _callback(std::move(callback)) {}
void rollback() final {
_callback();
}
void commit() final {}
private:
Callback _callback;
};
registerChange(new OnRollbackChange(std::move(callback)));
}
/**
* Registers a callback to be called if the current WriteUnitOfWork commits.
*
* Be careful about the lifetimes of all variables captured by the callback!
*/
template
void onCommit(Callback callback) {
class OnCommitChange final : public Change {
public:
OnCommitChange(Callback&& callback) : _callback(std::move(callback)) {}
void rollback() final {}
void commit() final {
_callback();
}
private:
Callback _callback;
};
registerChange(new OnCommitChange(std::move(callback)));
}
//
// The remaining methods probably belong on DurRecoveryUnit rather than on the interface.
//
/**
* Declare that the data at [x, x + len) is being written.
*/
virtual void* writingPtr(void* data, size_t len) = 0;
//
// Syntactic sugar
//
/**
* Declare write intent for an int
*/
inline int& writingInt(int& d) {
return *writing(&d);
}
/**
* A templated helper for writingPtr.
*/
template
inline T* writing(T* x) {
writingPtr(x, sizeof(T));
return x;
}
/**
* Sets a flag that declares this RecoveryUnit will skip rolling back writes, for the
* duration of the current outermost WriteUnitOfWork. This function can only be called
* between a pair of unnested beginUnitOfWork() / endUnitOfWork() calls.
* The flag is cleared when endUnitOfWork() is called.
* While the flag is set, rollback will skip rolling back writes, but custom rollback
* change functions are still called. Clearly, this functionality should only be used when
* writing to temporary collections that can be cleaned up externally. For example,
* foreground index builds write to a temporary collection; if something goes wrong that
* normally requires a rollback, we can instead clean up the index by dropping the entire
* index.
* Setting the flag may permit increased performance.
*/
virtual void setRollbackWritesDisabled() = 0;
protected:
RecoveryUnit() {}
};
} // namespace mongo