aboutsummaryrefslogtreecommitdiffstats
path: root/library/cpp/yt/error/error_code.cpp
diff options
context:
space:
mode:
authorarkady-e1ppa <arkady-e1ppa@yandex-team.com>2024-12-25 07:12:23 +0300
committerarkady-e1ppa <arkady-e1ppa@yandex-team.com>2024-12-25 07:31:55 +0300
commitb5dd91799751f9924acb7c17ddad16ddb2086bba (patch)
treef430b4e9f752af7411d5503bfcf823e9718056c2 /library/cpp/yt/error/error_code.cpp
parent75f1af270a6cf9a17b65fde6d12efbb94f235960 (diff)
downloadydb-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.cpp164
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