注意
本文档适用于 Ceph 开发版本。
错误处理
在 Seastar 中,一个future
表示一个尚未可用但稍后可以变得可用。future
可以具有以下状态:
不可用:值尚未可用,
值,
失败:在计算值时抛出了异常。该异常已被捕获并存储在
future
实例中通过std::exception_ptr
.
在最后一种情况下,可以使用future::handle_exception()
或future::handle_exception_type()
处理该异常。Seastar 甚至提供了future::or_terminate()
来在 future 失败时终止程序。
但在 Crimson 中,相当多的错误并不严重到足以完全失败程序。例如,如果我们尝试通过对象 ID 查找对象,该操作可能会失败,因为对象不存在或已损坏,我们需要恢复该对象以完成请求,而不是终止进程。
换句话说,这些错误是预期的。此外,不幸路径的性能也应该与幸福路径的性能相当。此外,我们希望有一种方法来确保所有预期的错误都被处理。这应该类似于编译器执行的静态分析,如果任何枚举值在switch-case
语句中未处理,则会发出警告。
不幸的是,seastar::future
无法满足这两个要求。
Seastar 强制重新抛出异常以在不同的异常类型之间进行分发。这既不高性能,甚至也不可扩展,因为语言运行时中的锁定可能会发生。
Seastar 不会在返回
seastar::future
的类型中编码预期的异常类型。只有值的类型被编码。这对程序员来说造成了巨大的精神负担,因为确保所有预期的错误确实被处理需要手动代码审核。
因此,“errorator”被创建。它是一个围绕原始seastar::future
的包装器。它解决了性能和可扩展性问题,同时将所有预期错误类型的类型信息嵌入到 future 的类型中:
using ertr = crimson::errorator<crimson::ct_error::enoent,
crimson::ct_error::einval>;
在上面的示例中,我们定义了一个 errorator,它允许两种错误类型:
crimson::ct_error::enoent
和crimson::ct_error::einval
.
这些(以及crimson::ct_error
命名空间中的其他)基本上是std::error_code
的不可抛出包装器,以排除偶然的抛出,并确保以允许编译时检查的方式报告错误。
errorator 中最基本的东西是一个seastar::future
的后代,它可以作为例如函数的返回类型:
static ertr::future<int> foo(int bar) {
if (bar == 42) {
return crimson::ct_error::einval::make();
} else {
return ertr::make_ready_future(bar);
}
}
值得注意的是,返回 errorator 的错误集的一部分之外的错误会导致编译时错误:
static ertr::future<int> foo(int bar) {
// Oops, input_output_error is not allowed in `ertr`. static_assert() will
// terminate the compilation. This behaviour is absolutely fundamental for
// callers -- to figure out about all possible errors they need to worry
// about is enough to just take a look on the function's signature; reading
// through its implementation is not necessary anymore!
return crimson::ct_error::input_output_error::make();
}
errorator 概念更进一步。它不仅为调用者提供了嵌入在函数类型中的所有潜在错误的信息;它还确保在调用者处处理所有这些错误。正如读者可能知道的,seastar::future
是then()
中的主方法。在 errorated future 上它是可用的,但只有当 errorator 的错误集为空(字面量:errorator<>::future
)时;否则调用者必须使用safe_then()
:
seastar::future<> baz() {
return foo(42).safe_then(
[] (const int bar) {
std::cout << "the optimistic path! got bar=" << bar << std::endl
return ertr::now();
},
ertr::all_same_way(const std::error_code& err) {
// handling errors removes them from errorator's error set
std::cout << "the error path! got err=" << err << std::endl;
return ertr::now();
}).then([] {
// as all errors have been handled, errorator's error set became
// empty and the future instance returned from `safe_then()` has
// `then()` available!
return seastar::now();
});
}
在上面的示例中ertr::all_same_way
已被用于以相同的方式处理所有错误。这不是强制性的——调用者可以分别处理每个错误。此外,它可以只提供部分错误的处理器。代价是then()
:
using einval_ertr = crimson::errorator<crimson::ct_error::einval>;
// we can't return seastar::future<> (aka errorator<>::future<>) as handling
// as this level deals only with enoent leaving einval without a handler.
// handling it becomes a responsibility of a caller of `baz()`.
einval_ertr::future<> baz() {
return foo(42).safe_then(
[] (const int bar) {
std::cout << "the optimistic path! got bar=" << bar << std::endl
return ertr::now();
},
// provide a handler only for crimson::ct_error::enoent.
// crimson::ct_error::einval stays unhandled!
crimson::ct_error::enoent::handle([] {
std::cout << "the enoent error path!" << std::endl;
return ertr::now();
}));
// .safe_then() above returned `errorator<crimson::ct_error::einval>::future<>`
// which lacks `then()`.
}
的可用性。safe_then()
appends thembaz()
’s
using broader_ertr = crimson::errorator<crimson::ct_error::enoent,
crimson::ct_error::einval,
crimson::ct_error::input_output_error>;
broader_ertr::future<> baz() {
return foo(42).safe_then(
[] (const int bar) {
std::cout << "oops, the optimistic path generates a new error!";
return crimson::ct_error::input_output_error::make();
},
// we have a special handler to delegate the handling up. For convenience,
// the same behaviour is available as single argument-taking variant of
// `safe_then()`.
ertr::pass_further{});
}
如此看来,在safe_then()
中处理和报告错误基本上是对编译时检查的错误集的操作。
更多细节可以在Seastar Summit 2019 上 ceph::errorator<> throw/catch-free, compile time-checked exceptions for seastar::future<>演示的幻灯片中找到。
由 Ceph 基金会带给您
Ceph 文档是一个社区资源,由非盈利的 Ceph 基金会资助和托管Ceph Foundation. 如果您想支持这一点和我们的其他工作,请考虑加入现在加入.