diff --git a/include/ircd/db/database/rocksdb.h b/include/ircd/db/database/rocksdb.h index 3fd9ac0ab..7339e7b0e 100644 --- a/include/ircd/db/database/rocksdb.h +++ b/include/ircd/db/database/rocksdb.h @@ -38,6 +38,7 @@ namespace rocksdb struct SstFileWriter; struct TableProperties; struct LogFile; + struct Status; } // diff --git a/include/ircd/db/db.h b/include/ircd/db/db.h index f142734d6..854d29243 100644 --- a/include/ircd/db/db.h +++ b/include/ircd/db/db.h @@ -15,6 +15,7 @@ namespace ircd::db { struct init; + struct error; struct gopts; struct sopts; struct cell; @@ -24,26 +25,6 @@ namespace ircd::db struct database; struct options; - // Errors for the database subsystem. The exceptions that use _HIDENAME - // are built from RocksDB errors which already have an info string with - // an included name. - // - IRCD_EXCEPTION(ircd::error, error) - IRCD_EXCEPTION(error, not_found) - IRCD_EXCEPTION(error, schema_error) - IRCD_EXCEPTION_HIDENAME(error, corruption) - IRCD_EXCEPTION_HIDENAME(error, not_supported) - IRCD_EXCEPTION_HIDENAME(error, invalid_argument) - IRCD_EXCEPTION_HIDENAME(error, io_error) - IRCD_EXCEPTION_HIDENAME(error, merge_in_progress) - IRCD_EXCEPTION_HIDENAME(error, incomplete) - IRCD_EXCEPTION_HIDENAME(error, shutdown_in_progress) - IRCD_EXCEPTION_HIDENAME(error, timed_out) - IRCD_EXCEPTION_HIDENAME(error, aborted) - IRCD_EXCEPTION_HIDENAME(error, busy) - IRCD_EXCEPTION_HIDENAME(error, expired) - IRCD_EXCEPTION_HIDENAME(error, try_again) - // db subsystem has its own logging facility extern struct log::log log; @@ -71,6 +52,7 @@ namespace ircd::db #include "database/snapshot.h" #include "database/sst.h" #include "database/wal.h" +#include "error.h" #include "cache.h" #include "opts.h" #include "column.h" diff --git a/include/ircd/db/error.h b/include/ircd/db/error.h new file mode 100644 index 000000000..3a7b5f792 --- /dev/null +++ b/include/ircd/db/error.h @@ -0,0 +1,91 @@ +// Matrix Construct +// +// Copyright (C) Matrix Construct Developers, Authors & Contributors +// Copyright (C) 2016-2018 Jason Volk +// +// Permission to use, copy, modify, and/or distribute this software for any +// purpose with or without fee is hereby granted, provided that the above +// copyright notice and this permission notice is present in all copies. The +// full license for this software is available in the LICENSE file. + +#pragma once +#define HAVE_IRCD_DB_ERROR_H + +namespace ircd::db +{ + struct error; +} + +/// Database error. For most catchers of this error outside of the db:: system, +/// the formatted what() message will be sufficient. The codes are not useful +/// outside of db::. A common error `not_found` has its own subtype to be +/// caught independently; +/// +struct ircd::db::error +:ircd::error +{ + struct not_found; + + protected: + static const rocksdb::Status _no_code_; + + public: + uint8_t code {0}; + uint8_t subcode {0}; + uint8_t severity {0}; + + error(generate_skip_t, + const rocksdb::Status &); + + explicit + error(const rocksdb::Status &); + + IRCD_OVERLOAD(internal) + error(internal_t, + const rocksdb::Status &s, + const string_view &fmt, + const va_rtti &ap); + + template + error(const rocksdb::Status &s, + const string_view &fmt, + args&&... a) + :error + { + internal, s, fmt, va_rtti{std::forward(a)...} + }{} + + template + error(const string_view &fmt, + args&&... a) + :error + { + internal, _no_code_, fmt, va_rtti{std::forward(a)...} + }{} +}; + +namespace ircd::db +{ + using not_found = error::not_found; +} + +/// Common error `not_found` has its own subtype to be caught independently; +/// it may contain a more limited what() (or none at all) as an optimization. +/// +struct ircd::db::error::not_found +:error +{ + protected: + static const rocksdb::Status _not_found_; + + public: + template + not_found(const string_view &fmt, + args&&... a) + :error + { + _not_found_, fmt, std::forward(a)... + }{} + + not_found(); +}; diff --git a/ircd/db.cc b/ircd/db.cc index b4ddd87fe..e6fc7da81 100644 --- a/ircd/db.cc +++ b/ircd/db.cc @@ -1371,14 +1371,9 @@ try d->GetLatestSequenceNumber() }; } -catch(const corruption &e) +catch(const error &e) { - throw corruption - { - "Corruption for '%s' (%s). Try restarting with the -pitrecdb command line option", - this->name, - e.what() - }; + throw; } catch(const std::exception &e) { @@ -1535,7 +1530,7 @@ ircd::db::database::operator[](const string_view &name) { const auto it{column_names.find(name)}; if(unlikely(it == std::end(column_names))) - throw schema_error + throw not_found { "'%s': column '%s' is not available or specified in schema", this->name, @@ -1555,7 +1550,7 @@ try } catch(const std::out_of_range &e) { - throw schema_error + throw not_found { "'%s': column id[%u] is not available or specified in schema", this->name, @@ -1569,7 +1564,7 @@ const { const auto it{column_names.find(name)}; if(unlikely(it == std::end(column_names))) - throw schema_error + throw not_found { "'%s': column '%s' is not available or specified in schema", this->name, @@ -1589,7 +1584,7 @@ const try } catch(const std::out_of_range &e) { - throw schema_error + throw not_found { "'%s': column id[%u] is not available or specified in schema", this->name, @@ -9167,7 +9162,7 @@ ircd::db::row::operator[](const string_view &column) { const auto it(find(column)); if(unlikely(it == end())) - throw schema_error + throw not_found { "column '%s' not specified in the descriptor schema", column }; @@ -9181,7 +9176,7 @@ const { const auto it(find(column)); if(unlikely(it == end())) - throw schema_error + throw not_found { "column '%s' not specified in the descriptor schema", column }; @@ -10994,11 +10989,133 @@ ircd::db::ticker(const rocksdb::Cache &cache, zero; } +/////////////////////////////////////////////////////////////////////////////// +// +// error.h +// + +// +// error::not_found +// + +decltype(ircd::db::error::not_found::_not_found_) +ircd::db::error::not_found::_not_found_ +{ + rocksdb::Status::NotFound() +}; + +// +// error::not_found::not_found +// + +ircd::db::error::not_found::not_found() +:error +{ + generate_skip, _not_found_ +} +{ + strlcpy(buf, "NotFound"); +} + +// +// error +// + +decltype(ircd::db::error::_no_code_) +ircd::db::error::_no_code_ +{ + rocksdb::Status::OK() +}; + +// +// error::error +// + +ircd::db::error::error(internal_t, + const rocksdb::Status &s, + const string_view &fmt, + const va_rtti &ap) +:error +{ + s +} +{ + const string_view &msg{buf}; + const mutable_buffer remain + { + buf + size(msg), sizeof(buf) - size(msg) + }; + + fmt::vsprintf + { + remain, fmt, ap + }; +} + +ircd::db::error::error(const rocksdb::Status &s) +:error +{ + generate_skip, s +} +{ + fmt::sprintf + { + buf, "%s (%u:%u): %s (%u)", + s.getState(), + this->code, + this->subcode, + reflect(s.severity()), + this->severity, + }; +} + +ircd::db::error::error(generate_skip_t, + const rocksdb::Status &s) +:ircd::error +{ + generate_skip +} +,code +{ + s.code() +} +,subcode +{ + s.subcode() +} +,severity +{ + s.severity() +} +{ +} + /////////////////////////////////////////////////////////////////////////////// // // Misc // +// +// throw_on_error +// + +ircd::db::throw_on_error::throw_on_error(const rocksdb::Status &status) +{ + using rocksdb::Status; + + switch(status.code()) + { + case Status::kOk: + return; + + case Status::kNotFound: + throw not_found{}; + + default: + throw error{status}; + } +} + std::vector ircd::db::column_names(const std::string &path, const std::string &options) @@ -11324,42 +11441,6 @@ ircd::db::error_to_status::error_to_status(const std::error_code &e) { } -// -// throw_on_error -// - -ircd::db::throw_on_error::throw_on_error(const rocksdb::Status &s) -{ - using rocksdb::Status; - - const string_view &message - { - s.getState() - }; - - switch(s.code()) - { - case Status::kOk: return; - case Status::kNotFound: throw not_found{"%s", message}; - case Status::kCorruption: throw corruption{"%s", message}; - case Status::kNotSupported: throw not_supported{"%s", message}; - case Status::kInvalidArgument: throw invalid_argument{"%s", message}; - case Status::kIOError: throw io_error{"%s", message}; - case Status::kMergeInProgress: throw merge_in_progress{"%s", message}; - case Status::kIncomplete: throw incomplete{"%s", message}; - case Status::kShutdownInProgress: throw shutdown_in_progress{"%s", message}; - case Status::kTimedOut: throw timed_out{"%s", message}; - case Status::kAborted: throw aborted{"%s", message}; - case Status::kBusy: throw busy{"%s", message}; - case Status::kExpired: throw expired{"%s", message}; - case Status::kTryAgain: throw try_again{"%s", message}; - default: throw error - { - "code[%d] %s", s.code(), message - }; - } -} - // // //