summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorarkady-e1ppa <[email protected]>2024-12-25 07:12:23 +0300
committerarkady-e1ppa <[email protected]>2024-12-25 07:31:55 +0300
commitb5dd91799751f9924acb7c17ddad16ddb2086bba (patch)
treef430b4e9f752af7411d5503bfcf823e9718056c2
parent75f1af270a6cf9a17b65fde6d12efbb94f235960 (diff)
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
-rw-r--r--library/cpp/yt/error/convert_to_cpo.h41
-rw-r--r--library/cpp/yt/error/error-inl.h (renamed from yt/yt/core/misc/stripped_error-inl.h)6
-rw-r--r--library/cpp/yt/error/error.cpp (renamed from yt/yt/core/misc/stripped_error.cpp)198
-rw-r--r--library/cpp/yt/error/error.h (renamed from yt/yt/core/misc/stripped_error.h)6
-rw-r--r--library/cpp/yt/error/error_attribute-inl.h57
-rw-r--r--library/cpp/yt/error/error_attribute.h62
-rw-r--r--library/cpp/yt/error/error_attributes-inl.h24
-rw-r--r--library/cpp/yt/error/error_attributes.cpp115
-rw-r--r--library/cpp/yt/error/error_attributes.h48
-rw-r--r--library/cpp/yt/error/error_code.cpp (renamed from yt/yt/core/misc/error_code.cpp)2
-rw-r--r--library/cpp/yt/error/error_code.h101
-rw-r--r--library/cpp/yt/error/error_helpers-inl.h (renamed from yt/yt/core/misc/error_helpers-inl.h)0
-rw-r--r--library/cpp/yt/error/error_helpers.h (renamed from yt/yt/core/misc/error_helpers.h)4
-rw-r--r--library/cpp/yt/error/origin_attributes.cpp43
-rw-r--r--library/cpp/yt/error/origin_attributes.h10
-rw-r--r--library/cpp/yt/error/public.h14
-rw-r--r--library/cpp/yt/error/unittests/error_code_ut.cpp (renamed from yt/yt/core/misc/unittests/error_code_ut.cpp)4
-rw-r--r--library/cpp/yt/error/unittests/error_ut.cpp779
-rw-r--r--library/cpp/yt/error/unittests/ya.make18
-rw-r--r--library/cpp/yt/error/ya.make9
-rw-r--r--library/cpp/yt/string/string.cpp15
-rw-r--r--library/cpp/yt/string/string.h8
-rw-r--r--library/cpp/yt/system/proc.h32
-rw-r--r--yt/yt/client/api/operation_client.cpp12
-rw-r--r--yt/yt/client/tablet_client/table_mount_cache_detail.cpp4
-rw-r--r--yt/yt/core/actions/unittests/future_ut.cpp4
-rw-r--r--yt/yt/core/misc/error-inl.h14
-rw-r--r--yt/yt/core/misc/error.cpp4
-rw-r--r--yt/yt/core/misc/error.h2
-rw-r--r--yt/yt/core/misc/error_code.h102
-rw-r--r--yt/yt/core/misc/origin_attributes.cpp50
-rw-r--r--yt/yt/core/misc/origin_attributes.h23
-rw-r--r--yt/yt/core/misc/proc.h27
-rw-r--r--yt/yt/core/misc/process_exit_profiler.h3
-rw-r--r--yt/yt/core/misc/public.h12
-rw-r--r--yt/yt/core/misc/string_helpers.cpp26
-rw-r--r--yt/yt/core/misc/string_helpers.h17
-rw-r--r--yt/yt/core/misc/unittests/error_ut.cpp761
-rw-r--r--yt/yt/core/misc/unittests/ya.make1
-rw-r--r--yt/yt/core/ya.make4
-rw-r--r--yt/yt/core/ytree/attributes-inl.h12
-rw-r--r--yt/yt/core/ytree/convert-inl.h54
-rw-r--r--yt/yt/core/ytree/convert.h8
-rw-r--r--yt/yt/core/ytree/node-inl.h11
44 files changed, 1403 insertions, 1344 deletions
diff --git a/library/cpp/yt/error/convert_to_cpo.h b/library/cpp/yt/error/convert_to_cpo.h
deleted file mode 100644
index dcfc8b0677d..00000000000
--- a/library/cpp/yt/error/convert_to_cpo.h
+++ /dev/null
@@ -1,41 +0,0 @@
-#pragma once
-
-#include <library/cpp/yt/misc/tag_invoke_cpo.h>
-
-namespace NYT {
-
-////////////////////////////////////////////////////////////////////////////////
-
-// NB(arkady-e1ppa): This file intentionally is unaware of possible "yson-implementations"
-// e.g. INode and TYsonString(Buf). This is done for two reasons:
-// 1) We can't have dep on INodePtr here anyway.
-// 2) We would like to eventually remove dep on yson of this module.
-
-////////////////////////////////////////////////////////////////////////////////
-
-// NB(arkady-e1ppa): We intentionally generate a separate namespace
-// where ConvertToImpl functions would reside
-// without polluting general-use namespaces.
-namespace NConvertToImpl {
-
-template <class T>
-struct TFn : public NYT::TTagInvokeCpoBase<TFn<T>>
-{ };
-
-} // NConvertToImpl
-
-////////////////////////////////////////////////////////////////////////////////
-
-template <class T>
-inline constexpr NConvertToImpl::TFn<T> ConvertTo = {};
-
-////////////////////////////////////////////////////////////////////////////////
-
-template <class TTo, class TFrom>
-concept CConvertsTo = requires (const TFrom& from) {
- { NYT::ConvertTo<TTo>(from) } -> std::same_as<TTo>;
-};
-
-////////////////////////////////////////////////////////////////////////////////
-
-} // namespace NYT
diff --git a/yt/yt/core/misc/stripped_error-inl.h b/library/cpp/yt/error/error-inl.h
index 7fbc8acd7e2..f9a7355d1a9 100644
--- a/yt/yt/core/misc/stripped_error-inl.h
+++ b/library/cpp/yt/error/error-inl.h
@@ -1,7 +1,7 @@
#ifndef STRIPPED_ERROR_INL_H_
-#error "Direct inclusion of this file is not allowed, include stripped_error.h"
+#error "Direct inclusion of this file is not allowed, include error.h"
// For the sake of sane code completion.
-#include "stripped_error.h"
+#include "error.h"
#endif
#include <library/cpp/yt/error/error_attributes.h>
@@ -67,6 +67,8 @@ inline TString FormatErrorMessage(TStringBuf format)
} // namespace NDetail
+////////////////////////////////////////////////////////////////////////////////
+
template <class... TArgs>
TError::TErrorOr(TFormatString<TArgs...> format, TArgs&&... args)
: TErrorOr(NYT::EErrorCode::Generic, NYT::NDetail::FormatErrorMessage(format.Get(), std::forward<TArgs>(args)...), DisableFormat)
diff --git a/yt/yt/core/misc/stripped_error.cpp b/library/cpp/yt/error/error.cpp
index 5be93260f93..bcc265ab2ed 100644
--- a/yt/yt/core/misc/stripped_error.cpp
+++ b/library/cpp/yt/error/error.cpp
@@ -1,27 +1,14 @@
-#include "stripped_error.h"
-
-// TODO(arkady-e1ppa): core/misc deps can easily be moved
-// to relevant library/cpp/yt sections.
-#include <yt/yt/core/misc/string_helpers.h>
-#include <yt/yt/core/misc/proc.h>
-
-// TODO(arkady-e1ppa): core/ytree deps removal requires making
-// a separate class that has some features of
-// IAttributeDictionary. Namely, it needs to
-// be created, have TYsonString added to it
-// merged with other dictionary and deep copied.
-// NB: We don't need printability since TError from
-// this file is not printable.
-#include <yt/yt/core/ytree/attributes.h>
-#include <yt/yt/core/ytree/exception_helpers.h>
-
-#include <yt/yt/core/misc/origin_attributes.h>
+#include "error.h"
#include <library/cpp/yt/exception/exception.h>
#include <library/cpp/yt/error/error_attributes.h>
#include <library/cpp/yt/error/origin_attributes.h>
+#include <library/cpp/yt/string/string.h>
+
+#include <library/cpp/yt/system/proc.h>
+
#include <library/cpp/yt/yson_string/string.h>
#include <util/string/subst.h>
@@ -31,7 +18,6 @@
namespace NYT {
-using namespace NYTree;
using namespace NYson;
////////////////////////////////////////////////////////////////////////////////
@@ -58,7 +44,7 @@ public:
: Code_(other.Code_)
, Message_(other.Message_)
, OriginAttributes_(other.OriginAttributes_)
- , AttributesImpl_(other.AttributesImpl_ ? other.AttributesImpl_->Clone() : nullptr)
+ , Attributes_(other.Attributes_)
, InnerErrors_(other.InnerErrors_)
{ }
@@ -150,7 +136,7 @@ public:
bool HasAttributes() const noexcept
{
- return AttributesImpl_.operator bool();
+ return true;
}
const TErrorAttributes& Attributes() const
@@ -160,15 +146,11 @@ public:
void UpdateOriginAttributes()
{
- OriginAttributes_ = NYT::NDetail::ExtractFromDictionary(AttributesImpl_);
+ OriginAttributes_ = NYT::NDetail::ExtractFromDictionary(&Attributes_);
}
TErrorAttributes* MutableAttributes() noexcept
{
- if (!HasAttributes()) {
- AttributesImpl_ = CreateEphemeralAttributes();
- Attributes_ = TErrorAttributes(AttributesImpl_.Get());
- }
return &Attributes_;
}
@@ -191,77 +173,13 @@ private:
.Tid = NThreading::InvalidThreadId,
};
- NYTree::IAttributeDictionaryPtr AttributesImpl_;
- TErrorAttributes Attributes_ = TErrorAttributes(AttributesImpl_.Get());
+ TErrorAttributes Attributes_;
std::vector<TError> InnerErrors_;
-
- friend class TErrorAttributes;
};
////////////////////////////////////////////////////////////////////////////////
-// NB(arkady-e1ppa): Currently TErrorAttributes is a facade
-// which has minimal API of original dict with backend being the
-// actual original dict. Once API-related issues are fixed we are
-// free to implement a backend which doesn't depend on original dict.
-std::vector<TErrorAttributes::TKey> TErrorAttributes::ListKeys() const
-{
- auto* attributes = static_cast<IAttributeDictionary*>(Attributes_);
- if (!attributes) {
- return {};
- }
- return attributes->ListKeys();
-}
-
-std::vector<TErrorAttributes::TKeyValuePair> TErrorAttributes::ListPairs() const
-{
- auto* attributes = static_cast<IAttributeDictionary*>(Attributes_);
- if (!attributes) {
- return {};
- }
- return attributes->ListPairs();
-}
-
-TErrorAttributes::TValue TErrorAttributes::FindYson(TStringBuf key) const
-{
- auto* attributes = static_cast<IAttributeDictionary*>(Attributes_);
- if (!attributes) {
- return {};
- }
- return attributes->FindYson(key);
-}
-
-void TErrorAttributes::SetYson(const TKey& key, const TValue& value)
-{
- auto* attributes = static_cast<IAttributeDictionary*>(Attributes_);
- YT_VERIFY(attributes);
- return attributes->SetYson(key, value);
-}
-
-bool TErrorAttributes::Remove(const TKey& key)
-{
- auto* attributes = static_cast<IAttributeDictionary*>(Attributes_);
- if (!attributes) {
- return false;
- }
- return attributes->Remove(key);
-}
-
-TErrorAttributes::TValue TErrorAttributes::GetYson(TStringBuf key) const
-{
- auto result = FindYson(key);
- if (!result) {
- // This method comes from "exception_helpers.h"
- // and requires NYTree::EErrorCode::ResolveError enum value.
- // TODO(arkady-e1ppa): eliminate this dependency somehow.
- ThrowNoSuchAttribute(key);
- }
- return result;
-}
-
-////////////////////////////////////////////////////////////////////////////////
-
namespace {
bool IsWhitelisted(const TError& error, const THashSet<TStringBuf>& attributeWhitelist)
@@ -517,7 +435,7 @@ bool TError::HasAttributes() const noexcept
const TErrorAttributes& TError::Attributes() const
{
if (!Impl_) {
- static TErrorAttributes empty{nullptr};
+ static TErrorAttributes empty = {};
return empty;
}
return Impl_->Attributes();
@@ -577,14 +495,14 @@ TError TError::Truncate(
auto truncateAttributes = [stringLimit, &attributeWhitelist] (const TErrorAttributes& attributes, TErrorAttributes* mutableAttributes) {
for (const auto& key : attributes.ListKeys()) {
- const auto& value = attributes.FindYson(key);
+ const auto& value = attributes.FindValue(key);
if (std::ssize(value.AsStringBuf()) > stringLimit && !attributeWhitelist.contains(key)) {
- mutableAttributes->SetYson(
+ mutableAttributes->SetValue(
key,
- NYson::ConvertToYsonString("...<attribute truncated>..."));
+ NYT::ToErrorAttributeValue("...<attribute truncated>..."));
} else {
- mutableAttributes->SetYson(
+ mutableAttributes->SetValue(
key,
value);
}
@@ -608,7 +526,7 @@ TError TError::Truncate(
copiedInnerErrors.push_back(truncateInnerError(innerError));
}
} else {
- result->MutableAttributes()->SetYson(InnerErrorsTruncatedKey, ConvertToYsonString(true));
+ result->MutableAttributes()->SetValue(InnerErrorsTruncatedKey, NYT::ToErrorAttributeValue(true));
// NB(arkady-e1ppa): We want to always keep the last inner error,
// so we make room for it and do not check if it is whitelisted.
@@ -642,10 +560,10 @@ TError TError::Truncate(
auto truncateAttributes = [stringLimit, &attributeWhitelist] (TErrorAttributes* attributes) {
for (const auto& key : attributes->ListKeys()) {
- if (std::ssize(attributes->FindYson(key).AsStringBuf()) > stringLimit && !attributeWhitelist.contains(key)) {
- attributes->SetYson(
+ if (std::ssize(attributes->FindValue(key).AsStringBuf()) > stringLimit && !attributeWhitelist.contains(key)) {
+ attributes->SetValue(
key,
- NYson::ConvertToYsonString("...<attribute truncated>..."));
+ NYT::ToErrorAttributeValue("...<attribute truncated>..."));
}
}
};
@@ -660,7 +578,7 @@ TError TError::Truncate(
}
} else {
auto& innerErrors = ApplyWhitelist(*MutableInnerErrors(), attributeWhitelist, maxInnerErrorCount);
- MutableAttributes()->SetYson(InnerErrorsTruncatedKey, ConvertToYsonString(true));
+ MutableAttributes()->SetValue(InnerErrorsTruncatedKey, NYT::ToErrorAttributeValue(true));
for (auto& innerError : innerErrors) {
truncateInnerError(innerError);
@@ -728,14 +646,14 @@ void TError::MakeMutable()
TError& TError::operator <<= (const TErrorAttribute& attribute) &
{
- MutableAttributes()->SetYson(attribute.Key, attribute.Value);
+ MutableAttributes()->SetAttribute(attribute);
return *this;
}
TError& TError::operator <<= (const std::vector<TErrorAttribute>& attributes) &
{
for (const auto& attribute : attributes) {
- MutableAttributes()->SetYson(attribute.Key, attribute.Value);
+ MutableAttributes()->SetAttribute(attribute);
}
return *this;
}
@@ -811,6 +729,18 @@ void AppendAttribute(TStringBuilderBase* builder, const TString& key, const TStr
void AppendError(TStringBuilderBase* builder, const TError& error, int indent)
{
+ auto isStringTextYson = [] (const NYson::TYsonString& str) {
+ return
+ str &&
+ std::ssize(str.AsStringBuf()) != 0 &&
+ str.AsStringBuf().front() == '\"';
+ };
+ auto isBoolTextYson = [] (const NYson::TYsonString& str) {
+ return
+ str.AsStringBuf() == "%false" ||
+ str.AsStringBuf() == "%true";
+ };
+
if (error.IsOK()) {
builder->AppendString("OK");
return;
@@ -821,21 +751,23 @@ void AppendError(TStringBuilderBase* builder, const TError& error, int indent)
builder->AppendChar('\n');
if (error.GetCode() != NYT::EErrorCode::Generic) {
- AppendAttribute(builder, "code", ToString(static_cast<int>(error.GetCode())), indent);
+ AppendAttribute(builder, "code", NYT::ToString(static_cast<int>(error.GetCode())), indent);
}
// Pretty-print origin.
+ const auto* originAttributes = error.MutableOriginAttributes();
+ YT_ASSERT(originAttributes);
if (error.HasOriginAttributes()) {
AppendAttribute(
builder,
"origin",
- NYT::NDetail::FormatOrigin(*error.MutableOriginAttributes()),
+ NYT::NDetail::FormatOrigin(*originAttributes),
indent);
- } else if (IsErrorSanitizerEnabled() && HasHost(error)) {
+ } else if (IsErrorSanitizerEnabled() && originAttributes->Host.operator bool()) {
AppendAttribute(
builder,
"host",
- ToString(GetHost(error)),
+ TString{originAttributes->Host},
indent);
}
@@ -848,41 +780,12 @@ void AppendError(TStringBuilderBase* builder, const TError& error, int indent)
}
for (const auto& [key, value] : error.Attributes().ListPairs()) {
- TTokenizer tokenizer(value.AsStringBuf());
- // TODO(arkady-e1ppa): Remove this once failed verifies have been dealt with.
- [[unlikely]] if (!tokenizer.ParseNext()) {
- Cerr <<
- NYT::Format(
- "%v *** Empty token encountered while formatting TError attribute (Key: %v, Value: %v)"
- "(BuilderAccumulatedData: %v)",
- TInstant::Now(),
- key,
- value.AsStringBuf(),
- builder->GetBuffer())
- << '\n';
- Flush(Cerr);
- YT_ABORT();
- }
- // YT_VERIFY(tokenizer.ParseNext());
- switch (tokenizer.GetCurrentType()) {
- case ETokenType::String:
- AppendAttribute(builder, key, TString(tokenizer.CurrentToken().GetStringValue()), indent);
- break;
- case ETokenType::Int64:
- AppendAttribute(builder, key, ToString(tokenizer.CurrentToken().GetInt64Value()), indent);
- break;
- case ETokenType::Uint64:
- AppendAttribute(builder, key, ToString(tokenizer.CurrentToken().GetUint64Value()), indent);
- break;
- case ETokenType::Double:
- AppendAttribute(builder, key, ToString(tokenizer.CurrentToken().GetDoubleValue()), indent);
- break;
- case ETokenType::Boolean:
- AppendAttribute(builder, key, TString(FormatBool(tokenizer.CurrentToken().GetBooleanValue())), indent);
- break;
- default:
- AppendAttribute(builder, key, ConvertToYsonString(value, EYsonFormat::Text).ToString(), indent);
- break;
+ if (isStringTextYson(value)) {
+ AppendAttribute(builder, key, ConvertFromTextYsonString<TString>(value), indent);
+ } else if (isBoolTextYson(value)) {
+ AppendAttribute(builder, key, TString{FormatBool(ConvertFromTextYsonString<bool>(value))}, indent);
+ } else {
+ AppendAttribute(builder, key, value.ToString(), indent);
}
}
@@ -921,15 +824,4 @@ const char* TErrorException::what() const noexcept
////////////////////////////////////////////////////////////////////////////////
-// TODO(arkady-e1ppa): Move this out eventually.
-[[noreturn]] void TErrorAttributes::ThrowCannotParseAttributeException(TStringBuf key, const std::exception& ex)
-{
- THROW_ERROR_EXCEPTION(
- "Error parsing attribute %Qv",
- key)
- << ex;
-}
-
-////////////////////////////////////////////////////////////////////////////////
-
} // namespace NYT
diff --git a/yt/yt/core/misc/stripped_error.h b/library/cpp/yt/error/error.h
index ffec3b0db75..6412ad029b8 100644
--- a/yt/yt/core/misc/stripped_error.h
+++ b/library/cpp/yt/error/error.h
@@ -1,8 +1,6 @@
#pragma once
-#include "public.h"
-
-#include <yt/yt/core/ytree/public.h>
+#include <library/cpp/yt/error/error_code.h>
#include <library/cpp/yt/threading/public.h>
@@ -440,5 +438,5 @@ auto RunNoExcept(F&& functor, As&&... args) noexcept -> decltype(functor(std::fo
} // namespace NYT
#define STRIPPED_ERROR_INL_H_
-#include "stripped_error-inl.h"
+#include "error-inl.h"
#undef STRIPPED_ERROR_INL_H_
diff --git a/library/cpp/yt/error/error_attribute-inl.h b/library/cpp/yt/error/error_attribute-inl.h
new file mode 100644
index 00000000000..df7696b85be
--- /dev/null
+++ b/library/cpp/yt/error/error_attribute-inl.h
@@ -0,0 +1,57 @@
+#ifndef ERROR_ATTRIBUTE_INL_H_
+#error "Direct inclusion of this file is not allowed, include error_attribute.h"
+// For the sake of sane code completion.
+#include "error_attribute.h"
+#endif
+
+#include <library/cpp/yt/yson_string/convert.h>
+#include <library/cpp/yt/yson_string/format.h>
+
+namespace NYT {
+
+////////////////////////////////////////////////////////////////////////////////
+
+namespace NAttributeValueConversionImpl {
+
+template <CPrimitiveConvertible T>
+NYson::TYsonString TagInvoke(TTagInvokeTag<ToErrorAttributeValue>, const T& value)
+{
+ if constexpr (std::constructible_from<TStringBuf, const T&>) {
+ return NYson::ConvertToTextYsonString(TStringBuf(value));
+ } else {
+ return NYson::ConvertToTextYsonString(value);
+ }
+}
+
+////////////////////////////////////////////////////////////////////////////////
+
+inline bool IsBinaryYson(const NYson::TYsonString& yson)
+{
+ using namespace NYson::NDetail;
+
+ auto view = yson.AsStringBuf();
+ return
+ std::ssize(view) != 0 &&
+ (view.front() == EntitySymbol ||
+ view.front() == StringMarker ||
+ view.front() == Int64Marker ||
+ view.front() == DoubleMarker ||
+ view.front() == FalseMarker ||
+ view.front() == TrueMarker ||
+ view.front() == Uint64Marker);
+}
+
+////////////////////////////////////////////////////////////////////////////////
+
+template <CPrimitiveConvertible T>
+T TagInvoke(TFrom<T>, const NYson::TYsonString& value)
+{
+ YT_VERIFY(!IsBinaryYson(value));
+ return NYson::ConvertFromTextYsonString<T>(value);
+}
+
+} // namespace NAttributeValueConversionImpl
+
+////////////////////////////////////////////////////////////////////////////////
+
+} // namespace NYT
diff --git a/library/cpp/yt/error/error_attribute.h b/library/cpp/yt/error/error_attribute.h
index dec4a4dd9b3..8b7a79004a0 100644
--- a/library/cpp/yt/error/error_attribute.h
+++ b/library/cpp/yt/error/error_attribute.h
@@ -1,5 +1,8 @@
#pragma once
+#include "public.h"
+
+#include <library/cpp/yt/misc/guid.h>
#include <library/cpp/yt/misc/tag_invoke_cpo.h>
// TODO(arkady-e1ppa): Eliminate.
@@ -9,43 +12,72 @@ namespace NYT {
////////////////////////////////////////////////////////////////////////////////
-namespace NToAttributeValueImpl {
+template <class T>
+concept CPrimitiveConvertible =
+ std::same_as<T, i8> ||
+ std::same_as<T, i32> ||
+ std::same_as<T, i64> ||
+ std::same_as<T, ui8> ||
+ std::same_as<T, ui32> ||
+ std::same_as<T, ui64> ||
+ std::same_as<T, float> ||
+ std::same_as<T, double> ||
+ std::constructible_from<TStringBuf, const T&> ||
+ std::same_as<T, TDuration> ||
+ std::same_as<T, TInstant> ||
+ std::same_as<T, bool> ||
+ std::same_as<T, TGuid>;
+
+////////////////////////////////////////////////////////////////////////////////
+
+namespace NAttributeValueConversionImpl {
+
+struct TTo
+ : public TTagInvokeCpoBase<TTo>
+{ };
+
+////////////////////////////////////////////////////////////////////////////////
-struct TFn
- : public TTagInvokeCpoBase<TFn>
+template <class U>
+struct TFrom
+ : public TTagInvokeCpoBase<TFrom<U>>
{ };
-} // namespace NToAttributeValueImpl
+} // namespace NAttributeValueConversionImpl
////////////////////////////////////////////////////////////////////////////////
-inline constexpr NToAttributeValueImpl::TFn ToAttributeValue = {};
+inline constexpr NAttributeValueConversionImpl::TTo ToErrorAttributeValue = {};
+template <class U>
+inline constexpr NAttributeValueConversionImpl::TFrom<U> FromErrorAttributeValue = {};
////////////////////////////////////////////////////////////////////////////////
template <class T>
-concept CConvertibleToAttributeValue = CTagInvocableS<
- TTagInvokeTag<ToAttributeValue>,
- NYson::TYsonString(const T&)>;
+concept CConvertibleToAttributeValue = requires (const T& value) {
+ { NYT::ToErrorAttributeValue(value) } -> std::same_as<NYson::TYsonString>;
+};
+
+template <class T>
+concept CConvertibleFromAttributeValue = requires (const NYson::TYsonString& value) {
+ { NYT::FromErrorAttributeValue<T>(value) } -> std::same_as<T>;
+};
////////////////////////////////////////////////////////////////////////////////
struct TErrorAttribute
{
- // NB(arkady-e1ppa): Switch to std::string is quite possible
+ // TODO(arkady-e1ppa): Switch to std::string is quite possible
// however it requires patching IAttributeDictionary or
// switching it to the std::string first for interop reasons.
// Do that later.
using TKey = TString;
- // TODO(arkady-e1ppa): Use ConvertToYsonString(value, Format::Text)
- // here for complex values. Write manual implementations as ToString
- // for primitive types (e.g. integral types, guid, string, time).
using TValue = NYson::TYsonString;
template <CConvertibleToAttributeValue T>
TErrorAttribute(const TKey& key, const T& value)
: Key(key)
- , Value(NYT::ToAttributeValue(value))
+ , Value(NYT::ToErrorAttributeValue(value))
{ }
TKey Key;
@@ -55,3 +87,7 @@ struct TErrorAttribute
////////////////////////////////////////////////////////////////////////////////
} // namespace NYT
+
+#define ERROR_ATTRIBUTE_INL_H_
+#include "error_attribute-inl.h"
+#undef ERROR_ATTRIBUTE_INL_H_
diff --git a/library/cpp/yt/error/error_attributes-inl.h b/library/cpp/yt/error/error_attributes-inl.h
index 6574665865b..0a77d9afb0c 100644
--- a/library/cpp/yt/error/error_attributes-inl.h
+++ b/library/cpp/yt/error/error_attributes-inl.h
@@ -9,34 +9,34 @@ namespace NYT {
////////////////////////////////////////////////////////////////////////////////
template <class T>
- requires CConvertsTo<T, TErrorAttributes::TValue>
+ requires CConvertibleFromAttributeValue<T>
T TErrorAttributes::Get(TStringBuf key) const
{
- auto yson = GetYson(key);
+ auto yson = GetValue(key);
try {
- return NYT::ConvertTo<T>(yson);
+ return NYT::FromErrorAttributeValue<T>(yson);
} catch (const std::exception& ex) {
ThrowCannotParseAttributeException(key, ex);
}
}
template <class T>
- requires CConvertsTo<T, TErrorAttributes::TValue>
+ requires CConvertibleFromAttributeValue<T>
typename TOptionalTraits<T>::TOptional TErrorAttributes::Find(TStringBuf key) const
{
- auto yson = FindYson(key);
- if (!yson) {
+ auto value = FindValue(key);
+ if (!value) {
return typename TOptionalTraits<T>::TOptional();
}
try {
- return NYT::ConvertTo<T>(yson);
+ return NYT::FromErrorAttributeValue<T>(value);
} catch (const std::exception& ex) {
ThrowCannotParseAttributeException(key, ex);
}
}
template <class T>
- requires CConvertsTo<T, TErrorAttributes::TValue>
+ requires CConvertibleFromAttributeValue<T>
T TErrorAttributes::GetAndRemove(const TKey& key)
{
auto result = Get<T>(key);
@@ -45,14 +45,14 @@ T TErrorAttributes::GetAndRemove(const TKey& key)
}
template <class T>
- requires CConvertsTo<T, TErrorAttributes::TValue>
+ requires CConvertibleFromAttributeValue<T>
T TErrorAttributes::Get(TStringBuf key, const T& defaultValue) const
{
return Find<T>(key).value_or(defaultValue);
}
template <class T>
- requires CConvertsTo<T, TErrorAttributes::TValue>
+ requires CConvertibleFromAttributeValue<T>
T TErrorAttributes::GetAndRemove(const TKey& key, const T& defaultValue)
{
auto result = Find<T>(key);
@@ -65,7 +65,7 @@ T TErrorAttributes::GetAndRemove(const TKey& key, const T& defaultValue)
}
template <class T>
- requires CConvertsTo<T, TErrorAttributes::TValue>
+ requires CConvertibleFromAttributeValue<T>
typename TOptionalTraits<T>::TOptional TErrorAttributes::FindAndRemove(const TKey& key)
{
auto result = Find<T>(key);
@@ -81,7 +81,7 @@ void TErrorAttributes::MergeFrom(const TDictionary& dict)
using TTraits = TMergeDictionariesTraits<TDictionary>;
for (const auto& [key, value] : TTraits::MakeIterableView(dict)) {
- SetYson(key, value);
+ SetValue(key, value);
}
}
diff --git a/library/cpp/yt/error/error_attributes.cpp b/library/cpp/yt/error/error_attributes.cpp
index 09aa48eebb9..06db3b211e6 100644
--- a/library/cpp/yt/error/error_attributes.cpp
+++ b/library/cpp/yt/error/error_attributes.cpp
@@ -1,30 +1,70 @@
#include "error_attributes.h"
+#include "error.h"
+#include "error_code.h"
+
namespace NYT {
////////////////////////////////////////////////////////////////////////////////
-TErrorAttributes::TErrorAttributes(void* attributes)
- : Attributes_(attributes)
-{ }
+std::vector<TErrorAttributes::TKey> TErrorAttributes::ListKeys() const
+{
+ std::vector<TString> keys;
+ keys.reserve(Map_.size());
+ for (const auto& [key, value] : Map_) {
+ keys.push_back(key);
+ }
+ return keys;
+}
-void TErrorAttributes::Clear()
+std::vector<TErrorAttributes::TKeyValuePair> TErrorAttributes::ListPairs() const
{
- for (const auto& key : ListKeys()) {
- Remove(key);
+ std::vector<TKeyValuePair> pairs;
+ pairs.reserve(Map_.size());
+ for (const auto& pair : Map_) {
+ pairs.push_back(pair);
}
+ return pairs;
+}
+
+TErrorAttributes::TValue TErrorAttributes::FindValue(TStringBuf key) const
+{
+ auto it = Map_.find(key);
+ return it == Map_.end() ? TValue{} : it->second;
+}
+
+void TErrorAttributes::SetValue(const TKey& key, const TValue& value)
+{
+ Map_[key] = value;
}
-TErrorAttributes::TValue TErrorAttributes::GetYsonAndRemove(const TKey& key)
+void TErrorAttributes::SetAttribute(const TErrorAttribute& attribute)
{
- auto result = GetYson(key);
- Remove(key);
+ SetValue(attribute.Key, attribute.Value);
+}
+
+bool TErrorAttributes::Remove(const TKey& key)
+{
+ return Map_.erase(key) > 0;
+}
+
+TErrorAttributes::TValue TErrorAttributes::GetValue(TStringBuf key) const
+{
+ auto result = FindValue(key);
+ if (!result) {
+ ThrowNoSuchAttributeException(key);
+ }
return result;
}
+void TErrorAttributes::Clear()
+{
+ Map_.clear();
+}
+
bool TErrorAttributes::Contains(TStringBuf key) const
{
- return FindYson(key).operator bool();
+ return Map_.contains(key);
}
bool operator == (const TErrorAttributes& lhs, const TErrorAttributes& rhs)
@@ -59,4 +99,59 @@ bool operator == (const TErrorAttributes& lhs, const TErrorAttributes& rhs)
////////////////////////////////////////////////////////////////////////////////
+namespace {
+
+bool IsSpecialCharacter(char ch)
+{
+ return ch == '\\' || ch == '/' || ch == '@' || ch == '*' || ch == '&' || ch == '[' || ch == '{';
+}
+
+// AppendYPathLiteral.
+void DoFormatAttributeKey(TStringBuilderBase* builder, TStringBuf value)
+{
+ constexpr char asciiBegin = 32;
+ constexpr char asciiEnd = 127;
+ builder->Preallocate(value.length() + 16);
+ for (unsigned char ch : value) {
+ if (IsSpecialCharacter(ch)) {
+ builder->AppendChar('\\');
+ builder->AppendChar(ch);
+ } else if (ch < asciiBegin || ch > asciiEnd) {
+ builder->AppendString(TStringBuf("\\x"));
+ builder->AppendChar(IntToHexLowercase[ch >> 4]);
+ builder->AppendChar(IntToHexLowercase[ch & 0xf]);
+ } else {
+ builder->AppendChar(ch);
+ }
+ }
+}
+
+} // namespace
+
+////////////////////////////////////////////////////////////////////////////////
+
+[[noreturn]] void TErrorAttributes::ThrowCannotParseAttributeException(TStringBuf key, const std::exception& ex)
+{
+ THROW_ERROR_EXCEPTION(
+ "Error parsing attribute %Qv",
+ key)
+ << ex;
+}
+
+[[noreturn]] void TErrorAttributes::ThrowNoSuchAttributeException(TStringBuf key)
+{
+ auto formatAttributeKey = [] (auto key) {
+ TStringBuilder builder;
+ DoFormatAttributeKey(&builder, key);
+ return builder.Flush();
+ };
+
+ THROW_ERROR_EXCEPTION(
+ /*NYTree::EErrorCode::ResolveError*/ TErrorCode{500},
+ "Attribute %Qv is not found",
+ formatAttributeKey(key));
+}
+
+////////////////////////////////////////////////////////////////////////////////
+
} // namespace NYT
diff --git a/library/cpp/yt/error/error_attributes.h b/library/cpp/yt/error/error_attributes.h
index 80dd80de484..da71af63cc4 100644
--- a/library/cpp/yt/error/error_attributes.h
+++ b/library/cpp/yt/error/error_attributes.h
@@ -1,6 +1,5 @@
#pragma once
-#include "convert_to_cpo.h"
#include "error_attribute.h"
#include "mergeable_dictionary.h"
@@ -10,12 +9,6 @@ namespace NYT {
////////////////////////////////////////////////////////////////////////////////
-// For now this is just an opaque handle to error attributes
-// used to remove dependency on IAttributeDictionary in public API.
-// Eventually it would be a simple hash map.
-// NB(arkady-e1ppa): For now most methods are defined in yt/yt/core/misc/stripped_error.cpp
-// eventually they will be moved here.
-// NB(arkady-e1ppa): For now we use TYsonString as value.
// TODO(arkady-e1ppa): Try switching to TString/std::string eventually.
// representing text-encoded yson string eventually (maybe).
class TErrorAttributes
@@ -31,11 +24,8 @@ public:
//! Returns the list of all key-value pairs in the dictionary.
std::vector<TKeyValuePair> ListPairs() const;
- //! Returns the value of the attribute (null indicates that the attribute is not found).
- TValue FindYson(TStringBuf key) const;
-
//! Sets the value of the attribute.
- void SetYson(const TKey& key, const TValue& value);
+ void SetAttribute(const TErrorAttribute& attribute);
//! Removes the attribute.
//! Returns |true| if the attribute was removed or |false| if there is no attribute with this key.
@@ -44,59 +34,50 @@ public:
//! Removes all attributes.
void Clear();
- //! Returns the value of the attribute (throws an exception if the attribute is not found).
- TValue GetYson(TStringBuf key) const;
-
- //! Same as #GetYson but removes the value.
- TValue GetYsonAndRemove(const TKey& key);
-
//! Returns |true| iff the given key is present.
bool Contains(TStringBuf key) const;
- // TODO(arkady-e1ppa): By default deserialization is located at yt/core
- // consider using deserialization of some default types (guid, string, int, double)
- // to be supported and everything else not supported without inclusion of yt/core.
//! Finds the attribute and deserializes its value.
//! Throws if no such value is found.
template <class T>
- requires CConvertsTo<T, TValue>
+ requires CConvertibleFromAttributeValue<T>
T Get(TStringBuf key) const;
//! Same as #Get but removes the value.
template <class T>
- requires CConvertsTo<T, TValue>
+ requires CConvertibleFromAttributeValue<T>
T GetAndRemove(const TKey& key);
//! Finds the attribute and deserializes its value.
//! Uses default value if no such attribute is found.
template <class T>
- requires CConvertsTo<T, TValue>
+ requires CConvertibleFromAttributeValue<T>
T Get(TStringBuf key, const T& defaultValue) const;
//! Same as #Get but removes the value if it exists.
template <class T>
- requires CConvertsTo<T, TValue>
+ requires CConvertibleFromAttributeValue<T>
T GetAndRemove(const TKey& key, const T& defaultValue);
//! Finds the attribute and deserializes its value.
//! Returns null if no such attribute is found.
template <class T>
- requires CConvertsTo<T, TValue>
+ requires CConvertibleFromAttributeValue<T>
typename TOptionalTraits<T>::TOptional Find(TStringBuf key) const;
//! Same as #Find but removes the value if it exists.
template <class T>
- requires CConvertsTo<T, TValue>
+ requires CConvertibleFromAttributeValue<T>
typename TOptionalTraits<T>::TOptional FindAndRemove(const TKey& key);
template <CMergeableDictionary TDictionary>
void MergeFrom(const TDictionary& dict);
private:
- void* Attributes_; // IAttributesDictionary*
+ THashMap<TKey, TValue> Map_;
friend class TErrorOr<void>;
- explicit TErrorAttributes(void* attributes);
+ TErrorAttributes() = default;
TErrorAttributes(const TErrorAttributes& other) = default;
TErrorAttributes& operator= (const TErrorAttributes& other) = default;
@@ -104,8 +85,17 @@ private:
TErrorAttributes(TErrorAttributes&& other) = default;
TErrorAttributes& operator= (TErrorAttributes&& other) = default;
- // defined in yt/yt/core/misc/stripped_error.cpp right now.
+ //! Returns the value of the attribute (null indicates that the attribute is not found).
+ TValue FindValue(TStringBuf key) const;
+
+ //! Returns the value of the attribute (throws an exception if the attribute is not found).
+ TValue GetValue(TStringBuf key) const;
+
+ //! Sets the value of the attribute.
+ void SetValue(const TKey& key, const TValue& value);
+
[[noreturn]] static void ThrowCannotParseAttributeException(TStringBuf key, const std::exception& ex);
+ [[noreturn]] static void ThrowNoSuchAttributeException(TStringBuf key);
};
bool operator == (const TErrorAttributes& lhs, const TErrorAttributes& rhs);
diff --git a/yt/yt/core/misc/error_code.cpp b/library/cpp/yt/error/error_code.cpp
index 666db885f05..9cb0d57dc2c 100644
--- a/yt/yt/core/misc/error_code.cpp
+++ b/library/cpp/yt/error/error_code.cpp
@@ -1,6 +1,6 @@
#include "error_code.h"
-#include <yt/yt/core/logging/log.h>
+#include <library/cpp/yt/logging/logger.h>
#include <library/cpp/yt/misc/global.h>
diff --git a/library/cpp/yt/error/error_code.h b/library/cpp/yt/error/error_code.h
new file mode 100644
index 00000000000..1c2c08fbb4a
--- /dev/null
+++ b/library/cpp/yt/error/error_code.h
@@ -0,0 +1,101 @@
+#pragma once
+
+#include <library/cpp/yt/misc/enum.h>
+#include <library/cpp/yt/misc/port.h>
+#include <library/cpp/yt/misc/static_initializer.h>
+
+#include <library/cpp/yt/string/format.h>
+
+#include <util/generic/hash_set.h>
+
+namespace NYT {
+
+////////////////////////////////////////////////////////////////////////////////
+
+class TErrorCodeRegistry
+{
+public:
+ static TErrorCodeRegistry* Get();
+
+ struct TErrorCodeInfo
+ {
+ TString Namespace;
+ //! Human-readable error code name.
+ TString Name;
+
+ bool operator==(const TErrorCodeInfo& rhs) const;
+ };
+
+ struct TErrorCodeRangeInfo
+ {
+ int From;
+ int To;
+ TString Namespace;
+ std::function<TString(int code)> Formatter;
+
+ TErrorCodeInfo Get(int code) const;
+ bool Intersects(const TErrorCodeRangeInfo& other) const;
+ bool Contains(int value) const;
+ };
+
+ //! Retrieves info from registered codes and code ranges.
+ TErrorCodeInfo Get(int code) const;
+
+ //! Retrieves information about registered codes.
+ THashMap<int, TErrorCodeInfo> GetAllErrorCodes() const;
+
+ //! Retrieves information about registered code ranges.
+ std::vector<TErrorCodeRangeInfo> GetAllErrorCodeRanges() const;
+
+ //! Registers a single error code.
+ void RegisterErrorCode(int code, const TErrorCodeInfo& errorCodeInfo);
+
+ //! Registers a range of error codes given a human-readable code to name formatter.
+ void RegisterErrorCodeRange(int from, int to, TString namespaceName, std::function<TString(int code)> formatter);
+
+ static TString ParseNamespace(const std::type_info& errorCodeEnumTypeInfo);
+
+private:
+ THashMap<int, TErrorCodeInfo> CodeToInfo_;
+ std::vector<TErrorCodeRangeInfo> ErrorCodeRanges_;
+
+ void CheckCodesAgainstRanges() const;
+};
+
+void FormatValue(
+ TStringBuilderBase* builder,
+ const TErrorCodeRegistry::TErrorCodeInfo& errorCodeInfo,
+ TStringBuf spec);
+
+void FormatValue(
+ TStringBuilderBase* builder,
+ const TErrorCodeRegistry::TErrorCodeRangeInfo& errorCodeInfo,
+ TStringBuf spec);
+
+////////////////////////////////////////////////////////////////////////////////
+
+#define YT_DEFINE_ERROR_ENUM(seq) \
+ DEFINE_ENUM(EErrorCode, seq); \
+ YT_ATTRIBUTE_USED inline const void* ErrorEnum_EErrorCode = [] { \
+ for (auto errorCode : ::NYT::TEnumTraits<EErrorCode>::GetDomainValues()) { \
+ ::NYT::TErrorCodeRegistry::Get()->RegisterErrorCode( \
+ static_cast<int>(errorCode), \
+ {::NYT::TErrorCodeRegistry::ParseNamespace(typeid(EErrorCode)), ToString(errorCode)}); \
+ } \
+ return nullptr; \
+ } ()
+
+////////////////////////////////////////////////////////////////////////////////
+
+//! NB: This macro should only by used in cpp files.
+#define YT_DEFINE_ERROR_CODE_RANGE(from, to, namespaceName, formatter) \
+ YT_STATIC_INITIALIZER( \
+ ::NYT::TErrorCodeRegistry::Get()->RegisterErrorCodeRange( \
+ from, \
+ to, \
+ namespaceName, \
+ formatter));
+
+////////////////////////////////////////////////////////////////////////////////
+
+} // namespace NYT
diff --git a/yt/yt/core/misc/error_helpers-inl.h b/library/cpp/yt/error/error_helpers-inl.h
index d15c5f6f93e..d15c5f6f93e 100644
--- a/yt/yt/core/misc/error_helpers-inl.h
+++ b/library/cpp/yt/error/error_helpers-inl.h
diff --git a/yt/yt/core/misc/error_helpers.h b/library/cpp/yt/error/error_helpers.h
index 5eda19e4bf7..b68385be357 100644
--- a/yt/yt/core/misc/error_helpers.h
+++ b/library/cpp/yt/error/error_helpers.h
@@ -1,8 +1,8 @@
#pragma once
-#include "error.h"
+#include <library/cpp/yt/error/error.h>
-#include <yt/yt/core/ytree/attributes.h>
+#include <library/cpp/yt/error/error_attributes.h>
#include <library/cpp/yt/misc/optional.h>
diff --git a/library/cpp/yt/error/origin_attributes.cpp b/library/cpp/yt/error/origin_attributes.cpp
index 5ff0b039339..6f86e31ae8f 100644
--- a/library/cpp/yt/error/origin_attributes.cpp
+++ b/library/cpp/yt/error/origin_attributes.cpp
@@ -1,4 +1,5 @@
#include "origin_attributes.h"
+#include "error_attributes.h"
#include <library/cpp/yt/assert/assert.h>
@@ -105,6 +106,48 @@ TString FormatOrigin(const TOriginAttributes& attributes)
}));
}
+////////////////////////////////////////////////////////////////////////////////
+
+TOriginAttributes ExtractFromDictionary(TErrorAttributes* attributes)
+{
+ using TFunctor = TOriginAttributes(*)(TErrorAttributes*);
+
+ if (auto strong = NGlobal::GetErasedVariable(ExtractFromDictionaryTag)) {
+ return strong->AsConcrete<TFunctor>()(attributes);
+ }
+
+ return ExtractFromDictionaryDefault(attributes);
+}
+
+////////////////////////////////////////////////////////////////////////////////
+
+TOriginAttributes ExtractFromDictionaryDefault(TErrorAttributes* attributes)
+{
+ TOriginAttributes result;
+ if (attributes == nullptr) {
+ return result;
+ }
+
+ // TODO(arkady-e1ppa): Try using std::string here.
+ static const TString HostKey("host");
+ result.HostHolder = TSharedRef::FromString(attributes->GetAndRemove(HostKey, TString()));
+ result.Host = result.HostHolder.empty() ? TStringBuf() : TStringBuf(result.HostHolder.Begin(), result.HostHolder.End());
+
+ static const TString DatetimeKey("datetime");
+ result.Datetime = attributes->GetAndRemove(DatetimeKey, TInstant());
+
+ static const TString PidKey("pid");
+ result.Pid = attributes->GetAndRemove(PidKey, TProcessId{});
+
+ static const TString TidKey("tid");
+ result.Tid = attributes->GetAndRemove(TidKey, NThreading::InvalidThreadId);
+
+ static const TString ThreadNameKey("thread");
+ result.ThreadName = attributes->GetAndRemove<TString>(ThreadNameKey, TString());
+
+ return result;
+}
+
} // namespace NDetail
////////////////////////////////////////////////////////////////////////////////
diff --git a/library/cpp/yt/error/origin_attributes.h b/library/cpp/yt/error/origin_attributes.h
index d98782469b8..e25eb37ea7d 100644
--- a/library/cpp/yt/error/origin_attributes.h
+++ b/library/cpp/yt/error/origin_attributes.h
@@ -1,5 +1,7 @@
#pragma once
+#include "public.h"
+
#include <library/cpp/yt/global/access.h>
#include <library/cpp/yt/memory/ref.h>
@@ -76,6 +78,14 @@ inline constexpr NGlobal::TVariableTag ExtractFromDictionaryTag = {};
std::optional<TOriginAttributes::TErasedExtensionData> GetExtensionData();
TString FormatOrigin(const TOriginAttributes& attributes);
+////////////////////////////////////////////////////////////////////////////////
+
+// Weak symbol.
+TOriginAttributes ExtractFromDictionary(TErrorAttributes* attributes);
+
+// Default impl of weak symbol.
+TOriginAttributes ExtractFromDictionaryDefault(TErrorAttributes* attributes);
+
} // namespace NDetail
////////////////////////////////////////////////////////////////////////////////
diff --git a/library/cpp/yt/error/public.h b/library/cpp/yt/error/public.h
index 04201128aa3..77ce70ee07f 100644
--- a/library/cpp/yt/error/public.h
+++ b/library/cpp/yt/error/public.h
@@ -1,6 +1,6 @@
#pragma once
-#include <library/cpp/yt/yson_string/string.h>
+#include "error_code.h"
namespace NYT {
@@ -9,6 +9,7 @@ namespace NYT {
template <class T>
class TErrorOr;
+using TError = TErrorOr<void>;
struct TErrorAttribute;
class TErrorAttributes;
@@ -16,4 +17,15 @@ struct TOriginAttributes;
////////////////////////////////////////////////////////////////////////////////
+YT_DEFINE_ERROR_ENUM(
+ ((OK) (0))
+ ((Generic) (1))
+ ((Canceled) (2))
+ ((Timeout) (3))
+ ((FutureCombinerFailure) (4))
+ ((FutureCombinerShortcut)(5))
+);
+
+////////////////////////////////////////////////////////////////////////////////
+
} // namespace NYT
diff --git a/yt/yt/core/misc/unittests/error_code_ut.cpp b/library/cpp/yt/error/unittests/error_code_ut.cpp
index 0fddbbee79d..4bdb17f5d9c 100644
--- a/yt/yt/core/misc/unittests/error_code_ut.cpp
+++ b/library/cpp/yt/error/unittests/error_code_ut.cpp
@@ -1,7 +1,7 @@
#include <yt/yt/core/test_framework/framework.h>
-#include <yt/yt/core/misc/error.h>
-#include <yt/yt/core/misc/error_code.h>
+#include <library/cpp/yt/error/error.h>
+#include <library/cpp/yt/error/error_code.h>
#include <library/cpp/yt/string/format.h>
diff --git a/library/cpp/yt/error/unittests/error_ut.cpp b/library/cpp/yt/error/unittests/error_ut.cpp
new file mode 100644
index 00000000000..a4248b3956a
--- /dev/null
+++ b/library/cpp/yt/error/unittests/error_ut.cpp
@@ -0,0 +1,779 @@
+#include <yt/yt/core/test_framework/framework.h>
+
+#include <library/cpp/yt/error/error.h>
+#include <library/cpp/yt/error/error_helpers.h>
+
+#include <util/stream/str.h>
+#include <util/string/join.h>
+#include <util/string/split.h>
+
+namespace NYT {
+namespace {
+
+using namespace NYson;
+
+////////////////////////////////////////////////////////////////////////////////
+
+class TAdlException
+ : public std::exception
+{
+public:
+ static int ResetCallCount()
+ {
+ return std::exchange(OverloadCallCount, 0);
+ }
+
+ const char* what() const noexcept override
+ {
+ return "Adl exception";
+ }
+
+ // Simulate overload from TAdlException::operator <<
+ template <class TLikeThis, class TArg>
+ requires std::derived_from<std::decay_t<TLikeThis>, TAdlException>
+ friend TLikeThis&& operator << (TLikeThis&& ex, const TArg& /*other*/)
+ {
+ ++OverloadCallCount;
+ return std::forward<TLikeThis>(ex);
+ }
+
+private:
+ static inline int OverloadCallCount = 0;
+};
+
+class TAdlArgument
+{
+public:
+ static int ResetCallCount()
+ {
+ return std::exchange(OverloadCallCount, 0);
+ }
+
+ // Simulate overload TAdlArgument::operator <<
+ friend TError operator << (TError&& error, const TAdlArgument& /*other*/)
+ {
+ static const TErrorAttribute Attr("attr", "attr_value");
+ ++OverloadCallCount;
+ return std::move(error) << Attr;
+ }
+
+ friend TError operator << (const TError& error, const TAdlArgument& /*other*/)
+ {
+ static const TErrorAttribute Attr("attr", "attr_value");
+ ++OverloadCallCount;
+ return error << Attr;
+ }
+
+private:
+ static inline int OverloadCallCount = 0;
+};
+
+class TWidget
+{
+public:
+ TWidget()
+ {
+ DefaultConstructorCalls++;
+ };
+
+ TWidget(const TWidget&)
+ {
+ CopyConstructorCalls++;
+ }
+ TWidget& operator = (const TWidget&) = delete;
+
+ TWidget(TWidget&&)
+ {
+ MoveConstructorCalls++;
+ }
+ TWidget& operator = (TWidget&&) = delete;
+
+ static int ResetDefaultCount()
+ {
+ return std::exchange(DefaultConstructorCalls, 0);
+ }
+
+ static int ResetCopyCount()
+ {
+ return std::exchange(CopyConstructorCalls, 0);
+ }
+
+ static int ResetMoveCount()
+ {
+ return std::exchange(MoveConstructorCalls, 0);
+ }
+
+private:
+ static inline int DefaultConstructorCalls = 0;
+ static inline int CopyConstructorCalls = 0;
+ static inline int MoveConstructorCalls = 0;
+};
+
+////////////////////////////////////////////////////////////////////////////////
+
+template <class TOverloadTest, bool LeftOperandHasUserDefinedOverload = false>
+void IterateTestOverEveryRightOperand(TOverloadTest& tester)
+{
+ {
+ TErrorAttribute attribute("attr", "attr_value");
+ const auto& attributeRef = attribute;
+ tester(attributeRef);
+ }
+
+ {
+ std::vector<TErrorAttribute> attributeVector{{"attr1", "attr_value"}, {"attr2", "attr_value"}};
+ const auto& attributeVectorRef = attributeVector;
+ tester(attributeVectorRef);
+ }
+
+ {
+ TError error("Error");
+
+ const auto& errorRef = error;
+ tester(errorRef);
+
+ auto errorCopy = error;
+ tester(std::move(errorCopy));
+
+ if constexpr (!LeftOperandHasUserDefinedOverload) {
+ EXPECT_TRUE(errorCopy.IsOK());
+ }
+ }
+
+ {
+ std::vector<TError> vectorError{TError("Error"), TError("Error")};
+
+ const auto& vectorErrorRef = vectorError;
+ tester(vectorErrorRef);
+
+ auto vectorErrorCopy = vectorError;
+ tester(std::move(vectorErrorCopy));
+
+ if constexpr (!LeftOperandHasUserDefinedOverload) {
+ for (const auto& errorCopy : vectorErrorCopy) {
+ EXPECT_TRUE(errorCopy.IsOK());
+ }
+ }
+ }
+
+ {
+ TError error("Error");
+
+ const auto& attributeDictionaryRef = error.Attributes();
+ tester(attributeDictionaryRef);
+ }
+
+ {
+ try {
+ THROW_ERROR TError("Test error");
+ } catch(const NYT::TErrorException& ex) {
+ const auto& exRef = ex;
+ tester(exRef);
+
+ auto exCopy = ex;
+ tester(std::move(exCopy));
+ }
+ }
+
+ {
+ TErrorOr<int> err(std::exception{});
+
+ const auto& errRef = err;
+ tester(errRef);
+
+ auto errCopy = err;
+ tester(std::move(errCopy));
+
+ if constexpr (!LeftOperandHasUserDefinedOverload) {
+ EXPECT_TRUE(errCopy.IsOK());
+ }
+ }
+
+ {
+ TAdlArgument adlArg;
+
+ const TAdlArgument& adlArgRef = adlArg;
+ tester(adlArgRef);
+
+ if constexpr (!LeftOperandHasUserDefinedOverload) {
+ EXPECT_EQ(TAdlArgument::ResetCallCount(), 1);
+ }
+ }
+}
+
+template <class T>
+void SetErrorAttribute(TError* error, TString key, const T& value)
+{
+ *error <<= TErrorAttribute(key, value);
+}
+
+////////////////////////////////////////////////////////////////////////////////
+
+TEST(TErrorTest, BitshiftOverloadsExplicitLeftOperand)
+{
+ // TError&& overload.
+ auto moveTester = [] (auto&& arg) {
+ TError error = TError("Test error");
+ TError moved = std::move(error) << std::forward<decltype(arg)>(arg);
+ EXPECT_TRUE(error.IsOK());
+ EXPECT_EQ(moved.GetMessage(), "Test error");
+ };
+ IterateTestOverEveryRightOperand(moveTester);
+
+ // const TError& overloads.
+ auto copyTester = [] (auto&& arg) {
+ TError error = TError("Test error");
+ TError copy = error << std::forward<decltype(arg)>(arg);
+ EXPECT_EQ(error.GetMessage(), copy.GetMessage());
+ };
+ IterateTestOverEveryRightOperand(copyTester);
+
+ // Test that TError pr value binds correctly and the call itself is unambiguous.
+ auto prvalueTester = [] (auto&& arg) {
+ TError error = TError("Test error") << std::forward<decltype(arg)>(arg);
+ EXPECT_EQ(error.GetMessage(), "Test error");
+ };
+ IterateTestOverEveryRightOperand(prvalueTester);
+}
+
+TEST(TErrorTest, BitshiftOverloadsImplicitLeftOperand)
+{
+ // We want to be able to write THROW_ERROR ex
+ auto throwErrorTester1 = [] (auto&& arg) {
+ try {
+ try {
+ THROW_ERROR TError("Test error");
+ } catch(const NYT::TErrorException& ex) {
+ THROW_ERROR ex << std::forward<decltype(arg)>(arg);
+ }
+ } catch(const NYT::TErrorException& ex) {
+ TError error = ex;
+ EXPECT_EQ(error.GetMessage(), "Test error");
+ }
+ };
+ IterateTestOverEveryRightOperand(throwErrorTester1);
+
+ // We also want to be able to write THROW_ERROR TError(smth) without compiler errors
+ auto throwErrorTester2 = [] (auto&& arg) {
+ try {
+ try {
+ THROW_ERROR TError("Test error");
+ } catch(const NYT::TErrorException& ex) {
+ THROW_ERROR TError(ex) << std::forward<decltype(arg)>(arg);
+ }
+ } catch(const NYT::TErrorException& ex) {
+ TError error = ex;
+ EXPECT_EQ(error.GetMessage(), "Test error");
+ }
+ };
+ IterateTestOverEveryRightOperand(throwErrorTester2);
+
+ // Left operand ADL finds the user-defined overload over NYT one.
+ // In this case AdlException should find templated function
+ // specialization with perfect match for args over conversions.
+ auto adlResolutionTester = [] (auto&& arg) {
+ TAdlException ex;
+ auto result = ex << std::forward<decltype(arg)>(arg);
+ static_assert(std::same_as<TAdlException, std::decay_t<decltype(result)>>);
+ EXPECT_EQ(TAdlException::ResetCallCount(), 1);
+ };
+ IterateTestOverEveryRightOperand<
+ decltype(adlResolutionTester),
+ /*LeftOperandHasUserDefinedOverload*/ true>(adlResolutionTester);
+
+ // Make sure no ambiguous calls.
+ auto genericErrorOrTester = [] (auto&& arg) {
+ TErrorOr<int> err(std::exception{});
+ TError error = err << std::forward<decltype(arg)>(arg);
+ EXPECT_EQ(error.GetCode(), NYT::EErrorCode::Generic);
+ };
+ IterateTestOverEveryRightOperand(genericErrorOrTester);
+}
+
+TEST(TErrorTest, Wrap)
+{
+ TError error("Error");
+
+ auto wrapped = error.Wrap("Wrapped error");
+ EXPECT_EQ(wrapped.GetCode(), NYT::EErrorCode::Generic);
+ EXPECT_EQ(wrapped.GetMessage(), "Wrapped error");
+ EXPECT_EQ(wrapped.InnerErrors().size(), 1u);
+ EXPECT_EQ(wrapped.InnerErrors()[0], error);
+
+ auto triviallyWrapped = error.Wrap();
+ EXPECT_EQ(triviallyWrapped, error);
+}
+
+TEST(TErrorTest, WrapRValue)
+{
+ TError error("Error");
+
+ TError errorCopy = error;
+ auto wrapped = std::move(errorCopy).Wrap("Wrapped error");
+ EXPECT_TRUE(errorCopy.IsOK());
+ EXPECT_EQ(wrapped.GetCode(), NYT::EErrorCode::Generic);
+ EXPECT_EQ(wrapped.GetMessage(), "Wrapped error");
+ EXPECT_EQ(wrapped.InnerErrors().size(), 1u);
+ EXPECT_EQ(wrapped.InnerErrors()[0], error);
+
+ TError anotherErrorCopy = error;
+ auto trviallyWrapped = std::move(anotherErrorCopy).Wrap();
+ EXPECT_TRUE(anotherErrorCopy.IsOK());
+ EXPECT_EQ(trviallyWrapped, error);
+}
+
+TEST(TErrorTest, ThrowErrorExceptionIfFailedMacroJustWorks)
+{
+ TError error;
+
+ EXPECT_NO_THROW(THROW_ERROR_EXCEPTION_IF_FAILED(error, "Outer error"));
+
+ error = TError("Real error");
+
+ TError errorCopy = error;
+
+ try {
+ THROW_ERROR_EXCEPTION_IF_FAILED(errorCopy, "Outer error");
+ } catch (const std::exception& ex) {
+ TError outerError(ex);
+
+ EXPECT_TRUE(errorCopy.IsOK());
+ EXPECT_EQ(outerError.GetMessage(), "Outer error");
+ EXPECT_EQ(outerError.InnerErrors().size(), 1u);
+ EXPECT_EQ(outerError.InnerErrors()[0], error);
+ }
+}
+
+TEST(TErrorTest, ThrowErrorExceptionIfFailedMacroExpression)
+{
+ try {
+ THROW_ERROR_EXCEPTION_IF_FAILED(
+ TError("Inner error")
+ << TErrorAttribute("attr", "attr_value"),
+ "Outer error");
+ } catch (const std::exception& ex) {
+ TError outerError(ex);
+
+ EXPECT_EQ(outerError.GetMessage(), "Outer error");
+ EXPECT_EQ(outerError.InnerErrors().size(), 1u);
+ EXPECT_EQ(outerError.InnerErrors()[0].GetMessage(), "Inner error");
+ EXPECT_EQ(outerError.InnerErrors()[0].Attributes().Get<TString>("attr"), "attr_value");
+ }
+}
+
+TEST(TErrorTest, ThrowErrorExceptionIfFailedMacroDontStealValue)
+{
+ TErrorOr<TWidget> widget = TWidget();
+ EXPECT_TRUE(widget.IsOK());
+ EXPECT_EQ(TWidget::ResetDefaultCount(), 1);
+ EXPECT_EQ(TWidget::ResetCopyCount(), 0);
+ EXPECT_EQ(TWidget::ResetMoveCount(), 1);
+
+ EXPECT_NO_THROW(THROW_ERROR_EXCEPTION_IF_FAILED(widget));
+ EXPECT_TRUE(widget.IsOK());
+ EXPECT_NO_THROW(widget.ValueOrThrow());
+ EXPECT_EQ(TWidget::ResetDefaultCount(), 0);
+ EXPECT_EQ(TWidget::ResetCopyCount(), 0);
+ EXPECT_EQ(TWidget::ResetMoveCount(), 0);
+}
+
+TEST(TErrorTest, ThrowErrorExceptionIfFailedMacroDontDupeCalls)
+{
+ EXPECT_NO_THROW(THROW_ERROR_EXCEPTION_IF_FAILED(TErrorOr<TWidget>(TWidget())));
+ EXPECT_EQ(TWidget::ResetDefaultCount(), 1);
+ EXPECT_EQ(TWidget::ResetCopyCount(), 0);
+ EXPECT_EQ(TWidget::ResetMoveCount(), 1);
+}
+
+TEST(TErrorTest, ErrorSkeletonStubImplementation)
+{
+ TError error("foo");
+ EXPECT_THROW(error.GetSkeleton(), std::exception);
+}
+
+TEST(TErrorTest, FormatCtor)
+{
+ // EXPECT_EQ("Some error %v", TError("Some error %v").GetMessage()); // No longer compiles due to static analysis.
+ EXPECT_EQ("Some error hello", TError("Some error %v", "hello").GetMessage());
+}
+
+TEST(TErrorTest, FindRecursive)
+{
+ auto inner = TError("Inner")
+ << TErrorAttribute("inner_attr", 42);
+ auto error = TError("Error")
+ << inner
+ << TErrorAttribute("attr", 8);
+
+ auto attr = FindAttribute<int>(error, "attr");
+ EXPECT_TRUE(attr);
+ EXPECT_EQ(*attr, 8);
+
+ EXPECT_FALSE(FindAttribute<int>(error, "inner_attr"));
+
+ auto innerAttr = FindAttributeRecursive<int>(error, "inner_attr");
+ EXPECT_TRUE(innerAttr);
+ EXPECT_EQ(*innerAttr, 42);
+}
+
+TEST(TErrorTest, TruncateSimple)
+{
+ auto error = TError("Some error")
+ << TErrorAttribute("my_attr", "Attr value")
+ << TError("Inner error");
+ auto truncatedError = error.Truncate();
+ EXPECT_EQ(error.GetCode(), truncatedError.GetCode());
+ EXPECT_EQ(error.GetMessage(), truncatedError.GetMessage());
+ EXPECT_EQ(error.GetPid(), truncatedError.GetPid());
+ EXPECT_EQ(error.GetTid(), truncatedError.GetTid());
+ EXPECT_EQ(error.GetDatetime(), truncatedError.GetDatetime());
+ EXPECT_EQ(error.Attributes().Get<TString>("my_attr"), truncatedError.Attributes().Get<TString>("my_attr"));
+ EXPECT_EQ(error.InnerErrors().size(), truncatedError.InnerErrors().size());
+ EXPECT_EQ(error.InnerErrors()[0].GetMessage(), truncatedError.InnerErrors()[0].GetMessage());
+}
+
+TEST(TErrorTest, TruncateLarge)
+{
+ auto error = TError("Some long long error")
+ << TError("First inner error")
+ << TError("Second inner error")
+ << TError("Third inner error")
+ << TError("Fourth inner error");
+ SetErrorAttribute(&error, "my_attr", "Some long long attr");
+
+ auto truncatedError = error.Truncate(/*maxInnerErrorCount*/ 3, /*stringLimit*/ 10);
+ EXPECT_EQ(error.GetCode(), truncatedError.GetCode());
+ EXPECT_EQ("Some long ...<message truncated>", truncatedError.GetMessage());
+ EXPECT_EQ("...<attribute truncated>...", truncatedError.Attributes().Get<TString>("my_attr"));
+ EXPECT_EQ(truncatedError.InnerErrors().size(), 3u);
+
+ EXPECT_EQ("First inne...<message truncated>", truncatedError.InnerErrors()[0].GetMessage());
+ EXPECT_EQ("Second inn...<message truncated>", truncatedError.InnerErrors()[1].GetMessage());
+ EXPECT_EQ("Fourth inn...<message truncated>", truncatedError.InnerErrors()[2].GetMessage());
+}
+
+TEST(TErrorTest, TruncateSimpleRValue)
+{
+ auto error = TError("Some error")
+ << TErrorAttribute("my_attr", "Attr value")
+ << TError("Inner error");
+ auto errorCopy = error;
+ auto truncatedError = std::move(errorCopy).Truncate();
+ EXPECT_TRUE(errorCopy.IsOK());
+
+ EXPECT_EQ(error.GetCode(), truncatedError.GetCode());
+ EXPECT_EQ(error.GetMessage(), truncatedError.GetMessage());
+ EXPECT_EQ(error.GetPid(), truncatedError.GetPid());
+ EXPECT_EQ(error.GetTid(), truncatedError.GetTid());
+ EXPECT_EQ(error.GetDatetime(), truncatedError.GetDatetime());
+ EXPECT_EQ(error.Attributes().Get<TString>("my_attr"), truncatedError.Attributes().Get<TString>("my_attr"));
+ EXPECT_EQ(error.InnerErrors().size(), truncatedError.InnerErrors().size());
+ EXPECT_EQ(error.InnerErrors()[0].GetMessage(), truncatedError.InnerErrors()[0].GetMessage());
+}
+
+TEST(TErrorTest, TruncateLargeRValue)
+{
+ auto error = TError("Some long long error")
+ << TError("First inner error")
+ << TError("Second inner error")
+ << TError("Third inner error")
+ << TError("Fourth inner error");
+ SetErrorAttribute(&error, "my_attr", "Some long long attr");
+
+ auto errorCopy = error;
+ auto truncatedError = std::move(errorCopy).Truncate(/*maxInnerErrorCount*/ 3, /*stringLimit*/ 10);
+ EXPECT_TRUE(errorCopy.IsOK());
+
+ EXPECT_EQ(error.GetCode(), truncatedError.GetCode());
+ EXPECT_EQ("Some long ...<message truncated>", truncatedError.GetMessage());
+ EXPECT_EQ("...<attribute truncated>...", truncatedError.Attributes().Get<TString>("my_attr"));
+ EXPECT_EQ(truncatedError.InnerErrors().size(), 3u);
+
+ EXPECT_EQ("First inne...<message truncated>", truncatedError.InnerErrors()[0].GetMessage());
+ EXPECT_EQ("Second inn...<message truncated>", truncatedError.InnerErrors()[1].GetMessage());
+ EXPECT_EQ("Fourth inn...<message truncated>", truncatedError.InnerErrors()[2].GetMessage());
+}
+
+TEST(TErrorTest, TruncateConsistentOverloads)
+{
+ auto error = TError("Some long long error")
+ << TError("First inner error")
+ << TError("Second inner error")
+ << TError("Third inner error")
+ << TError("Fourth inner error");
+ SetErrorAttribute(&error, "my_attr", "Some long long attr");
+
+ auto errorCopy = error;
+ auto truncatedRValueError = std::move(errorCopy).Truncate(/*maxInnerErrorCount*/ 3, /*stringLimit*/ 10);
+
+ auto trunactedLValueError = error.Truncate(/*maxInnerErrorCount*/ 3, /*stringLimit*/ 10);
+
+ EXPECT_EQ(truncatedRValueError, trunactedLValueError);
+}
+
+TEST(TErrorTest, TruncateWhitelist)
+{
+ auto error = TError("Some error");
+ SetErrorAttribute(&error, "attr1", "Some long long attr");
+ SetErrorAttribute(&error, "attr2", "Some long long attr");
+
+ THashSet<TStringBuf> myWhitelist = {"attr2"};
+
+ auto truncatedError = error.Truncate(2, 10, myWhitelist);
+
+ EXPECT_EQ(error.GetCode(), truncatedError.GetCode());
+ EXPECT_EQ(error.GetMessage(), truncatedError.GetMessage());
+
+ EXPECT_EQ("...<attribute truncated>...", truncatedError.Attributes().Get<TString>("attr1"));
+ EXPECT_EQ("Some long long attr", truncatedError.Attributes().Get<TString>("attr2"));
+}
+
+TEST(TErrorTest, TruncateWhitelistRValue)
+{
+ auto error = TError("Some error");
+ SetErrorAttribute(&error, "attr1", "Some long long attr");
+ SetErrorAttribute(&error, "attr2", "Some long long attr");
+
+ THashSet<TStringBuf> myWhitelist = {"attr2"};
+
+ auto errorCopy = error;
+ auto truncatedError = std::move(errorCopy).Truncate(2, 10, myWhitelist);
+ EXPECT_TRUE(errorCopy.IsOK());
+
+ EXPECT_EQ(error.GetCode(), truncatedError.GetCode());
+ EXPECT_EQ(error.GetMessage(), truncatedError.GetMessage());
+
+ EXPECT_EQ("...<attribute truncated>...", truncatedError.Attributes().Get<TString>("attr1"));
+ EXPECT_EQ("Some long long attr", truncatedError.Attributes().Get<TString>("attr2"));
+}
+
+TEST(TErrorTest, TruncateWhitelistInnerErrors)
+{
+ auto innerError = TError("Inner error");
+ SetErrorAttribute(&innerError, "attr1", "Some long long attr");
+ SetErrorAttribute(&innerError, "attr2", "Some long long attr");
+
+ auto error = TError("Error") << innerError;
+
+ THashSet<TStringBuf> myWhitelist = {"attr2"};
+
+ auto truncatedError = error.Truncate(2, 15, myWhitelist);
+ EXPECT_EQ(truncatedError.InnerErrors().size(), 1u);
+
+ auto truncatedInnerError = truncatedError.InnerErrors()[0];
+ EXPECT_EQ(truncatedInnerError.GetCode(), innerError.GetCode());
+ EXPECT_EQ(truncatedInnerError.GetMessage(), innerError.GetMessage());
+ EXPECT_EQ("...<attribute truncated>...", truncatedInnerError.Attributes().Get<TString>("attr1"));
+ EXPECT_EQ("Some long long attr", truncatedInnerError.Attributes().Get<TString>("attr2"));
+}
+
+TEST(TErrorTest, TruncateWhitelistInnerErrorsRValue)
+{
+ auto innerError = TError("Inner error");
+ SetErrorAttribute(&innerError, "attr1", "Some long long attr");
+ SetErrorAttribute(&innerError, "attr2", "Some long long attr");
+
+ auto error = TError("Error") << innerError;
+
+ THashSet<TStringBuf> myWhitelist = {"attr2"};
+
+ auto errorCopy = error;
+ auto truncatedError = std::move(errorCopy).Truncate(2, 15, myWhitelist);
+ EXPECT_TRUE(errorCopy.IsOK());
+ EXPECT_EQ(truncatedError.InnerErrors().size(), 1u);
+
+ auto truncatedInnerError = truncatedError.InnerErrors()[0];
+ EXPECT_EQ(truncatedInnerError.GetCode(), innerError.GetCode());
+ EXPECT_EQ(truncatedInnerError.GetMessage(), innerError.GetMessage());
+ EXPECT_EQ("...<attribute truncated>...", truncatedInnerError.Attributes().Get<TString>("attr1"));
+ EXPECT_EQ("Some long long attr", truncatedInnerError.Attributes().Get<TString>("attr2"));
+}
+
+TEST(TErrorTest, TruncateWhitelistSaveInnerError)
+{
+ auto genericInner = TError("GenericInner");
+ auto whitelistedInner = TError("Inner")
+ << TErrorAttribute("whitelisted_key", 42);
+
+ auto error = TError("Error")
+ << (genericInner << TErrorAttribute("foo", "bar"))
+ << whitelistedInner
+ << genericInner;
+
+ error = std::move(error).Truncate(1, 20, {
+ "whitelisted_key"
+ });
+ EXPECT_TRUE(!error.IsOK());
+ EXPECT_EQ(error.InnerErrors().size(), 2u);
+ EXPECT_EQ(error.InnerErrors()[0], whitelistedInner);
+ EXPECT_EQ(error.InnerErrors()[1], genericInner);
+
+ // TODO: error_helpers???
+ EXPECT_TRUE(FindAttributeRecursive<int>(error, "whitelisted_key"));
+ EXPECT_FALSE(FindAttributeRecursive<int>(error, "foo"));
+}
+
+TEST(TErrorTest, YTExceptionToError)
+{
+ try {
+ throw TSimpleException("message");
+ } catch (const std::exception& ex) {
+ TError error(ex);
+ EXPECT_EQ(NYT::EErrorCode::Generic, error.GetCode());
+ EXPECT_EQ("message", error.GetMessage());
+ }
+}
+
+TEST(TErrorTest, CompositeYTExceptionToError)
+{
+ try {
+ try {
+ throw TSimpleException("inner message");
+ } catch (const std::exception& ex) {
+ throw TSimpleException(ex, "outer message");
+ }
+ } catch (const std::exception& ex) {
+ TError outerError(ex);
+ EXPECT_EQ(NYT::EErrorCode::Generic, outerError.GetCode());
+ EXPECT_EQ("outer message", outerError.GetMessage());
+ EXPECT_EQ(1, std::ssize(outerError.InnerErrors()));
+ const auto& innerError = outerError.InnerErrors()[0];
+ EXPECT_EQ(NYT::EErrorCode::Generic, innerError.GetCode());
+ EXPECT_EQ("inner message", innerError.GetMessage());
+ }
+}
+
+TEST(TErrorTest, YTExceptionWithAttributesToError)
+{
+ try {
+ throw TSimpleException("message")
+ << TExceptionAttribute{"Int64 value", static_cast<i64>(42)}
+ << TExceptionAttribute{"double value", 7.77}
+ << TExceptionAttribute{"bool value", false}
+ << TExceptionAttribute{"String value", "FooBar"};
+ } catch (const std::exception& ex) {
+ TError error(ex);
+ EXPECT_EQ(NYT::EErrorCode::Generic, error.GetCode());
+ EXPECT_EQ("message", error.GetMessage());
+
+ auto i64value = error.Attributes().Find<i64>("Int64 value");
+ EXPECT_TRUE(i64value);
+ EXPECT_EQ(*i64value, static_cast<i64>(42));
+
+ auto doubleValue = error.Attributes().Find<double>("double value");
+ EXPECT_TRUE(doubleValue);
+ EXPECT_EQ(*doubleValue, 7.77);
+
+ auto boolValue = error.Attributes().Find<bool>("bool value");
+ EXPECT_TRUE(boolValue);
+ EXPECT_EQ(*boolValue, false);
+
+ auto stringValue = error.Attributes().Find<TString>("String value");
+ EXPECT_TRUE(stringValue);
+ EXPECT_EQ(*stringValue, "FooBar");
+ }
+}
+
+TEST(TErrorTest, AttributeSerialization)
+{
+ auto getWeededText = [] (const TError& err) {
+ std::vector<TString> lines;
+ for (const auto& line : StringSplitter(ToString(err)).Split('\n')) {
+ if (!line.Contains("origin") && !line.Contains("datetime")) {
+ lines.push_back(TString{line});
+ }
+ }
+ return JoinSeq("\n", lines);
+ };
+
+ EXPECT_EQ(getWeededText(TError("E1") << TErrorAttribute("A1", "V1")), TString(
+ "E1\n"
+ " A1 V1\n"));
+ EXPECT_EQ(getWeededText(TError("E1") << TErrorAttribute("A1", "L1\nL2\nL3")), TString(
+ "E1\n"
+ " A1\n"
+ " L1\n"
+ " L2\n"
+ " L3\n"));
+}
+
+TEST(TErrorTest, MacroStaticAnalysis)
+{
+ auto swallow = [] (auto expr) {
+ try {
+ expr();
+ } catch (...) {
+ }
+ };
+
+ swallow([] {
+ THROW_ERROR_EXCEPTION("Foo");
+ });
+ swallow([] {
+ THROW_ERROR_EXCEPTION("Hello, %v", "World");
+ });
+ swallow([] {
+ THROW_ERROR_EXCEPTION(NYT::EErrorCode::Generic, "Foo");
+ });
+ swallow([] {
+ THROW_ERROR_EXCEPTION(NYT::EErrorCode::Generic, "Foo%v", "Bar");
+ });
+ swallow([] {
+ THROW_ERROR_EXCEPTION(NYT::EErrorCode::Generic, "Foo%v%v", "Bar", "Baz");
+ });
+ swallow([] {
+ THROW_ERROR_EXCEPTION_IF_FAILED(TError{}, "Foo");
+ });
+ swallow([] {
+ THROW_ERROR_EXCEPTION_IF_FAILED(TError{}, "Foo%v", "Bar");
+ });
+ swallow([] {
+ THROW_ERROR_EXCEPTION_IF_FAILED(TError{}, "Foo%v%v", "Bar", "Baz");
+ });
+ swallow([] {
+ THROW_ERROR_EXCEPTION_IF_FAILED(TError{}, NYT::EErrorCode::Generic, "Foo%v", "Bar");
+ });
+ swallow([] {
+ THROW_ERROR_EXCEPTION_IF_FAILED(TError{}, NYT::EErrorCode::Generic, "Foo%v%v", "Bar", "Baz");
+ });
+}
+
+TEST(TErrorTest, WrapStaticAnalysis)
+{
+ TError error;
+ Y_UNUSED(error.Wrap());
+ Y_UNUSED(error.Wrap(std::exception{}));
+ Y_UNUSED(error.Wrap("Hello"));
+ Y_UNUSED(error.Wrap("Hello, %v", "World"));
+ Y_UNUSED(error.Wrap(TRuntimeFormat{"Hello, %v"}));
+}
+
+// NB(arkady-e1ppa): Uncomment these occasionally to see
+// that static analysis is still working.
+TEST(TErrorTest, MacroStaticAnalysisBrokenFormat)
+{
+ // auto swallow = [] (auto expr) {
+ // try {
+ // expr();
+ // } catch (...) {
+ // }
+ // };
+
+ // swallow([] {
+ // THROW_ERROR_EXCEPTION("Hello, %v");
+ // });
+ // swallow([] {
+ // THROW_ERROR_EXCEPTION(TErrorCode{}, "Foo%v");
+ // });
+ // swallow([] {
+ // THROW_ERROR_EXCEPTION_IF_FAILED(TError{}, "Foo%v");
+ // });
+ // swallow([] {
+ // THROW_ERROR_EXCEPTION_IF_FAILED(TError{}, TErrorCode{}, "Foo%v");
+ // });
+}
+
+////////////////////////////////////////////////////////////////////////////////
+
+} // namespace
+} // namespace NYT
diff --git a/library/cpp/yt/error/unittests/ya.make b/library/cpp/yt/error/unittests/ya.make
new file mode 100644
index 00000000000..9082a25535c
--- /dev/null
+++ b/library/cpp/yt/error/unittests/ya.make
@@ -0,0 +1,18 @@
+GTEST()
+
+INCLUDE(${ARCADIA_ROOT}/library/cpp/yt/ya_cpp.make.inc)
+
+SIZE(MEDIUM)
+
+SRCS(
+ error_ut.cpp
+ error_code_ut.cpp
+)
+
+PEERDIR(
+ library/cpp/yt/error
+
+ library/cpp/testing/gtest
+)
+
+END()
diff --git a/library/cpp/yt/error/ya.make b/library/cpp/yt/error/ya.make
index 60d3e9a92fb..ca4370ef9da 100644
--- a/library/cpp/yt/error/ya.make
+++ b/library/cpp/yt/error/ya.make
@@ -10,11 +10,20 @@ PEERDIR(
library/cpp/yt/threading
library/cpp/yt/string
library/cpp/yt/yson_string # TODO(arkady-e1ppa): eliminate
+ library/cpp/yt/logging # TODO(arkady-e1ppa): Consider logging error_code crashes to stderr and drop this dep.
+
+ util
)
SRCS(
+ error.cpp
error_attributes.cpp
+ error_code.cpp
origin_attributes.cpp
)
END()
+
+RECURSE_FOR_TESTS(
+ unittests
+)
diff --git a/library/cpp/yt/string/string.cpp b/library/cpp/yt/string/string.cpp
index c4bd98b66c0..3cf5651e230 100644
--- a/library/cpp/yt/string/string.cpp
+++ b/library/cpp/yt/string/string.cpp
@@ -359,4 +359,19 @@ TStringBuf FormatBool(bool value)
////////////////////////////////////////////////////////////////////////////////
+void TruncateStringInplace(TString* string, int lengthLimit, TStringBuf truncatedSuffix)
+{
+ if (std::ssize(*string) > lengthLimit) {
+ *string = Format("%v%v", string->substr(0, lengthLimit), truncatedSuffix);
+ }
+}
+
+TString TruncateString(TString string, int lengthLimit, TStringBuf truncatedSuffix)
+{
+ TruncateStringInplace(&string, lengthLimit, truncatedSuffix);
+ return string;
+}
+
+////////////////////////////////////////////////////////////////////////////////
+
} // namespace NYT
diff --git a/library/cpp/yt/string/string.h b/library/cpp/yt/string/string.h
index 9794cfd69f0..ac5aa5d4e4b 100644
--- a/library/cpp/yt/string/string.h
+++ b/library/cpp/yt/string/string.h
@@ -181,6 +181,14 @@ TStringBuf FormatBool(bool value);
////////////////////////////////////////////////////////////////////////////////
+inline constexpr TStringBuf DefaultTruncatedMessage = "...<truncated>";
+
+void TruncateStringInplace(TString* string, int lengthLimit, TStringBuf truncatedSuffix = DefaultTruncatedMessage);
+
+TString TruncateString(TString string, int lengthLimit, TStringBuf truncatedSuffix = DefaultTruncatedMessage);
+
+////////////////////////////////////////////////////////////////////////////////
+
} // namespace NYT
#define STRING_INL_H_
diff --git a/library/cpp/yt/system/proc.h b/library/cpp/yt/system/proc.h
new file mode 100644
index 00000000000..b1b65608e3f
--- /dev/null
+++ b/library/cpp/yt/system/proc.h
@@ -0,0 +1,32 @@
+#pragma once
+
+#include <library/cpp/yt/misc/enum.h>
+
+#include <errno.h>
+
+namespace NYT {
+
+////////////////////////////////////////////////////////////////////////////////
+
+//! NYT::TError::FromSystem adds this value to a system errno. The enum
+//! below lists several errno's that are used in our code.
+constexpr int LinuxErrorCodeBase = 4200;
+constexpr int LinuxErrorCodeCount = 2000;
+
+DEFINE_ENUM(ELinuxErrorCode,
+ ((NOENT) ((LinuxErrorCodeBase + ENOENT)))
+ ((IO) ((LinuxErrorCodeBase + EIO)))
+ ((ACCESS) ((LinuxErrorCodeBase + EACCES)))
+ ((NFILE) ((LinuxErrorCodeBase + ENFILE)))
+ ((MFILE) ((LinuxErrorCodeBase + EMFILE)))
+ ((NOSPC) ((LinuxErrorCodeBase + ENOSPC)))
+ ((PIPE) ((LinuxErrorCodeBase + EPIPE)))
+ ((CONNRESET) ((LinuxErrorCodeBase + ECONNRESET)))
+ ((TIMEDOUT) ((LinuxErrorCodeBase + ETIMEDOUT)))
+ ((CONNREFUSED) ((LinuxErrorCodeBase + ECONNREFUSED)))
+ ((DQUOT) ((LinuxErrorCodeBase + EDQUOT)))
+);
+
+////////////////////////////////////////////////////////////////////////////////
+
+} // namespace NYT
diff --git a/yt/yt/client/api/operation_client.cpp b/yt/yt/client/api/operation_client.cpp
index 07253e5ee52..fccdbbf60ab 100644
--- a/yt/yt/client/api/operation_client.cpp
+++ b/yt/yt/client/api/operation_client.cpp
@@ -267,15 +267,11 @@ static std::optional<NScheduler::EAbortReason> TryGetJobAbortReasonFromError(con
return std::nullopt;
}
- if (auto yson = error.Attributes().FindYson("abort_reason")) {
- try {
- return ConvertTo<NScheduler::EAbortReason>(yson);
- } catch (const std::exception& exception) {
- return std::nullopt;
- }
+ try {
+ return error.Attributes().Find<NScheduler::EAbortReason>("abort_reason");
+ } catch (const std::exception&) {
+ return std::nullopt;
}
-
- return std::nullopt;
}
void Serialize(const TJob& job, NYson::IYsonConsumer* consumer, TStringBuf idKey)
diff --git a/yt/yt/client/tablet_client/table_mount_cache_detail.cpp b/yt/yt/client/tablet_client/table_mount_cache_detail.cpp
index ae1d41c975f..59cf0de4d83 100644
--- a/yt/yt/client/tablet_client/table_mount_cache_detail.cpp
+++ b/yt/yt/client/tablet_client/table_mount_cache_detail.cpp
@@ -256,8 +256,8 @@ auto TTableMountCacheBase::TryHandleServantNotActiveError(const TError& error)
return {};
}
- if (auto siblingCellDescriptor = attributes.FindYson("sibling_servant_cell_descriptor")) {
- RegisterCell(ConvertToNode(siblingCellDescriptor));
+ if (auto siblingCellDescriptor = attributes.Find<INodePtr>("sibling_servant_cell_descriptor")) {
+ RegisterCell(std::move(siblingCellDescriptor));
} else {
return {};
}
diff --git a/yt/yt/core/actions/unittests/future_ut.cpp b/yt/yt/core/actions/unittests/future_ut.cpp
index 68e3c554a4d..8410a08c858 100644
--- a/yt/yt/core/actions/unittests/future_ut.cpp
+++ b/yt/yt/core/actions/unittests/future_ut.cpp
@@ -1474,7 +1474,7 @@ TEST_F(TFutureTest, WithDeadlineFail)
auto deadline = TInstant::Now() + SleepQuantum;
auto f2 = f1.WithDeadline(deadline);
EXPECT_EQ(NYT::EErrorCode::Timeout, f2.Get().GetCode());
- EXPECT_EQ(NYson::ConvertToYsonString(deadline), f2.Get().Attributes().FindYson("deadline"));
+ EXPECT_EQ(deadline, f2.Get().Attributes().Get<TInstant>("deadline"));
}
TEST_F(TFutureTest, WithTimeoutSuccess)
@@ -1503,7 +1503,7 @@ TEST_F(TFutureTest, WithTimeoutFail)
auto f1 = p.ToFuture();
auto f2 = f1.WithTimeout(SleepQuantum);
EXPECT_EQ(NYT::EErrorCode::Timeout, f2.Get().GetCode());
- EXPECT_EQ(NYson::ConvertToYsonString(SleepQuantum), f2.Get().Attributes().FindYson("timeout"));
+ EXPECT_EQ(SleepQuantum, f2.Get().Attributes().Get<TDuration>("timeout"));
}
TEST_W(TFutureTest, Holder)
diff --git a/yt/yt/core/misc/error-inl.h b/yt/yt/core/misc/error-inl.h
index 3185de11b2e..f215fcd9072 100644
--- a/yt/yt/core/misc/error-inl.h
+++ b/yt/yt/core/misc/error-inl.h
@@ -4,21 +4,17 @@
#include "error.h"
#endif
-namespace NYT::NToAttributeValueImpl {
+namespace NYT::NAttributeValueConversionImpl {
////////////////////////////////////////////////////////////////////////////////
template <class T>
-NYson::TYsonString TagInvoke(TTagInvokeTag<ToAttributeValue>, const T& value)
+ requires (!CPrimitiveConvertible<T>)
+NYson::TYsonString TagInvoke(TTagInvokeTag<ToErrorAttributeValue>, const T& value)
{
- return NYson::ConvertToYsonString(value);
-}
-
-inline NYson::TYsonString TagInvoke(TTagInvokeTag<ToAttributeValue>, const NYson::TYsonString& value)
-{
- return value;
+ return NYson::ConvertToYsonString(value, NYson::EYsonFormat::Text);
}
////////////////////////////////////////////////////////////////////////////////
-} // namespace NYT::NToAttributeValueImpl
+} // namespace NYT::NAttributeValueConversionImpl
diff --git a/yt/yt/core/misc/error.cpp b/yt/yt/core/misc/error.cpp
index 3aed00942b1..18394636d5e 100644
--- a/yt/yt/core/misc/error.cpp
+++ b/yt/yt/core/misc/error.cpp
@@ -1,6 +1,5 @@
#include "error.h"
#include "serialize.h"
-#include "origin_attributes.h"
#include <yt/yt/core/concurrency/public.h>
@@ -169,7 +168,7 @@ TString FormatOriginOverride(const TOriginAttributes& attributes)
GetFid(attributes));
}
-TOriginAttributes ExtractFromDictionaryOverride(const NYTree::IAttributeDictionaryPtr& attributes)
+TOriginAttributes ExtractFromDictionaryOverride(TErrorAttributes* attributes)
{
auto result = NYT::NDetail::ExtractFromDictionaryDefault(attributes);
@@ -627,7 +626,6 @@ void TErrorSerializer::Load(TStreamLoadContext& context, TError& error)
auto code = Load<TErrorCode>(context);
auto message = Load<TString>(context);
- IAttributeDictionaryPtr attributes;
if (Load<bool>(context)) {
size_t size = TSizeSerializer::Load(context);
for (size_t index = 0; index < size; ++index) {
diff --git a/yt/yt/core/misc/error.h b/yt/yt/core/misc/error.h
index 8399b49301d..fd47419ee9b 100644
--- a/yt/yt/core/misc/error.h
+++ b/yt/yt/core/misc/error.h
@@ -1,7 +1,6 @@
#pragma once
#include "public.h"
-#include "stripped_error.h"
#include <yt/yt/core/yson/public.h>
@@ -11,6 +10,7 @@
#include <yt/yt/core/concurrency/public.h>
+#include <library/cpp/yt/error/error.h>
#include <library/cpp/yt/error/origin_attributes.h>
namespace NYT {
diff --git a/yt/yt/core/misc/error_code.h b/yt/yt/core/misc/error_code.h
index 1c2c08fbb4a..bbc1ecb433d 100644
--- a/yt/yt/core/misc/error_code.h
+++ b/yt/yt/core/misc/error_code.h
@@ -1,101 +1 @@
-#pragma once
-
-#include <library/cpp/yt/misc/enum.h>
-#include <library/cpp/yt/misc/port.h>
-#include <library/cpp/yt/misc/static_initializer.h>
-
-#include <library/cpp/yt/string/format.h>
-
-#include <util/generic/hash_set.h>
-
-namespace NYT {
-
-////////////////////////////////////////////////////////////////////////////////
-
-class TErrorCodeRegistry
-{
-public:
- static TErrorCodeRegistry* Get();
-
- struct TErrorCodeInfo
- {
- TString Namespace;
- //! Human-readable error code name.
- TString Name;
-
- bool operator==(const TErrorCodeInfo& rhs) const;
- };
-
- struct TErrorCodeRangeInfo
- {
- int From;
- int To;
- TString Namespace;
- std::function<TString(int code)> Formatter;
-
- TErrorCodeInfo Get(int code) const;
- bool Intersects(const TErrorCodeRangeInfo& other) const;
- bool Contains(int value) const;
- };
-
- //! Retrieves info from registered codes and code ranges.
- TErrorCodeInfo Get(int code) const;
-
- //! Retrieves information about registered codes.
- THashMap<int, TErrorCodeInfo> GetAllErrorCodes() const;
-
- //! Retrieves information about registered code ranges.
- std::vector<TErrorCodeRangeInfo> GetAllErrorCodeRanges() const;
-
- //! Registers a single error code.
- void RegisterErrorCode(int code, const TErrorCodeInfo& errorCodeInfo);
-
- //! Registers a range of error codes given a human-readable code to name formatter.
- void RegisterErrorCodeRange(int from, int to, TString namespaceName, std::function<TString(int code)> formatter);
-
- static TString ParseNamespace(const std::type_info& errorCodeEnumTypeInfo);
-
-private:
- THashMap<int, TErrorCodeInfo> CodeToInfo_;
- std::vector<TErrorCodeRangeInfo> ErrorCodeRanges_;
-
- void CheckCodesAgainstRanges() const;
-};
-
-void FormatValue(
- TStringBuilderBase* builder,
- const TErrorCodeRegistry::TErrorCodeInfo& errorCodeInfo,
- TStringBuf spec);
-
-void FormatValue(
- TStringBuilderBase* builder,
- const TErrorCodeRegistry::TErrorCodeRangeInfo& errorCodeInfo,
- TStringBuf spec);
-
-////////////////////////////////////////////////////////////////////////////////
-
-#define YT_DEFINE_ERROR_ENUM(seq) \
- DEFINE_ENUM(EErrorCode, seq); \
- YT_ATTRIBUTE_USED inline const void* ErrorEnum_EErrorCode = [] { \
- for (auto errorCode : ::NYT::TEnumTraits<EErrorCode>::GetDomainValues()) { \
- ::NYT::TErrorCodeRegistry::Get()->RegisterErrorCode( \
- static_cast<int>(errorCode), \
- {::NYT::TErrorCodeRegistry::ParseNamespace(typeid(EErrorCode)), ToString(errorCode)}); \
- } \
- return nullptr; \
- } ()
-
-////////////////////////////////////////////////////////////////////////////////
-
-//! NB: This macro should only by used in cpp files.
-#define YT_DEFINE_ERROR_CODE_RANGE(from, to, namespaceName, formatter) \
- YT_STATIC_INITIALIZER( \
- ::NYT::TErrorCodeRegistry::Get()->RegisterErrorCodeRange( \
- from, \
- to, \
- namespaceName, \
- formatter));
-
-////////////////////////////////////////////////////////////////////////////////
-
-} // namespace NYT
+#include <library/cpp/yt/error/error_code.h>
diff --git a/yt/yt/core/misc/origin_attributes.cpp b/yt/yt/core/misc/origin_attributes.cpp
deleted file mode 100644
index 332d1f58b22..00000000000
--- a/yt/yt/core/misc/origin_attributes.cpp
+++ /dev/null
@@ -1,50 +0,0 @@
-#include "origin_attributes.h"
-
-#include <yt/yt/core/ytree/attributes.h>
-
-namespace NYT::NDetail {
-
-////////////////////////////////////////////////////////////////////////////////
-
-TOriginAttributes ExtractFromDictionary(const NYTree::IAttributeDictionaryPtr& attributes)
-{
- using TFunctor = TOriginAttributes(*)(const NYTree::IAttributeDictionaryPtr&);
-
- if (auto strong = NGlobal::GetErasedVariable(ExtractFromDictionaryTag)) {
- return strong->AsConcrete<TFunctor>()(attributes);
- }
-
- return ExtractFromDictionaryDefault(attributes);
-}
-
-////////////////////////////////////////////////////////////////////////////////
-
-TOriginAttributes ExtractFromDictionaryDefault(const NYTree::IAttributeDictionaryPtr& attributes)
-{
- TOriginAttributes result;
- if (!attributes) {
- return result;
- }
-
- static const TString HostKey("host");
- result.HostHolder = TSharedRef::FromString(attributes->GetAndRemove<TString>(HostKey, TString()));
- result.Host = result.HostHolder.empty() ? TStringBuf() : TStringBuf(result.HostHolder.Begin(), result.HostHolder.End());
-
- static const TString DatetimeKey("datetime");
- result.Datetime = attributes->GetAndRemove<TInstant>(DatetimeKey, TInstant());
-
- static const TString PidKey("pid");
- result.Pid = attributes->GetAndRemove<TProcessId>(PidKey, 0);
-
- static const TString TidKey("tid");
- result.Tid = attributes->GetAndRemove<NThreading::TThreadId>(TidKey, NThreading::InvalidThreadId);
-
- static const TString ThreadNameKey("thread");
- result.ThreadName = attributes->GetAndRemove<TString>(ThreadNameKey, TString());
-
- return result;
-}
-
-////////////////////////////////////////////////////////////////////////////////
-
-} // namespace NYT::NDetail
diff --git a/yt/yt/core/misc/origin_attributes.h b/yt/yt/core/misc/origin_attributes.h
deleted file mode 100644
index 5848431fc86..00000000000
--- a/yt/yt/core/misc/origin_attributes.h
+++ /dev/null
@@ -1,23 +0,0 @@
-#pragma once
-
-#include <yt/yt/core/ytree/public.h>
-
-#include <library/cpp/yt/error/origin_attributes.h>
-
-// TODO(arkady-e1ppa): Move this to library/cpp/yt/error when deps on ytree are gone.
-
-namespace NYT::NDetail {
-
-////////////////////////////////////////////////////////////////////////////////
-
-// Weak symbol.
-TOriginAttributes ExtractFromDictionary(const NYTree::IAttributeDictionaryPtr& attributes);
-
-////////////////////////////////////////////////////////////////////////////////
-
-// Default impl of weak symbol.
-TOriginAttributes ExtractFromDictionaryDefault(const NYTree::IAttributeDictionaryPtr& attributes);
-
-////////////////////////////////////////////////////////////////////////////////
-
-} // namespace NYT::NDetail
diff --git a/yt/yt/core/misc/proc.h b/yt/yt/core/misc/proc.h
index 86d8ad42232..76ef3e18403 100644
--- a/yt/yt/core/misc/proc.h
+++ b/yt/yt/core/misc/proc.h
@@ -2,37 +2,16 @@
#include "common.h"
-#include <yt/yt/core/misc/stripped_error.h>
+#include <library/cpp/yt/error/error.h>
-#include <util/system/file.h>
+#include <library/cpp/yt/system/proc.h>
-#include <errno.h>
+#include <util/system/file.h>
namespace NYT {
////////////////////////////////////////////////////////////////////////////////
-//! NYT::TError::FromSystem adds this value to a system errno. The enum
-//! below lists several errno's that are used in our code.
-constexpr int LinuxErrorCodeBase = 4200;
-constexpr int LinuxErrorCodeCount = 2000;
-
-DEFINE_ENUM(ELinuxErrorCode,
- ((NOENT) ((LinuxErrorCodeBase + ENOENT)))
- ((IO) ((LinuxErrorCodeBase + EIO)))
- ((ACCESS) ((LinuxErrorCodeBase + EACCES)))
- ((NFILE) ((LinuxErrorCodeBase + ENFILE)))
- ((MFILE) ((LinuxErrorCodeBase + EMFILE)))
- ((NOSPC) ((LinuxErrorCodeBase + ENOSPC)))
- ((PIPE) ((LinuxErrorCodeBase + EPIPE)))
- ((CONNRESET) ((LinuxErrorCodeBase + ECONNRESET)))
- ((TIMEDOUT) ((LinuxErrorCodeBase + ETIMEDOUT)))
- ((CONNREFUSED) ((LinuxErrorCodeBase + ECONNREFUSED)))
- ((DQUOT) ((LinuxErrorCodeBase + EDQUOT)))
-);
-
-////////////////////////////////////////////////////////////////////////////////
-
bool IsSystemErrorCode(TErrorCode errorCode);
bool IsSystemError(const TError& error);
diff --git a/yt/yt/core/misc/process_exit_profiler.h b/yt/yt/core/misc/process_exit_profiler.h
index 0306e44ed22..08cc1aa22d1 100644
--- a/yt/yt/core/misc/process_exit_profiler.h
+++ b/yt/yt/core/misc/process_exit_profiler.h
@@ -1,10 +1,11 @@
#pragma once
#include "error.h"
-#include "error_helpers.h"
#include <yt/yt/library/profiling/sensor.h>
+#include <library/cpp/yt/error/error_helpers.h>
+
#ifdef _unix_
#include <string.h>
#endif
diff --git a/yt/yt/core/misc/public.h b/yt/yt/core/misc/public.h
index 2ce8dd5a2d0..5fb6839f12d 100644
--- a/yt/yt/core/misc/public.h
+++ b/yt/yt/core/misc/public.h
@@ -1,7 +1,8 @@
#pragma once
#include "common.h"
-#include "error_code.h"
+
+#include <library/cpp/yt/error/public.h>
// Google Protobuf forward declarations.
namespace google::protobuf {
@@ -163,15 +164,6 @@ DECLARE_REFCOUNTED_STRUCT(IHedgingManager)
////////////////////////////////////////////////////////////////////////////////
-YT_DEFINE_ERROR_ENUM(
- ((OK) (0))
- ((Generic) (1))
- ((Canceled) (2))
- ((Timeout) (3))
- ((FutureCombinerFailure) (4))
- ((FutureCombinerShortcut)(5))
-);
-
DEFINE_ENUM(EProcessErrorCode,
((NonZeroExitCode) (10000))
((Signal) (10001))
diff --git a/yt/yt/core/misc/string_helpers.cpp b/yt/yt/core/misc/string_helpers.cpp
deleted file mode 100644
index 3a75b91d3a1..00000000000
--- a/yt/yt/core/misc/string_helpers.cpp
+++ /dev/null
@@ -1,26 +0,0 @@
-#include "string_helpers.h"
-
-#include <library/cpp/yt/string/format.h>
-
-namespace NYT {
-
-////////////////////////////////////////////////////////////////////////////////
-
-const TStringBuf DefaultTruncatedMessage("...<truncated>");
-
-void TruncateStringInplace(TString* string, int lengthLimit, TStringBuf truncatedSuffix)
-{
- if (std::ssize(*string) > lengthLimit) {
- *string = Format("%v%v", string->substr(0, lengthLimit), truncatedSuffix);
- }
-}
-
-TString TruncateString(TString string, int lengthLimit, TStringBuf truncatedSuffix)
-{
- TruncateStringInplace(&string, lengthLimit, truncatedSuffix);
- return string;
-}
-
-////////////////////////////////////////////////////////////////////////////////
-
-} // namespace NYT
diff --git a/yt/yt/core/misc/string_helpers.h b/yt/yt/core/misc/string_helpers.h
deleted file mode 100644
index c52b03009c2..00000000000
--- a/yt/yt/core/misc/string_helpers.h
+++ /dev/null
@@ -1,17 +0,0 @@
-#pragma once
-
-#include "public.h"
-
-namespace NYT {
-
-////////////////////////////////////////////////////////////////////////////////
-
-extern const TStringBuf DefaultTruncatedMessage;
-
-void TruncateStringInplace(TString* string, int lengthLimit, TStringBuf truncatedSuffix = DefaultTruncatedMessage);
-
-TString TruncateString(TString string, int lengthLimit, TStringBuf truncatedSuffix = DefaultTruncatedMessage);
-
-////////////////////////////////////////////////////////////////////////////////
-
-} // namespace NYT
diff --git a/yt/yt/core/misc/unittests/error_ut.cpp b/yt/yt/core/misc/unittests/error_ut.cpp
index 18c78a9cca3..de4e4fba123 100644
--- a/yt/yt/core/misc/unittests/error_ut.cpp
+++ b/yt/yt/core/misc/unittests/error_ut.cpp
@@ -4,7 +4,6 @@
#include <yt/yt/core/concurrency/scheduler.h>
#include <yt/yt/core/misc/error.h>
-#include <yt/yt/core/misc/error_helpers.h>
#include <yt/yt/core/net/local_address.h>
@@ -27,377 +26,6 @@ using namespace NYTree;
////////////////////////////////////////////////////////////////////////////////
-class TAdlException
- : public std::exception
-{
-public:
- static int ResetCallCount()
- {
- return std::exchange(OverloadCallCount, 0);
- }
-
- const char* what() const noexcept override
- {
- return "Adl exception";
- }
-
- // Simulate overload from TAdlException::operator <<
- template <class TLikeThis, class TArg>
- requires std::derived_from<std::decay_t<TLikeThis>, TAdlException>
- friend TLikeThis&& operator << (TLikeThis&& ex, const TArg& /*other*/)
- {
- ++OverloadCallCount;
- return std::forward<TLikeThis>(ex);
- }
-
-private:
- static inline int OverloadCallCount = 0;
-};
-
-class TAdlArgument
-{
-public:
- static int ResetCallCount()
- {
- return std::exchange(OverloadCallCount, 0);
- }
-
- // Simulate overload TAdlArgument::operator <<
- friend TError operator << (TError&& error, const TAdlArgument& /*other*/)
- {
- static const TErrorAttribute Attr("attr", "attr_value");
- ++OverloadCallCount;
- return std::move(error) << Attr;
- }
-
- friend TError operator << (const TError& error, const TAdlArgument& /*other*/)
- {
- static const TErrorAttribute Attr("attr", "attr_value");
- ++OverloadCallCount;
- return error << Attr;
- }
-
-private:
- static inline int OverloadCallCount = 0;
-};
-
-class TWidget
-{
-public:
- TWidget()
- {
- DefaultConstructorCalls++;
- };
-
- TWidget(const TWidget&)
- {
- CopyConstructorCalls++;
- }
- TWidget& operator = (const TWidget&) = delete;
-
- TWidget(TWidget&&)
- {
- MoveConstructorCalls++;
- }
- TWidget& operator = (TWidget&&) = delete;
-
- static int ResetDefaultCount()
- {
- return std::exchange(DefaultConstructorCalls, 0);
- }
-
- static int ResetCopyCount()
- {
- return std::exchange(CopyConstructorCalls, 0);
- }
-
- static int ResetMoveCount()
- {
- return std::exchange(MoveConstructorCalls, 0);
- }
-
-private:
- static inline int DefaultConstructorCalls = 0;
- static inline int CopyConstructorCalls = 0;
- static inline int MoveConstructorCalls = 0;
-};
-
-////////////////////////////////////////////////////////////////////////////////
-
-template <class TOverloadTest, bool LeftOperandHasUserDefinedOverload = false>
-void IterateTestOverEveryRightOperand(TOverloadTest& tester)
-{
- {
- TErrorAttribute attribute("attr", "attr_value");
- const auto& attributeRef = attribute;
- tester(attributeRef);
- }
-
- {
- std::vector<TErrorAttribute> attributeVector{{"attr1", "attr_value"}, {"attr2", "attr_value"}};
- const auto& attributeVectorRef = attributeVector;
- tester(attributeVectorRef);
- }
-
- {
- TError error("Error");
-
- const auto& errorRef = error;
- tester(errorRef);
-
- auto errorCopy = error;
- tester(std::move(errorCopy));
-
- if constexpr (!LeftOperandHasUserDefinedOverload) {
- EXPECT_TRUE(errorCopy.IsOK());
- }
- }
-
- {
- std::vector<TError> vectorError{TError("Error"), TError("Error")};
-
- const auto& vectorErrorRef = vectorError;
- tester(vectorErrorRef);
-
- auto vectorErrorCopy = vectorError;
- tester(std::move(vectorErrorCopy));
-
- if constexpr (!LeftOperandHasUserDefinedOverload) {
- for (const auto& errorCopy : vectorErrorCopy) {
- EXPECT_TRUE(errorCopy.IsOK());
- }
- }
- }
-
- {
- TError error("Error");
-
- const auto& attributeDictionaryRef = error.Attributes();
- tester(attributeDictionaryRef);
- }
-
- {
- try {
- THROW_ERROR TError("Test error");
- } catch(const NYT::TErrorException& ex) {
- const auto& exRef = ex;
- tester(exRef);
-
- auto exCopy = ex;
- tester(std::move(exCopy));
- }
- }
-
- {
- TErrorOr<int> err(std::exception{});
-
- const auto& errRef = err;
- tester(errRef);
-
- auto errCopy = err;
- tester(std::move(errCopy));
-
- if constexpr (!LeftOperandHasUserDefinedOverload) {
- EXPECT_TRUE(errCopy.IsOK());
- }
- }
-
- {
- TAdlArgument adlArg;
-
- const TAdlArgument& adlArgRef = adlArg;
- tester(adlArgRef);
-
- if constexpr (!LeftOperandHasUserDefinedOverload) {
- EXPECT_EQ(TAdlArgument::ResetCallCount(), 1);
- }
- }
-}
-
-template <class T>
-void SetErrorAttribute(TError* error, TString key, const T& value)
-{
- error->MutableAttributes()->SetYson(key, ConvertToYsonString(value));
-}
-
-////////////////////////////////////////////////////////////////////////////////
-
-TEST(TErrorTest, BitshiftOverloadsExplicitLeftOperand)
-{
- // TError&& overload.
- auto moveTester = [] (auto&& arg) {
- TError error = TError("Test error");
- TError moved = std::move(error) << std::forward<decltype(arg)>(arg);
- EXPECT_TRUE(error.IsOK());
- EXPECT_EQ(moved.GetMessage(), "Test error");
- };
- IterateTestOverEveryRightOperand(moveTester);
-
- // const TError& overloads.
- auto copyTester = [] (auto&& arg) {
- TError error = TError("Test error");
- TError copy = error << std::forward<decltype(arg)>(arg);
- EXPECT_EQ(error.GetMessage(), copy.GetMessage());
- };
- IterateTestOverEveryRightOperand(copyTester);
-
- // Test that TError pr value binds correctly and the call itself is unambiguous.
- auto prvalueTester = [] (auto&& arg) {
- TError error = TError("Test error") << std::forward<decltype(arg)>(arg);
- EXPECT_EQ(error.GetMessage(), "Test error");
- };
- IterateTestOverEveryRightOperand(prvalueTester);
-}
-
-TEST(TErrorTest, BitshiftOverloadsImplicitLeftOperand)
-{
- // We want to be able to write THROW_ERROR ex
- auto throwErrorTester1 = [] (auto&& arg) {
- try {
- try {
- THROW_ERROR TError("Test error");
- } catch(const NYT::TErrorException& ex) {
- THROW_ERROR ex << std::forward<decltype(arg)>(arg);
- }
- } catch(const NYT::TErrorException& ex) {
- TError error = ex;
- EXPECT_EQ(error.GetMessage(), "Test error");
- }
- };
- IterateTestOverEveryRightOperand(throwErrorTester1);
-
- // We also want to be able to write THROW_ERROR TError(smth) without compiler errors
- auto throwErrorTester2 = [] (auto&& arg) {
- try {
- try {
- THROW_ERROR TError("Test error");
- } catch(const NYT::TErrorException& ex) {
- THROW_ERROR TError(ex) << std::forward<decltype(arg)>(arg);
- }
- } catch(const NYT::TErrorException& ex) {
- TError error = ex;
- EXPECT_EQ(error.GetMessage(), "Test error");
- }
- };
- IterateTestOverEveryRightOperand(throwErrorTester2);
-
- // Left operand ADL finds the user-defined overload over NYT one.
- // In this case AdlException should find templated function
- // specialization with perfect match for args over conversions.
- auto adlResolutionTester = [] (auto&& arg) {
- TAdlException ex;
- auto result = ex << std::forward<decltype(arg)>(arg);
- static_assert(std::same_as<TAdlException, std::decay_t<decltype(result)>>);
- EXPECT_EQ(TAdlException::ResetCallCount(), 1);
- };
- IterateTestOverEveryRightOperand<
- decltype(adlResolutionTester),
- /*LeftOperandHasUserDefinedOverload*/ true>(adlResolutionTester);
-
- // Make sure no ambiguous calls.
- auto genericErrorOrTester = [] (auto&& arg) {
- TErrorOr<int> err(std::exception{});
- TError error = err << std::forward<decltype(arg)>(arg);
- EXPECT_EQ(error.GetCode(), NYT::EErrorCode::Generic);
- };
- IterateTestOverEveryRightOperand(genericErrorOrTester);
-}
-
-TEST(TErrorTest, Wrap)
-{
- TError error("Error");
-
- auto wrapped = error.Wrap("Wrapped error");
- EXPECT_EQ(wrapped.GetCode(), NYT::EErrorCode::Generic);
- EXPECT_EQ(wrapped.GetMessage(), "Wrapped error");
- EXPECT_EQ(wrapped.InnerErrors().size(), 1u);
- EXPECT_EQ(wrapped.InnerErrors()[0], error);
-
- auto triviallyWrapped = error.Wrap();
- EXPECT_EQ(triviallyWrapped, error);
-}
-
-TEST(TErrorTest, WrapRValue)
-{
- TError error("Error");
-
- TError errorCopy = error;
- auto wrapped = std::move(errorCopy).Wrap("Wrapped error");
- EXPECT_TRUE(errorCopy.IsOK());
- EXPECT_EQ(wrapped.GetCode(), NYT::EErrorCode::Generic);
- EXPECT_EQ(wrapped.GetMessage(), "Wrapped error");
- EXPECT_EQ(wrapped.InnerErrors().size(), 1u);
- EXPECT_EQ(wrapped.InnerErrors()[0], error);
-
- TError anotherErrorCopy = error;
- auto trviallyWrapped = std::move(anotherErrorCopy).Wrap();
- EXPECT_TRUE(anotherErrorCopy.IsOK());
- EXPECT_EQ(trviallyWrapped, error);
-}
-
-TEST(TErrorTest, ThrowErrorExceptionIfFailedMacroJustWorks)
-{
- TError error;
-
- EXPECT_NO_THROW(THROW_ERROR_EXCEPTION_IF_FAILED(error, "Outer error"));
-
- error = TError("Real error");
-
- TError errorCopy = error;
-
- try {
- THROW_ERROR_EXCEPTION_IF_FAILED(errorCopy, "Outer error");
- } catch (const std::exception& ex) {
- TError outerError(ex);
-
- EXPECT_TRUE(errorCopy.IsOK());
- EXPECT_EQ(outerError.GetMessage(), "Outer error");
- EXPECT_EQ(outerError.InnerErrors().size(), 1u);
- EXPECT_EQ(outerError.InnerErrors()[0], error);
- }
-}
-
-TEST(TErrorTest, ThrowErrorExceptionIfFailedMacroExpression)
-{
- try {
- THROW_ERROR_EXCEPTION_IF_FAILED(
- TError("Inner error")
- << TErrorAttribute("attr", "attr_value"),
- "Outer error");
- } catch (const std::exception& ex) {
- TError outerError(ex);
-
- EXPECT_EQ(outerError.GetMessage(), "Outer error");
- EXPECT_EQ(outerError.InnerErrors().size(), 1u);
- EXPECT_EQ(outerError.InnerErrors()[0].GetMessage(), "Inner error");
- EXPECT_EQ(outerError.InnerErrors()[0].Attributes().Get<TString>("attr"), "attr_value");
- }
-}
-
-TEST(TErrorTest, ThrowErrorExceptionIfFailedMacroDontStealValue)
-{
- TErrorOr<TWidget> widget = TWidget();
- EXPECT_TRUE(widget.IsOK());
- EXPECT_EQ(TWidget::ResetDefaultCount(), 1);
- EXPECT_EQ(TWidget::ResetCopyCount(), 0);
- EXPECT_EQ(TWidget::ResetMoveCount(), 1);
-
- EXPECT_NO_THROW(THROW_ERROR_EXCEPTION_IF_FAILED(widget));
- EXPECT_TRUE(widget.IsOK());
- EXPECT_NO_THROW(widget.ValueOrThrow());
- EXPECT_EQ(TWidget::ResetDefaultCount(), 0);
- EXPECT_EQ(TWidget::ResetCopyCount(), 0);
- EXPECT_EQ(TWidget::ResetMoveCount(), 0);
-}
-
-TEST(TErrorTest, ThrowErrorExceptionIfFailedMacroDontDupeCalls)
-{
- EXPECT_NO_THROW(THROW_ERROR_EXCEPTION_IF_FAILED(TErrorOr<TWidget>(TWidget())));
- EXPECT_EQ(TWidget::ResetDefaultCount(), 1);
- EXPECT_EQ(TWidget::ResetCopyCount(), 0);
- EXPECT_EQ(TWidget::ResetMoveCount(), 1);
-}
-
TEST(TErrorTest, SerializationDepthLimit)
{
constexpr int Depth = 1000;
@@ -479,296 +107,6 @@ TEST(TErrorTest, DoNotDuplicateOriginalErrorDepth)
}
}
-TEST(TErrorTest, ErrorSkeletonStubImplementation)
-{
- TError error("foo");
- EXPECT_THROW(error.GetSkeleton(), std::exception);
-}
-
-TEST(TErrorTest, FormatCtor)
-{
- // EXPECT_EQ("Some error %v", TError("Some error %v").GetMessage()); // No longer compiles due to static analysis.
- EXPECT_EQ("Some error hello", TError("Some error %v", "hello").GetMessage());
-}
-
-TEST(TErrorTest, FindRecursive)
-{
- auto inner = TError("Inner")
- << TErrorAttribute("inner_attr", 42);
- auto error = TError("Error")
- << inner
- << TErrorAttribute("attr", 8);
-
- auto attr = FindAttribute<int>(error, "attr");
- EXPECT_TRUE(attr);
- EXPECT_EQ(*attr, 8);
-
- EXPECT_FALSE(FindAttribute<int>(error, "inner_attr"));
-
- auto innerAttr = FindAttributeRecursive<int>(error, "inner_attr");
- EXPECT_TRUE(innerAttr);
- EXPECT_EQ(*innerAttr, 42);
-}
-
-TEST(TErrorTest, TruncateSimple)
-{
- auto error = TError("Some error")
- << TErrorAttribute("my_attr", "Attr value")
- << TError("Inner error");
- auto truncatedError = error.Truncate();
- EXPECT_EQ(error.GetCode(), truncatedError.GetCode());
- EXPECT_EQ(error.GetMessage(), truncatedError.GetMessage());
- EXPECT_EQ(error.GetPid(), truncatedError.GetPid());
- EXPECT_EQ(error.GetTid(), truncatedError.GetTid());
- EXPECT_EQ(GetSpanId(error), GetSpanId(truncatedError));
- EXPECT_EQ(error.GetDatetime(), truncatedError.GetDatetime());
- EXPECT_EQ(error.Attributes().Get<TString>("my_attr"), truncatedError.Attributes().Get<TString>("my_attr"));
- EXPECT_EQ(error.InnerErrors().size(), truncatedError.InnerErrors().size());
- EXPECT_EQ(error.InnerErrors()[0].GetMessage(), truncatedError.InnerErrors()[0].GetMessage());
-}
-
-TEST(TErrorTest, TruncateLarge)
-{
- auto error = TError("Some long long error")
- << TError("First inner error")
- << TError("Second inner error")
- << TError("Third inner error")
- << TError("Fourth inner error");
- SetErrorAttribute(&error, "my_attr", "Some long long attr");
-
- auto truncatedError = error.Truncate(/*maxInnerErrorCount*/ 3, /*stringLimit*/ 10);
- EXPECT_EQ(error.GetCode(), truncatedError.GetCode());
- EXPECT_EQ("Some long ...<message truncated>", truncatedError.GetMessage());
- EXPECT_EQ("...<attribute truncated>...", truncatedError.Attributes().Get<TString>("my_attr"));
- EXPECT_EQ(truncatedError.InnerErrors().size(), 3u);
-
- EXPECT_EQ("First inne...<message truncated>", truncatedError.InnerErrors()[0].GetMessage());
- EXPECT_EQ("Second inn...<message truncated>", truncatedError.InnerErrors()[1].GetMessage());
- EXPECT_EQ("Fourth inn...<message truncated>", truncatedError.InnerErrors()[2].GetMessage());
-}
-
-TEST(TErrorTest, TruncateSimpleRValue)
-{
- auto error = TError("Some error")
- << TErrorAttribute("my_attr", "Attr value")
- << TError("Inner error");
- auto errorCopy = error;
- auto truncatedError = std::move(errorCopy).Truncate();
- EXPECT_TRUE(errorCopy.IsOK());
-
- EXPECT_EQ(error.GetCode(), truncatedError.GetCode());
- EXPECT_EQ(error.GetMessage(), truncatedError.GetMessage());
- EXPECT_EQ(error.GetPid(), truncatedError.GetPid());
- EXPECT_EQ(error.GetTid(), truncatedError.GetTid());
- EXPECT_EQ(GetSpanId(error), GetSpanId(truncatedError));
- EXPECT_EQ(error.GetDatetime(), truncatedError.GetDatetime());
- EXPECT_EQ(error.Attributes().Get<TString>("my_attr"), truncatedError.Attributes().Get<TString>("my_attr"));
- EXPECT_EQ(error.InnerErrors().size(), truncatedError.InnerErrors().size());
- EXPECT_EQ(error.InnerErrors()[0].GetMessage(), truncatedError.InnerErrors()[0].GetMessage());
-}
-
-TEST(TErrorTest, TruncateLargeRValue)
-{
- auto error = TError("Some long long error")
- << TError("First inner error")
- << TError("Second inner error")
- << TError("Third inner error")
- << TError("Fourth inner error");
- SetErrorAttribute(&error, "my_attr", "Some long long attr");
-
- auto errorCopy = error;
- auto truncatedError = std::move(errorCopy).Truncate(/*maxInnerErrorCount*/ 3, /*stringLimit*/ 10);
- EXPECT_TRUE(errorCopy.IsOK());
-
- EXPECT_EQ(error.GetCode(), truncatedError.GetCode());
- EXPECT_EQ("Some long ...<message truncated>", truncatedError.GetMessage());
- EXPECT_EQ("...<attribute truncated>...", truncatedError.Attributes().Get<TString>("my_attr"));
- EXPECT_EQ(truncatedError.InnerErrors().size(), 3u);
-
- EXPECT_EQ("First inne...<message truncated>", truncatedError.InnerErrors()[0].GetMessage());
- EXPECT_EQ("Second inn...<message truncated>", truncatedError.InnerErrors()[1].GetMessage());
- EXPECT_EQ("Fourth inn...<message truncated>", truncatedError.InnerErrors()[2].GetMessage());
-}
-
-TEST(TErrorTest, TruncateConsistentOverloads)
-{
- auto error = TError("Some long long error")
- << TError("First inner error")
- << TError("Second inner error")
- << TError("Third inner error")
- << TError("Fourth inner error");
- SetErrorAttribute(&error, "my_attr", "Some long long attr");
-
- auto errorCopy = error;
- auto truncatedRValueError = std::move(errorCopy).Truncate(/*maxInnerErrorCount*/ 3, /*stringLimit*/ 10);
-
- auto trunactedLValueError = error.Truncate(/*maxInnerErrorCount*/ 3, /*stringLimit*/ 10);
-
- EXPECT_EQ(truncatedRValueError, trunactedLValueError);
-}
-
-TEST(TErrorTest, TruncateWhitelist)
-{
- auto error = TError("Some error");
- SetErrorAttribute(&error, "attr1", "Some long long attr");
- SetErrorAttribute(&error, "attr2", "Some long long attr");
-
- THashSet<TStringBuf> myWhitelist = {"attr2"};
-
- auto truncatedError = error.Truncate(2, 10, myWhitelist);
-
- EXPECT_EQ(error.GetCode(), truncatedError.GetCode());
- EXPECT_EQ(error.GetMessage(), truncatedError.GetMessage());
-
- EXPECT_EQ("...<attribute truncated>...", truncatedError.Attributes().Get<TString>("attr1"));
- EXPECT_EQ("Some long long attr", truncatedError.Attributes().Get<TString>("attr2"));
-}
-
-TEST(TErrorTest, TruncateWhitelistRValue)
-{
- auto error = TError("Some error");
- SetErrorAttribute(&error, "attr1", "Some long long attr");
- SetErrorAttribute(&error, "attr2", "Some long long attr");
-
- THashSet<TStringBuf> myWhitelist = {"attr2"};
-
- auto errorCopy = error;
- auto truncatedError = std::move(errorCopy).Truncate(2, 10, myWhitelist);
- EXPECT_TRUE(errorCopy.IsOK());
-
- EXPECT_EQ(error.GetCode(), truncatedError.GetCode());
- EXPECT_EQ(error.GetMessage(), truncatedError.GetMessage());
-
- EXPECT_EQ("...<attribute truncated>...", truncatedError.Attributes().Get<TString>("attr1"));
- EXPECT_EQ("Some long long attr", truncatedError.Attributes().Get<TString>("attr2"));
-}
-
-TEST(TErrorTest, TruncateWhitelistInnerErrors)
-{
- auto innerError = TError("Inner error");
- SetErrorAttribute(&innerError, "attr1", "Some long long attr");
- SetErrorAttribute(&innerError, "attr2", "Some long long attr");
-
- auto error = TError("Error") << innerError;
-
- THashSet<TStringBuf> myWhitelist = {"attr2"};
-
- auto truncatedError = error.Truncate(2, 20, myWhitelist);
- EXPECT_EQ(truncatedError.InnerErrors().size(), 1u);
-
- auto truncatedInnerError = truncatedError.InnerErrors()[0];
- EXPECT_EQ(truncatedInnerError.GetCode(), innerError.GetCode());
- EXPECT_EQ(truncatedInnerError.GetMessage(), innerError.GetMessage());
- EXPECT_EQ("...<attribute truncated>...", truncatedInnerError.Attributes().Get<TString>("attr1"));
- EXPECT_EQ("Some long long attr", truncatedInnerError.Attributes().Get<TString>("attr2"));
-}
-
-TEST(TErrorTest, TruncateWhitelistInnerErrorsRValue)
-{
- auto innerError = TError("Inner error");
- SetErrorAttribute(&innerError, "attr1", "Some long long attr");
- SetErrorAttribute(&innerError, "attr2", "Some long long attr");
-
- auto error = TError("Error") << innerError;
-
- THashSet<TStringBuf> myWhitelist = {"attr2"};
-
- auto errorCopy = error;
- auto truncatedError = std::move(errorCopy).Truncate(2, 20, myWhitelist);
- EXPECT_TRUE(errorCopy.IsOK());
- EXPECT_EQ(truncatedError.InnerErrors().size(), 1u);
-
- auto truncatedInnerError = truncatedError.InnerErrors()[0];
- EXPECT_EQ(truncatedInnerError.GetCode(), innerError.GetCode());
- EXPECT_EQ(truncatedInnerError.GetMessage(), innerError.GetMessage());
- EXPECT_EQ("...<attribute truncated>...", truncatedInnerError.Attributes().Get<TString>("attr1"));
- EXPECT_EQ("Some long long attr", truncatedInnerError.Attributes().Get<TString>("attr2"));
-}
-
-TEST(TErrorTest, TruncateWhitelistSaveInnerError)
-{
- auto genericInner = TError("GenericInner");
- auto whitelistedInner = TError("Inner")
- << TErrorAttribute("whitelisted_key", 42);
-
- auto error = TError("Error")
- << (genericInner << TErrorAttribute("foo", "bar"))
- << whitelistedInner
- << genericInner;
-
- error = std::move(error).Truncate(1, 20, {
- "whitelisted_key"
- });
- EXPECT_TRUE(!error.IsOK());
- EXPECT_EQ(error.InnerErrors().size(), 2u);
- EXPECT_EQ(error.InnerErrors()[0], whitelistedInner);
- EXPECT_EQ(error.InnerErrors()[1], genericInner);
-
- EXPECT_TRUE(FindAttributeRecursive<int>(error, "whitelisted_key"));
- EXPECT_FALSE(FindAttributeRecursive<int>(error, "foo"));
-}
-
-TEST(TErrorTest, YTExceptionToError)
-{
- try {
- throw TSimpleException("message");
- } catch (const std::exception& ex) {
- TError error(ex);
- EXPECT_EQ(NYT::EErrorCode::Generic, error.GetCode());
- EXPECT_EQ("message", error.GetMessage());
- }
-}
-
-TEST(TErrorTest, CompositeYTExceptionToError)
-{
- try {
- try {
- throw TSimpleException("inner message");
- } catch (const std::exception& ex) {
- throw TSimpleException(ex, "outer message");
- }
- } catch (const std::exception& ex) {
- TError outerError(ex);
- EXPECT_EQ(NYT::EErrorCode::Generic, outerError.GetCode());
- EXPECT_EQ("outer message", outerError.GetMessage());
- EXPECT_EQ(1, std::ssize(outerError.InnerErrors()));
- const auto& innerError = outerError.InnerErrors()[0];
- EXPECT_EQ(NYT::EErrorCode::Generic, innerError.GetCode());
- EXPECT_EQ("inner message", innerError.GetMessage());
- }
-}
-
-TEST(TErrorTest, YTExceptionWithAttributesToError)
-{
- try {
- throw TSimpleException("message")
- << TExceptionAttribute{"Int64 value", static_cast<i64>(42)}
- << TExceptionAttribute{"double value", 7.77}
- << TExceptionAttribute{"bool value", false}
- << TExceptionAttribute{"String value", "FooBar"};
- } catch (const std::exception& ex) {
- TError error(ex);
- EXPECT_EQ(NYT::EErrorCode::Generic, error.GetCode());
- EXPECT_EQ("message", error.GetMessage());
-
- auto i64value = error.Attributes().Find<i64>("Int64 value");
- EXPECT_TRUE(i64value);
- EXPECT_EQ(*i64value, static_cast<i64>(42));
-
- auto doubleValue = error.Attributes().Find<double>("double value");
- EXPECT_TRUE(doubleValue);
- EXPECT_EQ(*doubleValue, 7.77);
-
- auto boolValue = error.Attributes().Find<bool>("bool value");
- EXPECT_TRUE(boolValue);
- EXPECT_EQ(*boolValue, false);
-
- auto stringValue = error.Attributes().Find<TString>("String value");
- EXPECT_TRUE(stringValue);
- EXPECT_EQ(*stringValue, "FooBar");
- }
-}
-
TEST(TErrorTest, ErrorSanitizer)
{
auto checkSantizied = [&] (const TError& error) {
@@ -842,29 +180,6 @@ TEST(TErrorTest, SimpleLoadAfterSave)
EXPECT_EQ(ToString(savedError), ToString(loadedError));
}
-TEST(TErrorTest, AttributeSerialization)
-{
- auto getWeededText = [] (const TError& err) {
- std::vector<TString> lines;
- for (const auto& line : StringSplitter(ToString(err)).Split('\n')) {
- if (!line.Contains("origin") && !line.Contains("datetime")) {
- lines.push_back(TString{line});
- }
- }
- return JoinSeq("\n", lines);
- };
-
- EXPECT_EQ(getWeededText(TError("E1") << TErrorAttribute("A1", "V1")), TString(
- "E1\n"
- " A1 V1\n"));
- EXPECT_EQ(getWeededText(TError("E1") << TErrorAttribute("A1", "L1\nL2\nL3")), TString(
- "E1\n"
- " A1\n"
- " L1\n"
- " L2\n"
- " L3\n"));
-}
-
TEST(TErrorTest, TraceContext)
{
TError error;
@@ -913,82 +228,6 @@ TEST(TErrorTest, NativeFiberId)
.ThrowOnError();
}
-TEST(TErrorTest, MacroStaticAnalysis)
-{
- auto swallow = [] (auto expr) {
- try {
- expr();
- } catch (...) {
- }
- };
-
- swallow([] {
- THROW_ERROR_EXCEPTION("Foo");
- });
- swallow([] {
- THROW_ERROR_EXCEPTION("Hello, %v", "World");
- });
- swallow([] {
- THROW_ERROR_EXCEPTION(NYT::EErrorCode::Generic, "Foo");
- });
- swallow([] {
- THROW_ERROR_EXCEPTION(NYT::EErrorCode::Generic, "Foo%v", "Bar");
- });
- swallow([] {
- THROW_ERROR_EXCEPTION(NYT::EErrorCode::Generic, "Foo%v%v", "Bar", "Baz");
- });
- swallow([] {
- THROW_ERROR_EXCEPTION_IF_FAILED(TError{}, "Foo");
- });
- swallow([] {
- THROW_ERROR_EXCEPTION_IF_FAILED(TError{}, "Foo%v", "Bar");
- });
- swallow([] {
- THROW_ERROR_EXCEPTION_IF_FAILED(TError{}, "Foo%v%v", "Bar", "Baz");
- });
- swallow([] {
- THROW_ERROR_EXCEPTION_IF_FAILED(TError{}, NYT::EErrorCode::Generic, "Foo%v", "Bar");
- });
- swallow([] {
- THROW_ERROR_EXCEPTION_IF_FAILED(TError{}, NYT::EErrorCode::Generic, "Foo%v%v", "Bar", "Baz");
- });
-}
-
-TEST(TErrorTest, WrapStaticAnalysis)
-{
- TError error;
- Y_UNUSED(error.Wrap());
- Y_UNUSED(error.Wrap(std::exception{}));
- Y_UNUSED(error.Wrap("Hello"));
- Y_UNUSED(error.Wrap("Hello, %v", "World"));
- Y_UNUSED(error.Wrap(TRuntimeFormat{"Hello, %v"}));
-}
-
-// NB(arkady-e1ppa): Uncomment these occasionally to see
-// that static analysis is still working.
-TEST(TErrorTest, MacroStaticAnalysisBrokenFormat)
-{
- // auto swallow = [] (auto expr) {
- // try {
- // expr();
- // } catch (...) {
- // }
- // };
-
- // swallow([] {
- // THROW_ERROR_EXCEPTION("Hello, %v");
- // });
- // swallow([] {
- // THROW_ERROR_EXCEPTION(TErrorCode{}, "Foo%v");
- // });
- // swallow([] {
- // THROW_ERROR_EXCEPTION_IF_FAILED(TError{}, "Foo%v");
- // });
- // swallow([] {
- // THROW_ERROR_EXCEPTION_IF_FAILED(TError{}, TErrorCode{}, "Foo%v");
- // });
-}
-
////////////////////////////////////////////////////////////////////////////////
} // namespace
diff --git a/yt/yt/core/misc/unittests/ya.make b/yt/yt/core/misc/unittests/ya.make
index 79d62c52c28..f8ab343a69d 100644
--- a/yt/yt/core/misc/unittests/ya.make
+++ b/yt/yt/core/misc/unittests/ya.make
@@ -24,7 +24,6 @@ SRCS(
digest_ut.cpp
ema_counter_ut.cpp
enum_ut.cpp
- error_code_ut.cpp
error_ut.cpp
fair_scheduler_ut.cpp
fenwick_tree_ut.cpp
diff --git a/yt/yt/core/ya.make b/yt/yt/core/ya.make
index 9794fce0a28..04c85cab3a7 100644
--- a/yt/yt/core/ya.make
+++ b/yt/yt/core/ya.make
@@ -129,7 +129,6 @@ SRCS(
misc/crash_handler.cpp
misc/digest.cpp
misc/error.cpp
- misc/error_code.cpp
misc/fs.cpp
# NB: it is necessary to prevent linker optimization of
# REGISTER_INTERMEDIATE_PROTO_INTEROP_REPRESENTATION macros for TGuid.
@@ -142,7 +141,6 @@ SRCS(
misc/linear_probe.cpp
misc/memory_usage_tracker.cpp
misc/relaxed_mpsc_queue.cpp
- misc/origin_attributes.cpp
misc/parser_helpers.cpp
misc/pattern_formatter.cpp
misc/phoenix.cpp
@@ -162,8 +160,6 @@ SRCS(
misc/slab_allocator.cpp
misc/statistic_path.cpp
misc/statistics.cpp
- misc/string_helpers.cpp
- misc/stripped_error.cpp
misc/cache_config.cpp
misc/utf8_decoder.cpp
misc/zerocopy_output_writer.cpp
diff --git a/yt/yt/core/ytree/attributes-inl.h b/yt/yt/core/ytree/attributes-inl.h
index cfb18c40cfa..1783c33044f 100644
--- a/yt/yt/core/ytree/attributes-inl.h
+++ b/yt/yt/core/ytree/attributes-inl.h
@@ -99,7 +99,17 @@ struct TMergeDictionariesTraits<NYTree::IAttributeDictionary>
{
static auto MakeIterableView(const NYTree::IAttributeDictionary& dict)
{
- return dict.ListPairs();
+ auto pairs = dict.ListPairs();
+
+ std::vector<TErrorAttributes::TKeyValuePair> ret = {};
+ ret.reserve(std::ssize(pairs));
+
+ for (const auto& [key, value] : pairs) {
+ ret.emplace_back(
+ key,
+ NYT::ToErrorAttributeValue(value));
+ }
+ return ret;
}
};
diff --git a/yt/yt/core/ytree/convert-inl.h b/yt/yt/core/ytree/convert-inl.h
index 0e3385ed5bf..60bf5979411 100644
--- a/yt/yt/core/ytree/convert-inl.h
+++ b/yt/yt/core/ytree/convert-inl.h
@@ -182,14 +182,6 @@ T ConstructYTreeConvertibleObject()
////////////////////////////////////////////////////////////////////////////////
-} // namespace NYT::NYTree
-
-////////////////////////////////////////////////////////////////////////////////
-
-namespace NYT::NConvertToImpl {
-
-////////////////////////////////////////////////////////////////////////////////
-
namespace {
double ConvertYsonStringBaseToDouble(const NYson::TYsonStringBuf& yson)
@@ -234,15 +226,9 @@ TString ConvertYsonStringBaseToString(const NYson::TYsonStringBuf& yson)
////////////////////////////////////////////////////////////////////////////////
-// NB(arkady-e1ppa): TTagInvokeTag uses decltype under the hood
-// meaning the resulting expression is not viable for template argument deduction
-// thus we have to write the type by hand in order to have TTo deducible
-// automatically.
template <class TTo>
-TTo TagInvoke(NConvertToImpl::TFn<TTo>, const NYTree::INodePtr& node)
+TTo ConvertTo(const NYTree::INodePtr& node)
{
- using namespace NYTree;
-
auto result = ConstructYTreeConvertibleObject<TTo>();
Deserialize(result, node);
return result;
@@ -251,10 +237,8 @@ TTo TagInvoke(NConvertToImpl::TFn<TTo>, const NYTree::INodePtr& node)
////////////////////////////////////////////////////////////////////////////////
template <class TTo, class TFrom>
-TTo TagInvoke(NConvertToImpl::TFn<TTo>, const TFrom& value)
+TTo ConvertTo(const TFrom& value)
{
- using namespace NYTree;
-
auto type = GetYsonType(value);
if constexpr (
NYson::ArePullParserDeserializable<TTo>() &&
@@ -285,9 +269,8 @@ TTo TagInvoke(NConvertToImpl::TFn<TTo>, const TFrom& value)
#define IMPLEMENT_CHECKED_INTEGRAL_CONVERT_TO(type) \
template <> \
- inline type TagInvoke(TTagInvokeTag<ConvertTo<type>>, const NYson::TYsonString& str) \
+ inline type ConvertTo(const NYson::TYsonString& str) \
{ \
- using namespace NYTree; \
NYson::TTokenizer tokenizer(str.AsStringBuf()); \
const auto& token = SkipAttributes(&tokenizer); \
switch (token.GetType()) { \
@@ -316,29 +299,50 @@ IMPLEMENT_CHECKED_INTEGRAL_CONVERT_TO(ui8)
////////////////////////////////////////////////////////////////////////////////
template <>
-inline double TagInvoke(TTagInvokeTag<ConvertTo<double>>, const NYson::TYsonString& str)
+inline double ConvertTo(const NYson::TYsonString& str)
{
return ConvertYsonStringBaseToDouble(str);
}
template <>
-inline double TagInvoke(TTagInvokeTag<ConvertTo<double>>, const NYson::TYsonStringBuf& str)
+inline double ConvertTo(const NYson::TYsonStringBuf& str)
{
return ConvertYsonStringBaseToDouble(str);
}
template <>
-inline TString TagInvoke(TTagInvokeTag<ConvertTo<TString>>, const NYson::TYsonString& str)
+inline TString ConvertTo(const NYson::TYsonString& str)
{
return ConvertYsonStringBaseToString(str);
}
template <>
-inline TString TagInvoke(TTagInvokeTag<ConvertTo<TString>>, const NYson::TYsonStringBuf& str)
+inline TString ConvertTo(const NYson::TYsonStringBuf& str)
{
return ConvertYsonStringBaseToString(str);
}
////////////////////////////////////////////////////////////////////////////////
-} // namespace NYT::NConvertToImpl
+} // namespace NYT::NYTree
+
+namespace NYT::NAttributeValueConversionImpl {
+
+////////////////////////////////////////////////////////////////////////////////
+
+template <class T>
+ requires (!CPrimitiveConvertible<T>)
+T TagInvoke(TFrom<T>, const NYson::TYsonString& value)
+{
+ return NYTree::ConvertTo<T>(value);
+}
+
+////////////////////////////////////////////////////////////////////////////////
+
+} // namespace NYT::NAttributeValueConversionImpl
+
+namespace NYT {
+
+using NYT::NYTree::ConvertTo;
+
+} // namespace NYT
diff --git a/yt/yt/core/ytree/convert.h b/yt/yt/core/ytree/convert.h
index d80e4448f2f..e07b10c0bbd 100644
--- a/yt/yt/core/ytree/convert.h
+++ b/yt/yt/core/ytree/convert.h
@@ -5,8 +5,6 @@
#include <yt/yt/core/yson/consumer.h>
-#include <library/cpp/yt/error/convert_to_cpo.h>
-
namespace NYT::NYson {
////////////////////////////////////////////////////////////////////////////////
@@ -55,7 +53,11 @@ T ConstructYTreeConvertibleObject();
////////////////////////////////////////////////////////////////////////////////
-using NYT::ConvertTo;
+template <class TTo>
+TTo ConvertTo(const INodePtr& node);
+
+template <class TTo, class TFrom>
+TTo ConvertTo(const TFrom& value);
////////////////////////////////////////////////////////////////////////////////
diff --git a/yt/yt/core/ytree/node-inl.h b/yt/yt/core/ytree/node-inl.h
index a766ad7f020..49e3c0acd40 100644
--- a/yt/yt/core/ytree/node-inl.h
+++ b/yt/yt/core/ytree/node-inl.h
@@ -4,12 +4,19 @@
#include "node.h"
#endif
-#include <library/cpp/yt/error/convert_to_cpo.h>
-
namespace NYT::NYTree {
////////////////////////////////////////////////////////////////////////////////
+// Forward declarations
+template <class TTo>
+TTo ConvertTo(const INodePtr& node);
+
+template <class TTo, class TFrom>
+TTo ConvertTo(const TFrom& value);
+
+////////////////////////////////////////////////////////////////////////////////
+
template <class T>
T INode::GetValue() const
{