foundationdb/fdbclient/AuditUtils.actor.cpp

342 lines
12 KiB
C++

/*
* AuditUtils.actor.cpp
*
* This source file is part of the FoundationDB open source project
*
* Copyright 2013-2022 Apple Inc. and the FoundationDB project authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include "fdbclient/AuditUtils.actor.h"
#include "fdbclient/Audit.h"
#include "fdbclient/FDBTypes.h"
#include "fdbclient/NativeAPI.actor.h"
#include "fdbclient/ClientKnobs.h"
#include "flow/actorcompiler.h" // has to be last include
ACTOR static Future<std::vector<AuditStorageState>> getLatestAuditStatesImpl(Transaction* tr, AuditType type, int num) {
state std::vector<AuditStorageState> auditStates;
loop {
auditStates.clear();
try {
RangeResult res = wait(tr->getRange(auditKeyRange(type), num, Snapshot::False, Reverse::True));
for (int i = 0; i < res.size(); ++i) {
auditStates.push_back(decodeAuditStorageState(res[i].value));
}
break;
} catch (Error& e) {
wait(tr->onError(e));
}
}
return auditStates;
}
ACTOR static Future<Void> checkMoveKeysLock(Transaction* tr,
MoveKeyLockInfo lock,
bool isDDEnabled,
bool isWrite = true) {
tr->setOption(FDBTransactionOptions::READ_SYSTEM_KEYS);
if (!isDDEnabled) {
TraceEvent(SevDebug, "DDDisabledByInMemoryCheck").log();
throw movekeys_conflict(); // need a new name
}
Optional<Value> readVal = wait(tr->get(moveKeysLockOwnerKey));
UID currentOwner = readVal.present() ? BinaryReader::fromStringRef<UID>(readVal.get(), Unversioned()) : UID();
if (currentOwner == lock.prevOwner) {
// Check that the previous owner hasn't touched the lock since we took it
Optional<Value> readVal = wait(tr->get(moveKeysLockWriteKey));
UID lastWrite = readVal.present() ? BinaryReader::fromStringRef<UID>(readVal.get(), Unversioned()) : UID();
if (lastWrite != lock.prevWrite) {
CODE_PROBE(true, "checkMoveKeysLock: Conflict with previous owner");
TraceEvent(SevDebug, "ConflictWithPreviousOwner");
throw movekeys_conflict(); // need a new name
}
// Take the lock
if (isWrite) {
BinaryWriter wrMyOwner(Unversioned());
wrMyOwner << lock.myOwner;
tr->set(moveKeysLockOwnerKey, wrMyOwner.toValue());
BinaryWriter wrLastWrite(Unversioned());
UID lastWriter = deterministicRandom()->randomUniqueID();
wrLastWrite << lastWriter;
tr->set(moveKeysLockWriteKey, wrLastWrite.toValue());
TraceEvent("CheckMoveKeysLock")
.detail("PrevOwner", lock.prevOwner.toString())
.detail("PrevWrite", lock.prevWrite.toString())
.detail("MyOwner", lock.myOwner.toString())
.detail("Writer", lastWriter.toString());
}
return Void();
} else if (currentOwner == lock.myOwner) {
if (isWrite) {
// Touch the lock, preventing overlapping attempts to take it
BinaryWriter wrLastWrite(Unversioned());
wrLastWrite << deterministicRandom()->randomUniqueID();
tr->set(moveKeysLockWriteKey, wrLastWrite.toValue());
// Make this transaction self-conflicting so the database will not execute it twice with the same write key
tr->makeSelfConflicting();
}
return Void();
} else {
CODE_PROBE(true, "checkMoveKeysLock: Conflict with new owner");
TraceEvent(SevDebug, "ConflictWithNewOwner");
throw movekeys_conflict(); // need a new name
}
}
ACTOR Future<UID> persistNewAuditState(Database cx,
AuditStorageState auditState,
MoveKeyLockInfo lock,
bool ddEnabled) {
ASSERT(!auditState.id.isValid());
state Transaction tr(cx);
state UID auditId;
loop {
try {
wait(checkMoveKeysLock(&tr, lock, ddEnabled, true));
std::vector<AuditStorageState> auditStates = wait(getLatestAuditStatesImpl(&tr, auditState.getType(), 1));
uint64_t nextId = 1;
if (!auditStates.empty()) {
nextId = auditStates.front().id.first() + 1;
}
auditId = UID(nextId, 0LL);
auditState.id = auditId;
tr.set(auditKey(auditState.getType(), auditId), auditStorageStateValue(auditState));
wait(tr.commit());
TraceEvent(SevDebug, "PersistedNewAuditState", auditId)
.detail("AuditKey", auditKey(auditState.getType(), auditId));
break;
} catch (Error& e) {
TraceEvent(SevDebug, "PersistedNewAuditStateError", auditId)
.errorUnsuppressed(e)
.detail("AuditKey", auditKey(auditState.getType(), auditId));
wait(tr.onError(e));
}
}
return auditId;
}
ACTOR Future<Void> persistAuditState(Database cx,
AuditStorageState auditState,
std::string context,
MoveKeyLockInfo lock,
bool ddEnabled) {
state Transaction tr(cx);
loop {
try {
tr.setOption(FDBTransactionOptions::PRIORITY_SYSTEM_IMMEDIATE);
tr.setOption(FDBTransactionOptions::ACCESS_SYSTEM_KEYS);
auto auditPhase = auditState.getPhase();
if (auditPhase == AuditPhase::Complete || auditPhase == AuditPhase::Failed ||
auditPhase == AuditPhase::Error) {
wait(checkMoveKeysLock(&tr, lock, ddEnabled, true));
}
tr.set(auditKey(auditState.getType(), auditState.id), auditStorageStateValue(auditState));
wait(tr.commit());
TraceEvent(SevDebug, "PersistAuditState", auditState.id)
.detail("AuditID", auditState.id)
.detail("AuditType", auditState.getType())
.detail("AuditKey", auditKey(auditState.getType(), auditState.id))
.detail("Context", context);
break;
} catch (Error& e) {
TraceEvent(SevDebug, "PersistAuditStateError", auditState.id)
.errorUnsuppressed(e)
.detail("AuditID", auditState.id)
.detail("AuditType", auditState.getType())
.detail("AuditKey", auditKey(auditState.getType(), auditState.id))
.detail("Context", context);
wait(tr.onError(e));
}
}
return Void();
}
ACTOR Future<AuditStorageState> getAuditState(Database cx, AuditType type, UID id) {
state Transaction tr(cx);
state Optional<Value> res;
loop {
try {
tr.setOption(FDBTransactionOptions::PRIORITY_SYSTEM_IMMEDIATE);
tr.setOption(FDBTransactionOptions::READ_SYSTEM_KEYS);
Optional<Value> res_ = wait(tr.get(auditKey(type, id)));
res = res_;
TraceEvent(SevDebug, "ReadAuditState", id)
.detail("AuditID", id)
.detail("AuditType", type)
.detail("AuditKey", auditKey(type, id));
break;
} catch (Error& e) {
TraceEvent(SevDebug, "ReadAuditStateError", id)
.errorUnsuppressed(e)
.detail("AuditID", id)
.detail("AuditType", type)
.detail("AuditKey", auditKey(type, id));
wait(tr.onError(e));
}
}
if (!res.present()) {
throw key_not_found();
}
return decodeAuditStorageState(res.get());
}
ACTOR Future<std::vector<AuditStorageState>> getLatestAuditStates(Database cx, AuditType type, int num) {
Transaction tr(cx);
std::vector<AuditStorageState> auditStates = wait(getLatestAuditStatesImpl(&tr, type, num));
return auditStates;
}
ACTOR Future<Void> persistAuditStateByRange(Database cx, AuditStorageState auditState) {
state Transaction tr(cx);
loop {
try {
tr.setOption(FDBTransactionOptions::PRIORITY_SYSTEM_IMMEDIATE);
tr.setOption(FDBTransactionOptions::ACCESS_SYSTEM_KEYS);
wait(krmSetRange(&tr,
auditRangePrefixFor(auditState.getType(), auditState.id),
auditState.range,
auditStorageStateValue(auditState)));
break;
} catch (Error& e) {
wait(tr.onError(e));
}
}
return Void();
}
ACTOR Future<std::vector<AuditStorageState>> getAuditStateByRange(Database cx,
AuditType type,
UID auditId,
KeyRange range) {
state RangeResult auditStates;
state Transaction tr(cx);
loop {
try {
tr.setOption(FDBTransactionOptions::PRIORITY_SYSTEM_IMMEDIATE);
tr.setOption(FDBTransactionOptions::READ_SYSTEM_KEYS);
RangeResult res_ = wait(krmGetRanges(&tr,
auditRangePrefixFor(type, auditId),
range,
CLIENT_KNOBS->KRM_GET_RANGE_LIMIT,
CLIENT_KNOBS->KRM_GET_RANGE_LIMIT_BYTES));
auditStates = res_;
break;
} catch (Error& e) {
TraceEvent(SevDebug, "GetAuditStateForRangeError").errorUnsuppressed(e).detail("AuditID", auditId);
wait(tr.onError(e));
}
}
// For a range of state that have value read from auditRangePrefixFor
// add the state with the same range to res (these states are persisted by auditServers)
// For a range of state that does not have value read from auditRangePrefixFor
// add an default (Invalid phase) state with the same range to res (DD will start audit for these ranges)
std::vector<AuditStorageState> res;
for (int i = 0; i < auditStates.size() - 1; ++i) {
KeyRange currentRange = KeyRangeRef(auditStates[i].key, auditStates[i + 1].key);
AuditStorageState auditState(auditId, currentRange, type);
if (!auditStates[i].value.empty()) {
AuditStorageState auditState_ = decodeAuditStorageState(auditStates[i].value);
auditState.setPhase(auditState_.getPhase());
auditState.error = auditState_.error;
}
res.push_back(auditState);
}
return res;
}
ACTOR Future<Void> persistAuditStateByServer(Database cx, AuditStorageState auditState) {
state Transaction tr(cx);
loop {
try {
tr.setOption(FDBTransactionOptions::PRIORITY_SYSTEM_IMMEDIATE);
tr.setOption(FDBTransactionOptions::ACCESS_SYSTEM_KEYS);
wait(krmSetRange(&tr,
auditServerPrefixFor(auditState.getType(), auditState.id, auditState.auditServerId),
auditState.range,
auditStorageStateValue(auditState)));
break;
} catch (Error& e) {
wait(tr.onError(e));
}
}
return Void();
}
ACTOR Future<std::vector<AuditStorageState>> getAuditStateByServer(Database cx,
AuditType type,
UID auditId,
UID auditServerId,
KeyRange range) {
state RangeResult auditStates;
state Transaction tr(cx);
loop {
try {
tr.setOption(FDBTransactionOptions::PRIORITY_SYSTEM_IMMEDIATE);
tr.setOption(FDBTransactionOptions::READ_SYSTEM_KEYS);
RangeResult res_ = wait(krmGetRanges(&tr,
auditServerPrefixFor(type, auditId, auditServerId),
range,
CLIENT_KNOBS->KRM_GET_RANGE_LIMIT,
CLIENT_KNOBS->KRM_GET_RANGE_LIMIT_BYTES));
auditStates = res_;
break;
} catch (Error& e) {
TraceEvent(SevDebug, "GetAuditStateForRangeError").errorUnsuppressed(e).detail("AuditID", auditId);
wait(tr.onError(e));
}
}
// For a range of state that have value read from auditServerPrefixFor
// add the state with the same range to res (these states are persisted by auditServers)
// For a range of state that does not have value read from auditServerPrefixFor
// add an default (Invalid phase) state with the same range to res (DD will start audit for these ranges)
std::vector<AuditStorageState> res;
for (int i = 0; i < auditStates.size() - 1; ++i) {
KeyRange currentRange = KeyRangeRef(auditStates[i].key, auditStates[i + 1].key);
AuditStorageState auditState(auditId, currentRange, type);
if (!auditStates[i].value.empty()) {
AuditStorageState auditState_ = decodeAuditStorageState(auditStates[i].value);
auditState.setPhase(auditState_.getPhase());
auditState.error = auditState_.error;
}
res.push_back(auditState);
}
return res;
}