diff options
author | arkady-e1ppa <arkady-e1ppa@yandex-team.com> | 2024-08-15 18:32:55 +0300 |
---|---|---|
committer | arkady-e1ppa <arkady-e1ppa@yandex-team.com> | 2024-08-15 18:44:19 +0300 |
commit | 28ff4da78aa89f0226af33522332b7f06521412e (patch) | |
tree | 09c5bbbba83b4a04d51a77c654d0eaeab9bb404f /yt/yt/core/misc/stripped_error.h | |
parent | 0ecc758c681a1a8d54fbf95f0b58bba175518fac (diff) | |
download | ydb-28ff4da78aa89f0226af33522332b7f06521412e.tar.gz |
YT-21233: Split error into error and stripped_error to rid the latter of deps on global variables
What happened:
1. error contents has been split into stripped_error and error. stripped_error contains the error itself (with attributes for now) and macros; error contains stripped_error and some extensions, namely, functions to get fiberId, hostname and traceid/spanid and all functions used to (de-)serialize error. This means that you cannot print error if you only include stripped_error, therefore you are likely to still require the entire error.h at the moment.
2. Mechanic for gathering origin attributes has been moved to newly created library/cpp/yt/error thus having no dependency on fibers, net or tracing. stripped_error uses these attributes as extendable semi-erased (meaning, you still would have to add a field and recompile the entire thing, but you don't have to introduce an extra dependency) storage for a bunch of attributes
3. Parsing of said attributes is done in error file (and not stripped_error).
P.S. So far the plan is to eventually move stripped_error (once dependency on core/ytree/attributes is eliminated) without any actual change to dependency graph of anything outside of core (e.g. you would still have to include misc/error.h to use it). Next step would be re-teaching the error how to print, which would move some more methods from core to the standalone module. After that one could finally depend on the error itself and not the entire core.
Annotations: [nodiff:caesar]
66615172181355821241d2e5f8e4a0f15e0ea791
Diffstat (limited to 'yt/yt/core/misc/stripped_error.h')
-rw-r--r-- | yt/yt/core/misc/stripped_error.h | 458 |
1 files changed, 458 insertions, 0 deletions
diff --git a/yt/yt/core/misc/stripped_error.h b/yt/yt/core/misc/stripped_error.h new file mode 100644 index 0000000000..aea3077e63 --- /dev/null +++ b/yt/yt/core/misc/stripped_error.h @@ -0,0 +1,458 @@ +#pragma once + +#include "public.h" + +#include <yt/yt/core/ytree/public.h> + +#include <library/cpp/yt/threading/public.h> + +#include <library/cpp/yt/error/origin_attributes.h> + +#include <library/cpp/yt/yson/public.h> + +#include <library/cpp/yt/yson_string/convert.h> +#include <library/cpp/yt/yson_string/string.h> + +#include <library/cpp/yt/misc/property.h> + +#include <util/system/compiler.h> +#include <util/system/getpid.h> + +#include <util/generic/size_literals.h> + +#include <type_traits> + +namespace NYT { + +//////////////////////////////////////////////////////////////////////////////// + +//! An opaque wrapper around |int| value capable of conversions from |int|s and +//! arbitrary enum types. +class TErrorCode +{ +public: + using TUnderlying = int; + + constexpr TErrorCode(); + explicit constexpr TErrorCode(int value); + template <class E> + requires std::is_enum_v<E> + constexpr TErrorCode(E value); + + constexpr operator int() const; + + template <class E> + requires std::is_enum_v<E> + constexpr operator E() const; + + template <class E> + requires std::is_enum_v<E> + constexpr bool operator == (E rhs) const; + + constexpr bool operator == (TErrorCode rhs) const; +private: + int Value_; +}; + +//////////////////////////////////////////////////////////////////////////////// + +void FormatValue(TStringBuilderBase* builder, TErrorCode code, TStringBuf spec); + +//////////////////////////////////////////////////////////////////////////////// + +struct TErrorAttribute +{ + template <class T> + TErrorAttribute(const TString& key, const T& value) + : Key(key) + , Value(NYson::ConvertToYsonString(value)) + { } + + TErrorAttribute(const TString& key, const NYson::TYsonString& value) + : Key(key) + , Value(value) + { } + + TString Key; + NYson::TYsonString Value; +}; + +//////////////////////////////////////////////////////////////////////////////// + +template <class TValue> +concept CErrorNestable = requires (TError& error, TValue&& operand) +{ + { error <<= std::forward<TValue>(operand) } -> std::same_as<TError&>; +}; + +template <> +class [[nodiscard]] TErrorOr<void> +{ +public: + TErrorOr(); + ~TErrorOr(); + + TErrorOr(const TError& other); + TErrorOr(TError&& other) noexcept; + + TErrorOr(const std::exception& ex); + + struct TDisableFormat + { }; + static constexpr TDisableFormat DisableFormat = {}; + + TErrorOr(TString message, TDisableFormat); + TErrorOr(TErrorCode code, TString message, TDisableFormat); + + template <class... TArgs> + explicit TErrorOr( + TFormatString<TArgs...> format, + TArgs&&... arg); + + template <class... TArgs> + TErrorOr( + TErrorCode code, + TFormatString<TArgs...> format, + TArgs&&... args); + + TError& operator = (const TError& other); + TError& operator = (TError&& other) noexcept; + + static TError FromSystem(); + static TError FromSystem(int error); + static TError FromSystem(const TSystemError& error); + + TErrorCode GetCode() const; + TError& SetCode(TErrorCode code); + + TErrorCode GetNonTrivialCode() const; + THashSet<TErrorCode> GetDistinctNonTrivialErrorCodes() const; + + const TString& GetMessage() const; + TError& SetMessage(TString message); + + bool HasOriginAttributes() const; + TProcessId GetPid() const; + TStringBuf GetThreadName() const; + NThreading::TThreadId GetTid() const; + + bool HasDatetime() const; + TInstant GetDatetime() const; + + bool HasAttributes() const noexcept; + + const NYTree::IAttributeDictionary& Attributes() const; + NYTree::IAttributeDictionary* MutableAttributes(); + + const std::vector<TError>& InnerErrors() const; + std::vector<TError>* MutableInnerErrors(); + + // Used for deserialization only. + TOriginAttributes* MutableOriginAttributes() const noexcept; + void SetAttributes(NYTree::IAttributeDictionaryPtr attributes) noexcept; + + TError Truncate( + int maxInnerErrorCount = 2, + i64 stringLimit = 16_KB, + const THashSet<TStringBuf>& attributeWhitelist = {}) const &; + TError Truncate( + int maxInnerErrorCount = 2, + i64 stringLimit = 16_KB, + const THashSet<TStringBuf>& attributeWhitelist = {}) &&; + + bool IsOK() const; + + template <class U> + requires (!CStringLiteral<std::remove_cvref_t<U>>) + void ThrowOnError(U&& u) const &; + template <class... TArgs> + void ThrowOnError(TFormatString<TArgs...> format, TArgs&&... args) const &; + template <class... TArgs> + void ThrowOnError(TErrorCode code, TFormatString<TArgs...> format, TArgs&&... args) const &; + inline void ThrowOnError() const &; + + template <class U> + requires (!CStringLiteral<std::remove_cvref_t<U>>) + void ThrowOnError(U&& u) &&; + template <class... TArgs> + void ThrowOnError(TFormatString<TArgs...> format, TArgs&&... args) &&; + template <class... TArgs> + void ThrowOnError(TErrorCode code, TFormatString<TArgs...> format, TArgs&&... args) &&; + inline void ThrowOnError() &&; + + template <CInvocable<bool(const TError&)> TFilter> + std::optional<TError> FindMatching(const TFilter& filter) const; + template <CInvocable<bool(TErrorCode)> TFilter> + std::optional<TError> FindMatching(const TFilter& filter) const; + std::optional<TError> FindMatching(TErrorCode code) const; + std::optional<TError> FindMatching(const THashSet<TErrorCode>& codes) const; + + template <class U> + requires (!CStringLiteral<std::remove_cvref_t<U>>) + TError Wrap(U&& u) const &; + template <class... TArgs> + TError Wrap(TFormatString<TArgs...> format, TArgs&&... args) const &; + template <class... TArgs> + TError Wrap(TErrorCode code, TFormatString<TArgs...> format, TArgs&&... args) const &; + TError Wrap() const &; + + template <class U> + requires (!CStringLiteral<std::remove_cvref_t<U>>) + TError Wrap(U&& u) &&; + template <class... TArgs> + TError Wrap(TFormatString<TArgs...> format, TArgs&&... args) &&; + template <class... TArgs> + TError Wrap(TErrorCode code, TFormatString<TArgs...> format, TArgs&&... args) &&; + TError Wrap() &&; + + //! Perform recursive aggregation of error codes and messages over the error tree. + //! Result of this aggregation is suitable for error clustering in groups of + //! "similar" errors. Refer to yt/yt/library/error_skeleton/skeleton_ut.cpp for examples. + //! + //! This method builds skeleton from scratch by doing complete error tree traversal, + //! so calling it in computationally hot code is discouraged. + //! + //! In order to prevent core -> re2 dependency, implementation belongs to a separate library + //! yt/yt/library/error_skeleton. Calling this method without PEERDIR'ing implementation + //! results in an exception. + TString GetSkeleton() const; + + TError& operator <<= (const TErrorAttribute& attribute) &; + TError& operator <<= (const std::vector<TErrorAttribute>& attributes) &; + TError& operator <<= (const TError& innerError) &; + TError& operator <<= (TError&& innerError) &; + TError& operator <<= (const std::vector<TError>& innerErrors) &; + TError& operator <<= (std::vector<TError>&& innerErrors) &; + TError& operator <<= (const NYTree::IAttributeDictionary& attributes) &; + + template <CErrorNestable TValue> + TError&& operator << (TValue&& operand) &&; + + template <CErrorNestable TValue> + TError operator << (TValue&& operand) const &; + + template <CErrorNestable TValue> + TError&& operator << (const std::optional<TValue>& rhs) &&; + + template <CErrorNestable TValue> + TError operator << (const std::optional<TValue>& rhs) const &; + +private: + class TImpl; + std::unique_ptr<TImpl> Impl_; + + explicit TErrorOr(std::unique_ptr<TImpl> impl); + + void MakeMutable(); +}; + +//////////////////////////////////////////////////////////////////////////////// + +bool operator == (const TError& lhs, const TError& rhs); + +//////////////////////////////////////////////////////////////////////////////// + +void FormatValue(TStringBuilderBase* builder, const TError& error, TStringBuf spec); + +//////////////////////////////////////////////////////////////////////////////// + +using TErrorVisitor = std::function<void(const TError&, int depth)>; + +//! Traverses the error tree in DFS order. +void TraverseError( + const TError& error, + const TErrorVisitor& visitor, + int depth = 0); + +//////////////////////////////////////////////////////////////////////////////// + +template <class T> +struct TErrorTraits +{ + using TWrapped = TErrorOr<T>; + using TUnwrapped = T; +}; + +template <class T> +struct TErrorTraits<TErrorOr<T>> +{ + using TUnderlying = T; + using TWrapped = TErrorOr<T>; + using TUnwrapped = T; +}; + +//////////////////////////////////////////////////////////////////////////////// + +class TErrorException + : public std::exception +{ +public: + DEFINE_BYREF_RW_PROPERTY(TError, Error); + +public: + TErrorException() = default; + TErrorException(const TErrorException& other) = default; + TErrorException(TErrorException&& other) = default; + + const char* what() const noexcept override; + +private: + mutable TString CachedWhat_; +}; + +// Make these templates to avoid type erasure during throw. +template <class TException> + requires std::derived_from<std::remove_cvref_t<TException>, TErrorException> +TException&& operator <<= (TException&& ex, const TError& error); +template <class TException> + requires std::derived_from<std::remove_cvref_t<TException>, TErrorException> +TException&& operator <<= (TException&& ex, TError&& error); + +//////////////////////////////////////////////////////////////////////////////// + +namespace NDetail { + +struct TErrorAdaptor +{ + template <class TArg> + requires std::constructible_from<TError, TArg> + TError operator << (TArg&& rhs) const; + + template <class TArg> + requires + std::constructible_from<TError, TArg> && + std::derived_from<std::remove_cvref_t<TArg>, TError> + TArg&& operator << (TArg&& rhs) const; +}; + +// Make these to correctly forward TError to Wrap call. +template <class TErrorLike, class U> + requires + std::derived_from<std::remove_cvref_t<TErrorLike>, TError> && + (!CStringLiteral<std::remove_cvref_t<U>>) +void ThrowErrorExceptionIfFailed(TErrorLike&& error, U&& u); + +template <class TErrorLike, class... TArgs> + requires std::derived_from<std::remove_cvref_t<TErrorLike>, TError> +void ThrowErrorExceptionIfFailed(TErrorLike&& error, TFormatString<TArgs...> format, TArgs&&... args); + +template <class TErrorLike, class... TArgs> + requires std::derived_from<std::remove_cvref_t<TErrorLike>, TError> +void ThrowErrorExceptionIfFailed(TErrorLike&& error, TErrorCode code, TFormatString<TArgs...> format, TArgs&&... args); + +template <class TErrorLike> + requires std::derived_from<std::remove_cvref_t<TErrorLike>, TError> +void ThrowErrorExceptionIfFailed(TErrorLike&& error); + +} // namespace NDetail + +//////////////////////////////////////////////////////////////////////////////// + +#define THROW_ERROR \ + throw ::NYT::TErrorException() <<= ::NYT::NDetail::TErrorAdaptor() << + +#define THROW_ERROR_EXCEPTION(head, ...) \ + THROW_ERROR ::NYT::TError(head __VA_OPT__(,) __VA_ARGS__) + +#define THROW_ERROR_EXCEPTION_IF_FAILED(error, ...) \ + ::NYT::NDetail::ThrowErrorExceptionIfFailed((error) __VA_OPT__(,) __VA_ARGS__); \ + +#define THROW_ERROR_EXCEPTION_UNLESS(condition, head, ...) \ + if ((condition)) {\ + } else { \ + THROW_ERROR ::NYT::TError(head __VA_OPT__(,) __VA_ARGS__); \ + } + +#define THROW_ERROR_EXCEPTION_IF(condition, head, ...) \ + THROW_ERROR_EXCEPTION_UNLESS(!(condition), head, __VA_ARGS__) + +//////////////////////////////////////////////////////////////////////////////// + +template <class T> +class [[nodiscard]] TErrorOr + : public TError +{ +public: + TErrorOr(); + + TErrorOr(const T& value); + TErrorOr(T&& value) noexcept; + + TErrorOr(const TErrorOr<T>& other); + TErrorOr(TErrorOr<T>&& other) noexcept; + + TErrorOr(const TError& other); + TErrorOr(TError&& other) noexcept; + + TErrorOr(const std::exception& ex); + + template <class U> + TErrorOr(const TErrorOr<U>& other); + template <class U> + TErrorOr(TErrorOr<U>&& other) noexcept; + + TErrorOr<T>& operator = (const TErrorOr<T>& other) + requires std::is_copy_assignable_v<T>; + TErrorOr<T>& operator = (TErrorOr<T>&& other) noexcept + requires std::is_nothrow_move_assignable_v<T>; + + const T& Value() const & Y_LIFETIME_BOUND; + T& Value() & Y_LIFETIME_BOUND; + T&& Value() && Y_LIFETIME_BOUND; + + template <class U> + requires (!CStringLiteral<std::remove_cvref_t<U>>) + const T& ValueOrThrow(U&& u) const & Y_LIFETIME_BOUND; + template <class... TArgs> + const T& ValueOrThrow(TFormatString<TArgs...> format, TArgs&&... args) const & Y_LIFETIME_BOUND; + template <class... TArgs> + const T& ValueOrThrow(TErrorCode code, TFormatString<TArgs...> format, TArgs&&... args) const & Y_LIFETIME_BOUND; + const T& ValueOrThrow() const & Y_LIFETIME_BOUND; + + template <class U> + requires (!CStringLiteral<std::remove_cvref_t<U>>) + T& ValueOrThrow(U&& u) & Y_LIFETIME_BOUND; + template <class... TArgs> + T& ValueOrThrow(TFormatString<TArgs...> format, TArgs&&... args) & Y_LIFETIME_BOUND; + template <class... TArgs> + T& ValueOrThrow(TErrorCode code, TFormatString<TArgs...> format, TArgs&&... args) & Y_LIFETIME_BOUND; + T& ValueOrThrow() & Y_LIFETIME_BOUND; + + template <class U> + requires (!CStringLiteral<std::remove_cvref_t<U>>) + T&& ValueOrThrow(U&& u) && Y_LIFETIME_BOUND; + template <class... TArgs> + T&& ValueOrThrow(TFormatString<TArgs...> format, TArgs&&... args) && Y_LIFETIME_BOUND; + template <class... TArgs> + T&& ValueOrThrow(TErrorCode code, TFormatString<TArgs...> format, TArgs&&... args) && Y_LIFETIME_BOUND; + T&& ValueOrThrow() && Y_LIFETIME_BOUND; + + const T& ValueOrDefault(const T& defaultValue Y_LIFETIME_BOUND) const & Y_LIFETIME_BOUND; + T& ValueOrDefault(T& defaultValue Y_LIFETIME_BOUND) & Y_LIFETIME_BOUND; + constexpr T ValueOrDefault(T&& defaultValue) const &; + constexpr T ValueOrDefault(T&& defaultValue) &&; + +private: + std::optional<T> Value_; +}; + +//////////////////////////////////////////////////////////////////////////////// + +template <class T> +void FormatValue(TStringBuilderBase* builder, const TErrorOr<T>& error, TStringBuf spec); + +//////////////////////////////////////////////////////////////////////////////// + +template <class F, class... As> +auto RunNoExcept(F&& functor, As&&... args) noexcept -> decltype(functor(std::forward<As>(args)...)) +{ + return functor(std::forward<As>(args)...); +} + +//////////////////////////////////////////////////////////////////////////////// + +} // namespace NYT + +#define STRIPPED_ERROR_INL_H_ +#include "stripped_error-inl.h" +#undef STRIPPED_ERROR_INL_H_ |