/* * RemoteIKeyValueStore.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 "flow/ActorCollection.h" #include "flow/Error.h" #include "flow/Platform.h" #include "flow/Trace.h" #include "fdbrpc/FlowProcess.actor.h" #include "fdbrpc/fdbrpc.h" #include "fdbclient/FDBTypes.h" #include "fdbserver/FDBExecHelper.actor.h" #include "fdbserver/Knobs.h" #include "fdbserver/RemoteIKeyValueStore.actor.h" #include "flow/actorcompiler.h" // This must be the last #include. StringRef KeyValueStoreProcess::_name = "KeyValueStoreProcess"_sr; // A guard for guaranteed killing of machine after runIKVS returns struct AfterReturn { IKeyValueStore* kvStore; UID id; AfterReturn() : kvStore(nullptr) {} AfterReturn(IKeyValueStore* store, UID& uid) : kvStore(store), id(uid) {} ~AfterReturn() { TraceEvent(SevDebug, "RemoteKVStoreAfterReturn") .detail("Valid", kvStore != nullptr ? "True" : "False") .detail("UID", id) .log(); if (kvStore != nullptr) { kvStore->close(); } } // called when we already explicitly closed the kv store void invalidate() { kvStore = nullptr; } }; ACTOR void sendCommitReply(IKVSCommitRequest commitReq, IKeyValueStore* kvStore, Future onClosed) { try { choose { when(wait(onClosed)) { commitReq.reply.sendError(remote_kvs_cancelled()); } when(wait(kvStore->commit(commitReq.sequential))) { StorageBytes storageBytes = kvStore->getStorageBytes(); commitReq.reply.send(IKVSCommitReply(storageBytes)); } } } catch (Error& e) { TraceEvent(SevDebug, "RemoteKVSCommitReplyError").errorUnsuppressed(e); commitReq.reply.sendError(e.code() == error_code_actor_cancelled ? remote_kvs_cancelled() : e); } } ACTOR template Future cancellableForwardPromise(ReplyPromise output, Future input) { try { T value = wait(input); output.send(value); } catch (Error& e) { TraceEvent(SevDebug, "CancellableForwardPromiseError").errorUnsuppressed(e).backtrace(); output.sendError(e.code() == error_code_actor_cancelled ? remote_kvs_cancelled() : e); } return Void(); } ACTOR Future runIKVS(OpenKVStoreRequest openReq, IKVSInterface ikvsInterface) { state IKeyValueStore* kvStore = openKVStore(openReq.storeType, openReq.filename, openReq.logID, openReq.memoryLimit, openReq.checkChecksums, openReq.checkIntegrity); state UID kvsId(ikvsInterface.id()); state ActorCollection actors(false); state AfterReturn guard(kvStore, kvsId); state Promise onClosed; TraceEvent(SevDebug, "RemoteKVStoreInitializing").detail("UID", kvsId); wait(kvStore->init()); openReq.reply.send(ikvsInterface); TraceEvent(SevInfo, "RemoteKVStoreInitialized").detail("IKVSInterfaceUID", kvsId); loop { try { choose { when(IKVSGetValueRequest getReq = waitNext(ikvsInterface.getValue.getFuture())) { actors.add(cancellableForwardPromise(getReq.reply, kvStore->readValue(getReq.key, getReq.type, getReq.debugID))); } when(IKVSSetRequest req = waitNext(ikvsInterface.set.getFuture())) { kvStore->set(req.keyValue); } when(IKVSClearRequest req = waitNext(ikvsInterface.clear.getFuture())) { kvStore->clear(req.range); } when(IKVSCommitRequest commitReq = waitNext(ikvsInterface.commit.getFuture())) { sendCommitReply(commitReq, kvStore, onClosed.getFuture()); } when(IKVSReadValuePrefixRequest readPrefixReq = waitNext(ikvsInterface.readValuePrefix.getFuture())) { actors.add(cancellableForwardPromise( readPrefixReq.reply, kvStore->readValuePrefix( readPrefixReq.key, readPrefixReq.maxLength, readPrefixReq.type, readPrefixReq.debugID))); } when(IKVSReadRangeRequest readRangeReq = waitNext(ikvsInterface.readRange.getFuture())) { actors.add(cancellableForwardPromise( readRangeReq.reply, fmap( [](const RangeResult& result) { return IKVSReadRangeReply(result); }, kvStore->readRange( readRangeReq.keys, readRangeReq.rowLimit, readRangeReq.byteLimit, readRangeReq.type)))); } when(IKVSGetStorageByteRequest req = waitNext(ikvsInterface.getStorageBytes.getFuture())) { StorageBytes storageBytes = kvStore->getStorageBytes(); req.reply.send(storageBytes); } when(IKVSGetErrorRequest getFutureReq = waitNext(ikvsInterface.getError.getFuture())) { actors.add(cancellableForwardPromise(getFutureReq.reply, kvStore->getError())); } when(IKVSOnClosedRequest onClosedReq = waitNext(ikvsInterface.onClosed.getFuture())) { // onClosed request is not cancelled even this actor is cancelled forwardPromise(onClosedReq.reply, kvStore->onClosed()); } when(IKVSDisposeRequest disposeReq = waitNext(ikvsInterface.dispose.getFuture())) { TraceEvent(SevDebug, "RemoteIKVSDisposeReceivedRequest").detail("UID", kvsId); kvStore->dispose(); guard.invalidate(); onClosed.send(Void()); return Void(); } when(IKVSCloseRequest closeReq = waitNext(ikvsInterface.close.getFuture())) { TraceEvent(SevDebug, "RemoteIKVSCloseReceivedRequest").detail("UID", kvsId); kvStore->close(); guard.invalidate(); onClosed.send(Void()); return Void(); } } } catch (Error& e) { if (e.code() == error_code_actor_cancelled) { TraceEvent(SevInfo, "RemoteKVStoreCancelled").detail("UID", kvsId).backtrace(); onClosed.send(Void()); return Void(); } else { TraceEvent(SevError, "RemoteKVStoreError").error(e).detail("UID", kvsId).backtrace(); throw; } } } } ACTOR static Future flowProcessRunner(RemoteIKeyValueStore* self, Promise ready) { state FlowProcessInterface processInterface; state Future process; auto path = abspath(getExecPath()); auto endpoint = processInterface.registerProcess.getEndpoint(); auto address = endpoint.addresses.address.toString(); auto token = endpoint.token; // port 0 means we will find a random available port number for it std::string flowProcessAddr = g_network->getLocalAddress().ip.toString().append(":0"); std::vector args = { "bin/fdbserver", "-r", "flowprocess", "-C", SERVER_KNOBS->CONN_FILE, "--logdir", SERVER_KNOBS->LOG_DIRECTORY, "-p", flowProcessAddr, "--process-name", KeyValueStoreProcess::_name.toString(), "--process-endpoint", format("%s,%lu,%lu", address.c_str(), token.first(), token.second()) }; // For remote IKV store, we need to make sure the shutdown signal is sent back until we can destroy it in the // simulation process = spawnProcess(path, args, -1.0, false, 0.01 /*not used*/, self); choose { when(FlowProcessRegistrationRequest req = waitNext(processInterface.registerProcess.getFuture())) { self->consumeInterface(req.flowProcessInterface); ready.send(Void()); } when(int res = wait(process)) { // 0 means process normally shut down; non-zero means errors // process should not shut down normally before not ready ASSERT(res); return res; } } int res = wait(process); return res; } ACTOR static Future initializeRemoteKVStore(RemoteIKeyValueStore* self, OpenKVStoreRequest openKVSReq) { TraceEvent(SevInfo, "WaitingOnFlowProcess").detail("StoreType", openKVSReq.storeType).log(); Promise ready; self->returnCode = flowProcessRunner(self, ready); wait(ready.getFuture()); IKVSInterface ikvsInterface = wait(self->kvsProcess.openKVStore.getReply(openKVSReq)); TraceEvent(SevInfo, "IKVSInterfaceReceived").detail("UID", ikvsInterface.id()); self->interf = ikvsInterface; self->interf.storeType = openKVSReq.storeType; return Void(); } IKeyValueStore* openRemoteKVStore(KeyValueStoreType storeType, std::string const& filename, UID logID, int64_t memoryLimit, bool checkChecksums, bool checkIntegrity) { RemoteIKeyValueStore* self = new RemoteIKeyValueStore(); self->initialized = initializeRemoteKVStore( self, OpenKVStoreRequest(storeType, filename, logID, memoryLimit, checkChecksums, checkIntegrity)); return self; } ACTOR static Future delayFlowProcessRunAction(FlowProcess* self, double time) { wait(delay(time)); wait(self->run()); return Void(); } Future runFlowProcess(std::string const& name, Endpoint endpoint) { TraceEvent(SevInfo, "RunFlowProcessStart").log(); FlowProcess* self = IProcessFactory::create(name.c_str()); self->registerEndpoint(endpoint); RequestStream registerProcess(endpoint); FlowProcessRegistrationRequest req; req.flowProcessInterface = self->serializedInterface(); registerProcess.send(req); TraceEvent(SevDebug, "FlowProcessInitFinished").log(); return delayFlowProcessRunAction(self, g_network->isSimulated() ? 0 : SERVER_KNOBS->REMOTE_KV_STORE_INIT_DELAY); }