diff options
author | arkady-e1ppa <arkady-e1ppa@yandex-team.com> | 2024-12-25 07:12:23 +0300 |
---|---|---|
committer | arkady-e1ppa <arkady-e1ppa@yandex-team.com> | 2024-12-25 07:31:55 +0300 |
commit | b5dd91799751f9924acb7c17ddad16ddb2086bba (patch) | |
tree | f430b4e9f752af7411d5503bfcf823e9718056c2 /library/cpp/yt/error/error_code.cpp | |
parent | 75f1af270a6cf9a17b65fde6d12efbb94f235960 (diff) | |
download | ydb-b5dd91799751f9924acb7c17ddad16ddb2086bba.tar.gz |
YT-21233: Adjust (To/From)ErrorAttributeValue CPOs, revert ConvertTo becoming CPO, move TErrorCode and TError to library/cpp/yt/error
\[nodiff:caesar\]
List of changes:
1) Make `ConvertTo` a normal function again (basically, a revert of previous change). It turned out to be very inconvenient entry point
2) Introduce concept for "Primitive types" which are handled without yt/core module. Please note that inclusion of core **does not** affect work with primitive types (otherwise we would be facing and ODR violation). These are numerics, duration, instant, string-like objects and guid
3) Introduce `FromErrorAttributeValue` which is a substitute to `ConvertTo` with special behavior (see below)
4) Scheme for (de-)serialization has changed:
1) Primitive types are handled via `ConvertToTextYsonString` and `ConvertFromTextYsonString`
2) Other types are not supported without yt/core. Otherwise `ConvertToYsonString(value, EYsonFormat::Text)` used for serialization and `ConvertTo<T>(value)` for deserialization.
5) New format of attribute value storage allows to not care about concrete type of the attribute (text yson strings can be detected since they start with `\"` and deserialized so are text yson bools, everything else is already human readable). This drops dependency on `TTokenizer`
6) Some small parts (e.g. enums or constants or aliases) of yt/core/misc files were moved to library/cpp/yt/\* locations and re-included to drop dependencies of stripped\_error on them
7) `TErrorAttributes` is now a simple hash map instead of a handle to `IAttributeDictionary`
8) `ExtractFromAttributes` weak symbol is finally moved to library/cpp/yt/error due to point 9 allowing to fully drop dependency on `IAttributeDictionary`. Combined with point 7 this drops dep on yt/core/ytree altogether
9) Moved `TErrorCode` to library/cpp/yt/error. There is a logger there which forces dep on library/cpp/yt/logging. It is not required really, and can be later removed
10) Moved `TError` with format and macroes to library/cpp/yt/error. It still (and probably forever will) depend on yson.
What can be done next: Switch TYsonString to TString and move ConvertTo/FromTextYsonString to library/cpp/yt/error. This would remove dep on library/cpp/yt/yson\_string
commit_hash:6f11dc478ab782a1f98a5aedcd45a4800d3f4e7b
Diffstat (limited to 'library/cpp/yt/error/error_code.cpp')
-rw-r--r-- | library/cpp/yt/error/error_code.cpp | 164 |
1 files changed, 164 insertions, 0 deletions
diff --git a/library/cpp/yt/error/error_code.cpp b/library/cpp/yt/error/error_code.cpp new file mode 100644 index 0000000000..9cb0d57dc2 --- /dev/null +++ b/library/cpp/yt/error/error_code.cpp @@ -0,0 +1,164 @@ +#include "error_code.h" + +#include <library/cpp/yt/logging/logger.h> + +#include <library/cpp/yt/misc/global.h> + +#include <util/string/split.h> + +#include <util/system/type_name.h> + +namespace NYT { + +//////////////////////////////////////////////////////////////////////////////// + +// TODO(achulkov2): Remove this once we find all duplicate error codes. +static YT_DEFINE_GLOBAL(NLogging::TLogger, Logger, "ErrorCode") + +//////////////////////////////////////////////////////////////////////////////// + +bool TErrorCodeRegistry::TErrorCodeInfo::operator==(const TErrorCodeInfo& rhs) const +{ + return Namespace == rhs.Namespace && Name == rhs.Name; +} + +TErrorCodeRegistry* TErrorCodeRegistry::Get() +{ + return LeakySingleton<TErrorCodeRegistry>(); +} + +TErrorCodeRegistry::TErrorCodeInfo TErrorCodeRegistry::Get(int code) const +{ + auto it = CodeToInfo_.find(code); + if (it != CodeToInfo_.end()) { + return it->second; + } + for (const auto& range : ErrorCodeRanges_) { + if (range.Contains(code)) { + return range.Get(code); + } + } + return {"NUnknown", Format("ErrorCode%v", code)}; +} + +THashMap<int, TErrorCodeRegistry::TErrorCodeInfo> TErrorCodeRegistry::GetAllErrorCodes() const +{ + return CodeToInfo_; +} + +std::vector<TErrorCodeRegistry::TErrorCodeRangeInfo> TErrorCodeRegistry::GetAllErrorCodeRanges() const +{ + return ErrorCodeRanges_; +} + +void TErrorCodeRegistry::RegisterErrorCode(int code, const TErrorCodeInfo& errorCodeInfo) +{ + if (!CodeToInfo_.insert({code, errorCodeInfo}).second) { + // TODO(achulkov2): Deal with duplicate TransportError in NRpc and NBus. + if (code == 100) { + return; + } + // TODO(yuryalekseev): Deal with duplicate SslError in NRpc and NBus. + if (code == 119) { + return; + } + YT_LOG_FATAL( + "Duplicate error code (Code: %v, StoredCodeInfo: %v, NewCodeInfo: %v)", + code, + CodeToInfo_[code], + errorCodeInfo); + } +} + +TErrorCodeRegistry::TErrorCodeInfo TErrorCodeRegistry::TErrorCodeRangeInfo::Get(int code) const +{ + return {Namespace, Formatter(code)}; +} + +bool TErrorCodeRegistry::TErrorCodeRangeInfo::Intersects(const TErrorCodeRangeInfo& other) const +{ + return std::max(From, other.From) <= std::min(To, other.To); +} + +bool TErrorCodeRegistry::TErrorCodeRangeInfo::Contains(int value) const +{ + return From <= value && value <= To; +} + +void TErrorCodeRegistry::RegisterErrorCodeRange(int from, int to, TString namespaceName, std::function<TString(int)> formatter) +{ + YT_VERIFY(from <= to); + + TErrorCodeRangeInfo newRange{from, to, std::move(namespaceName), std::move(formatter)}; + for (const auto& range : ErrorCodeRanges_) { + YT_LOG_FATAL_IF( + range.Intersects(newRange), + "Intersecting error code ranges registered (FirstRange: %v, SecondRange: %v)", + range, + newRange); + } + ErrorCodeRanges_.push_back(std::move(newRange)); + CheckCodesAgainstRanges(); +} + +void TErrorCodeRegistry::CheckCodesAgainstRanges() const +{ + for (const auto& [code, info] : CodeToInfo_) { + for (const auto& range : ErrorCodeRanges_) { + YT_LOG_FATAL_IF( + range.Contains(code), + "Error code range contains another registered code " + "(Range: %v, Code: %v, RangeCodeInfo: %v, StandaloneCodeInfo: %v)", + range, + code, + range.Get(code), + info); + } + } +} + +TString TErrorCodeRegistry::ParseNamespace(const std::type_info& errorCodeEnumTypeInfo) +{ + TString name; + // Ensures that "EErrorCode" is found as a substring in the type name and stores the prefix before + // the first occurrence into #name. + YT_VERIFY(StringSplitter( + TypeName(errorCodeEnumTypeInfo)).SplitByString("EErrorCode").Limit(2).TryCollectInto(&name, &std::ignore)); + + // TypeName returns name in form "enum ErrorCode" on Windows + if (name.StartsWith("enum ")) { + name.remove(0, 5); + } + + // If the enum was declared directly in the global namespace, #name should be empty. + // Otherwise, #name should end with "::". + if (!name.empty()) { + YT_VERIFY(name.EndsWith("::")); + name.resize(name.size() - 2); + } + return name; +} + +void FormatValue( + TStringBuilderBase* builder, + const TErrorCodeRegistry::TErrorCodeInfo& errorCodeInfo, + TStringBuf /*spec*/) +{ + if (errorCodeInfo.Namespace.empty()) { + Format(builder, "EErrorCode::%v", errorCodeInfo.Name); + return; + } + Format(builder, "%v::EErrorCode::%v", errorCodeInfo.Namespace, errorCodeInfo.Name); +} + +void FormatValue( + TStringBuilderBase* builder, + const TErrorCodeRegistry::TErrorCodeRangeInfo& errorCodeRangeInfo, + TStringBuf /*spec*/) +{ + Format(builder, "%v-%v", errorCodeRangeInfo.From, errorCodeRangeInfo.To); +} + +//////////////////////////////////////////////////////////////////////////////// + +} // namespace NYT |