MongoDB源码学习:Mongo中的OpRunner

88 阅读3分钟

简介

接上一回,mongod在接收到用户的request后,到了ServiceEntryPoint模块(service_entry_point_common.cpp)之后,会根据request的类型创建出不同的OpRunner处理request。那么这个OpRunner是什么?

OpRunner

我们先回顾下ServiceEntryPointCommon::handleRequest是调用hr.makeOpRunner得到一个OpRunner,然后再执行OpRunner::run方法的。以下是makeOpRunner的源码

std::unique_ptr<HandleRequest::OpRunner> HandleRequest::makeOpRunner() {
    switch (executionContext->op()) {
        case dbQuery:
            if (!executionContext->nsString().isCommand())
                return std::make_unique<QueryOpRunner>(this);
            // FALLTHROUGH: it's a query containing a command
        case dbMsg:
            return std::make_unique<CommandOpRunner>(this);
        case dbGetMore:
            return std::make_unique<GetMoreOpRunner>(this);
        case dbKillCursors:
            return std::make_unique<KillCursorsOpRunner>(this);
        case dbInsert:
            return std::make_unique<InsertOpRunner>(this);
        case dbUpdate:
            return std::make_unique<UpdateOpRunner>(this);
        case dbDelete:
            return std::make_unique<DeleteOpRunner>(this);
        default:
            return std::make_unique<UnsupportedOpRunner>(this);
    }
}

接下来看下OpRunner家族的情况。

OpRunner-类图.jpeg

可以看到OpRunner是一个抽象,主要分3大类。

CommandOpRunner

负责处理命令的OpRunner,例如db.createCollection命令。当中主要逻辑在receivedCommands方法完成。

struct CommandOpRunner : HandleRequest::OpRunner {
    using HandleRequest::OpRunner::OpRunner;
    Future<DbResponse> run() override {
        return receivedCommands(executionContext);
    }
};

SynchronousOpRunner

增加了try-catch逻辑,让子类不需要再单独处理DBException。其子类分别有:

  • QueryOpRunner
  • GetMoreOpRunner
  • FireAndForgetOpRunner
  • UnsupportedOpRunner
// Allows wrapping synchronous code in futures without repeating the try-catch block.
struct SynchronousOpRunner : HandleRequest::OpRunner {
    using HandleRequest::OpRunner::OpRunner;
    virtual DbResponse runSync() = 0;
    Future<DbResponse> run() final try { return runSync(); } catch (const DBException& ex) {
        return ex.toStatus();
    }
};

FireAndForgetOpRunner

是SynchronousOpRunner的子类,因为SynchronousOpRunner会捕获DBException,而FireAndForgetOpRunner会处理成只有NotPrimaryError会抛出异常,其余情况只做错误记录。其子类分别有:

  • KillCursorsOpRunner
  • InsertOpRunner
  • UpdateOpRunner
  • DeleteOpRunner
/**
 * Fire and forget network operations don't produce a `DbResponse`.
 * They override `runAndForget` instead of `run`, and this base
 * class provides a `run` that calls it and handles error reporting
 * via the `LastError` slot.
 */
struct FireAndForgetOpRunner : SynchronousOpRunner {
    using SynchronousOpRunner::SynchronousOpRunner;
    virtual void runAndForget() = 0;
    DbResponse runSync() final;
};

DbResponse FireAndForgetOpRunner::runSync() {
    try {
        warnDeprecation(executionContext->client(), networkOpToString(executionContext->op()));
        runAndForget();
    } catch (const AssertionException& ue) {
        LastError::get(executionContext->client()).setLastError(ue.code(), ue.reason());
        LOGV2_DEBUG(21969,
                    3,
                    "Caught Assertion in {networkOp}, continuing: {error}",
                    "Assertion in fire-and-forget operation",
                    "networkOp"_attr = networkOpToString(executionContext->op()),
                    "error"_attr = redact(ue));
        executionContext->currentOp().debug().errInfo = ue.toStatus();
    }
    // A NotWritablePrimary error can be set either within
    // receivedInsert/receivedUpdate/receivedDelete or within the AssertionException handler above.
    // Either way, we want to throw an exception here, which will cause the client to be
    // disconnected.
    if (LastError::get(executionContext->client()).hadNotPrimaryError()) {
        notPrimaryLegacyUnackWrites.increment();
        uasserted(ErrorCodes::NotWritablePrimary,
                  str::stream() << "Not-master error while processing '"
                                << networkOpToString(executionContext->op()) << "' operation  on '"
                                << executionContext->nsString() << "' namespace via legacy "
                                << "fire-and-forget command execution.");
    }
    return {};
}

细说一下CommandOpRunner

接下来细说一下CommandOpRunner的处理逻辑,回想一下CommandOpRunner.Run,就只有调用了receivedCommands方法。那receivedCommands到底做了什么,以及由如何处理命令的呢?

receivedCommands

receivedCommands的逻辑主要分为3步:

  • parseCommand - 解析命令
  • executeCommand - 执行命令
  • makeCommandResponse - 创建DbResponse
Future<DbResponse> receivedCommands(std::shared_ptr<HandleRequest::ExecutionContext> execContext) {
    // 设置一个ReplyBuilder处理回复的信息
    execContext->setReplyBuilder(
        rpc::makeReplyBuilder(rpc::protocolForMessage(execContext->getMessage())));
    return parseCommand(execContext)
        .then([execContext]() mutable { return executeCommand(std::move(execContext)); })
        .onError([execContext](Status status) {
            // 省略了代码,这里处理了错误的情况
        })
        .then([execContext]() mutable { return makeCommandResponse(std::move(execContext)); });
}

parseCommand - 解析命令

根据用户request中的message,解析成OpMsgRequest对象(这里不太想看,有兴趣的朋友自行阅读吧)。

makeCommandResponse - 创建DbResponse

根据处理结果,封装成DbResponse返回(没错,这里我也不想看)。

executeCommand - 执行命令

这个方法主要做的事情就是:

  • 找到Command
  • 封装成ExecCommandDatabase,然后执行run方法。这一步骤也可以分为4个主要步骤:
    • _parseCommand - 解析Command。在初始化ExecCommandDatabase的时候会调用。在这一步骤中会将Command解析得到一个CommandInvocation对象。
    • _initiateCommand - 初始化Command,在执行ExecCommandDatabase::run时候调用。
    • _commandExec - 执行Command,同样在执行ExecCommandDatabase::run时候调用。
Future<void> executeCommand(std::shared_ptr<HandleRequest::ExecutionContext> execContext) {
    auto future =
        std::move(present)
            .then([execContext]() -> Future<void> {
                // 省略了部分逻辑,例如检查command是否存在
                Command* c = execContext->getCommand();
                {
                    stdx::lock_guard<Client> lk(*opCtx->getClient());
                    CurOp::get(opCtx)->setLogicalOp_inlock(c->getLogicalOp());
                }

                opCtx->setExhaust(
                    OpMsg::isFlagSet(execContext->getMessage(), OpMsg::kExhaustSupported));

                return Status::OK();
            })
            .then([execContext]() mutable {
                // 封装成ExecCommandDatabase,然后调用其run方法
                return future_util::makeState<ExecCommandDatabase>(std::move(execContext))
                    .thenWithState([](auto* runner) { return runner->run(); });
            })
            .tapError([execContext](Status status) { // 处理错误的情况})
    past.emplaceValue();
    return future;
}

to be continue