Skip to content

Commit b14aa36

Browse files
tanjialiangfacebook-github-bot
authored andcommitted
refactor: Refactor exception translation to be extensible (prestodb#26563)
Summary: Currently the velox exception translation is through global static methods, not extensible. Use cases arises when we want additional error types exposed from not within presto/velox stack. This change makes the translation layer a global singleton, similar to BaseStatsReporter in velox, creating flexibility to extend and customize. Reviewed By: xiaoxmeng Differential Revision: D86562978
1 parent 7497682 commit b14aa36

File tree

7 files changed

+251
-130
lines changed

7 files changed

+251
-130
lines changed

presto-native-execution/presto_cpp/main/PrestoMain.cpp

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
#include <gflags/gflags_declare.h>
1717
#include <glog/logging.h>
1818
#include "presto_cpp/main/PrestoServer.h"
19+
#include "presto_cpp/main/common/Exception.h"
1920
#include "presto_cpp/main/common/Utils.h"
2021
#include "velox/common/base/StatsReporter.h"
2122

@@ -37,3 +38,15 @@ folly::Singleton<facebook::velox::BaseStatsReporter> reporter([]() {
3738
return new facebook::velox::DummyStatsReporter();
3839
});
3940
#endif
41+
42+
// Initialize singleton for the exception translator.
43+
// NOTE: folly::Singleton enforces that only ONE registration per type can exist.
44+
// If another file tries to register VeloxToPrestoExceptionTranslator again,
45+
// the program will fail during static initialization with a duplicate registration error.
46+
// Extended servers should register a DERIVED class instead:
47+
// folly::Singleton<VeloxToPrestoExceptionTranslator> customTranslator([]() {
48+
// return new CustomExceptionTranslator(); // derived class
49+
// });
50+
folly::Singleton<facebook::presto::VeloxToPrestoExceptionTranslator>
51+
exceptionTranslator(
52+
[]() { return new facebook::presto::VeloxToPrestoExceptionTranslator(); });

presto-native-execution/presto_cpp/main/PrestoTask.cpp

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -97,9 +97,9 @@ protocol::ExecutionFailureInfo toPrestoError(std::exception_ptr ex) {
9797
try {
9898
rethrow_exception(ex);
9999
} catch (const VeloxException& e) {
100-
return VeloxToPrestoExceptionTranslator::translate(e);
100+
return translateToPrestoException(e);
101101
} catch (const std::exception& e) {
102-
return VeloxToPrestoExceptionTranslator::translate(e);
102+
return translateToPrestoException(e);
103103
}
104104
}
105105

presto-native-execution/presto_cpp/main/common/Exception.cpp

Lines changed: 142 additions & 100 deletions
Original file line numberDiff line numberDiff line change
@@ -16,105 +16,149 @@
1616

1717
namespace facebook::presto {
1818

19-
std::unordered_map<
20-
std::string,
21-
std::unordered_map<std::string, protocol::ErrorCode>>&
22-
VeloxToPrestoExceptionTranslator::translateMap() {
23-
static std::unordered_map<
24-
std::string,
25-
std::unordered_map<std::string, protocol::ErrorCode>>
26-
errorMap = {
27-
{velox::error_source::kErrorSourceRuntime,
28-
{{velox::error_code::kMemCapExceeded,
29-
{0x00020007,
30-
"EXCEEDED_LOCAL_MEMORY_LIMIT",
31-
protocol::ErrorType::INSUFFICIENT_RESOURCES}},
32-
33-
{velox::error_code::kMemAborted,
34-
{0x00020000,
35-
"GENERIC_INSUFFICIENT_RESOURCES",
36-
protocol::ErrorType::INSUFFICIENT_RESOURCES}},
37-
38-
{velox::error_code::kSpillLimitExceeded,
39-
{0x00020006,
40-
"EXCEEDED_SPILL_LIMIT",
41-
protocol::ErrorType::INSUFFICIENT_RESOURCES}},
42-
43-
{velox::error_code::kMemArbitrationFailure,
44-
{0x00020000,
45-
"MEMORY_ARBITRATION_FAILURE",
46-
protocol::ErrorType::INSUFFICIENT_RESOURCES}},
47-
48-
{velox::error_code::kMemArbitrationTimeout,
49-
{0x00020000,
50-
"GENERIC_INSUFFICIENT_RESOURCES",
51-
protocol::ErrorType::INSUFFICIENT_RESOURCES}},
52-
53-
{velox::error_code::kMemAllocError,
54-
{0x00020000,
55-
"GENERIC_INSUFFICIENT_RESOURCES",
56-
protocol::ErrorType::INSUFFICIENT_RESOURCES}},
57-
58-
{velox::error_code::kInvalidState,
59-
{0x00010000,
60-
"GENERIC_INTERNAL_ERROR",
61-
protocol::ErrorType::INTERNAL_ERROR}},
62-
63-
{velox::error_code::kGenericSpillFailure,
64-
{0x00010023,
65-
"GENERIC_SPILL_FAILURE",
66-
protocol::ErrorType::INTERNAL_ERROR}},
67-
68-
{velox::error_code::kUnreachableCode,
69-
{0x00010000,
70-
"GENERIC_INTERNAL_ERROR",
71-
protocol::ErrorType::INTERNAL_ERROR}},
72-
73-
{velox::error_code::kNotImplemented,
74-
{0x00010000,
75-
"GENERIC_INTERNAL_ERROR",
76-
protocol::ErrorType::INTERNAL_ERROR}},
77-
78-
{velox::error_code::kUnknown,
79-
{0x00010000,
80-
"GENERIC_INTERNAL_ERROR",
81-
protocol::ErrorType::INTERNAL_ERROR}},
82-
83-
{presto::error_code::kExceededLocalBroadcastJoinMemoryLimit,
84-
{0x0002000C,
85-
"EXCEEDED_LOCAL_BROADCAST_JOIN_MEMORY_LIMIT",
86-
protocol::ErrorType::INSUFFICIENT_RESOURCES}}}},
87-
88-
{velox::error_source::kErrorSourceUser,
89-
{{velox::error_code::kInvalidArgument,
90-
{0x00000000,
91-
"GENERIC_USER_ERROR",
92-
protocol::ErrorType::USER_ERROR}},
93-
{velox::error_code::kUnsupported,
94-
{0x0000000D, "NOT_SUPPORTED", protocol::ErrorType::USER_ERROR}},
95-
{velox::error_code::kUnsupportedInputUncatchable,
96-
{0x0000000D, "NOT_SUPPORTED", protocol::ErrorType::USER_ERROR}},
97-
{velox::error_code::kArithmeticError,
98-
{0x00000000,
99-
"GENERIC_USER_ERROR",
100-
protocol::ErrorType::USER_ERROR}},
101-
{velox::error_code::kSchemaMismatch,
102-
{0x00000000,
103-
"GENERIC_USER_ERROR",
104-
protocol::ErrorType::USER_ERROR}},
105-
{velox::error_code::kInvalidArgument,
106-
{0x00000000,
107-
"GENERIC_USER_ERROR",
108-
protocol::ErrorType::USER_ERROR}}}},
109-
110-
{velox::error_source::kErrorSourceSystem, {}}};
111-
return errorMap;
19+
VeloxToPrestoExceptionTranslator::VeloxToPrestoExceptionTranslator() {
20+
// Register runtime errors
21+
registerError(
22+
velox::error_source::kErrorSourceRuntime,
23+
velox::error_code::kMemCapExceeded,
24+
{.code = 0x00020007,
25+
.name = "EXCEEDED_LOCAL_MEMORY_LIMIT",
26+
.type = protocol::ErrorType::INSUFFICIENT_RESOURCES});
27+
28+
registerError(
29+
velox::error_source::kErrorSourceRuntime,
30+
velox::error_code::kMemAborted,
31+
{.code = 0x00020000,
32+
.name = "GENERIC_INSUFFICIENT_RESOURCES",
33+
.type = protocol::ErrorType::INSUFFICIENT_RESOURCES});
34+
35+
registerError(
36+
velox::error_source::kErrorSourceRuntime,
37+
velox::error_code::kSpillLimitExceeded,
38+
{.code = 0x00020006,
39+
.name = "EXCEEDED_SPILL_LIMIT",
40+
.type = protocol::ErrorType::INSUFFICIENT_RESOURCES});
41+
42+
registerError(
43+
velox::error_source::kErrorSourceRuntime,
44+
velox::error_code::kMemArbitrationFailure,
45+
{.code = 0x00020000,
46+
.name = "MEMORY_ARBITRATION_FAILURE",
47+
.type = protocol::ErrorType::INSUFFICIENT_RESOURCES});
48+
49+
registerError(
50+
velox::error_source::kErrorSourceRuntime,
51+
velox::error_code::kMemArbitrationTimeout,
52+
{.code = 0x00020000,
53+
.name = "GENERIC_INSUFFICIENT_RESOURCES",
54+
.type = protocol::ErrorType::INSUFFICIENT_RESOURCES});
55+
56+
registerError(
57+
velox::error_source::kErrorSourceRuntime,
58+
velox::error_code::kMemAllocError,
59+
{.code = 0x00020000,
60+
.name = "GENERIC_INSUFFICIENT_RESOURCES",
61+
.type = protocol::ErrorType::INSUFFICIENT_RESOURCES});
62+
63+
registerError(
64+
velox::error_source::kErrorSourceRuntime,
65+
velox::error_code::kInvalidState,
66+
{.code = 0x00010000,
67+
.name = "GENERIC_INTERNAL_ERROR",
68+
.type = protocol::ErrorType::INTERNAL_ERROR});
69+
70+
registerError(
71+
velox::error_source::kErrorSourceRuntime,
72+
velox::error_code::kGenericSpillFailure,
73+
{.code = 0x00010023,
74+
.name = "GENERIC_SPILL_FAILURE",
75+
.type = protocol::ErrorType::INTERNAL_ERROR});
76+
77+
registerError(
78+
velox::error_source::kErrorSourceRuntime,
79+
velox::error_code::kUnreachableCode,
80+
{.code = 0x00010000,
81+
.name = "GENERIC_INTERNAL_ERROR",
82+
.type = protocol::ErrorType::INTERNAL_ERROR});
83+
84+
registerError(
85+
velox::error_source::kErrorSourceRuntime,
86+
velox::error_code::kNotImplemented,
87+
{.code = 0x00010000,
88+
.name = "GENERIC_INTERNAL_ERROR",
89+
.type = protocol::ErrorType::INTERNAL_ERROR});
90+
91+
registerError(
92+
velox::error_source::kErrorSourceRuntime,
93+
velox::error_code::kUnknown,
94+
{.code = 0x00010000,
95+
.name = "GENERIC_INTERNAL_ERROR",
96+
.type = protocol::ErrorType::INTERNAL_ERROR});
97+
98+
registerError(
99+
velox::error_source::kErrorSourceRuntime,
100+
presto::error_code::kExceededLocalBroadcastJoinMemoryLimit,
101+
{.code = 0x0002000C,
102+
.name = "EXCEEDED_LOCAL_BROADCAST_JOIN_MEMORY_LIMIT",
103+
.type = protocol::ErrorType::INSUFFICIENT_RESOURCES});
104+
105+
// Register user errors
106+
registerError(
107+
velox::error_source::kErrorSourceUser,
108+
velox::error_code::kInvalidArgument,
109+
{.code = 0x00000000,
110+
.name = "GENERIC_USER_ERROR",
111+
.type = protocol::ErrorType::USER_ERROR});
112+
113+
registerError(
114+
velox::error_source::kErrorSourceUser,
115+
velox::error_code::kUnsupported,
116+
{.code = 0x0000000D,
117+
.name = "NOT_SUPPORTED",
118+
.type = protocol::ErrorType::USER_ERROR});
119+
120+
registerError(
121+
velox::error_source::kErrorSourceUser,
122+
velox::error_code::kUnsupportedInputUncatchable,
123+
{.code = 0x0000000D,
124+
.name = "NOT_SUPPORTED",
125+
.type = protocol::ErrorType::USER_ERROR});
126+
127+
registerError(
128+
velox::error_source::kErrorSourceUser,
129+
velox::error_code::kArithmeticError,
130+
{.code = 0x00000000,
131+
.name = "GENERIC_USER_ERROR",
132+
.type = protocol::ErrorType::USER_ERROR});
133+
134+
registerError(
135+
velox::error_source::kErrorSourceUser,
136+
velox::error_code::kSchemaMismatch,
137+
{.code = 0x00000000,
138+
.name = "GENERIC_USER_ERROR",
139+
.type = protocol::ErrorType::USER_ERROR});
140+
}
141+
142+
void VeloxToPrestoExceptionTranslator::registerError(
143+
const std::string& errorSource,
144+
const std::string& errorCode,
145+
const protocol::ErrorCode& prestoErrorCode) {
146+
auto& innerMap = errorMap_[errorSource];
147+
auto [it, inserted] = innerMap.emplace(errorCode, prestoErrorCode);
148+
VELOX_CHECK(
149+
inserted,
150+
"Duplicate errorCode '{}' for errorSource '{}' is not allowed. "
151+
"Existing mapping: [code={}, name={}, type={}]",
152+
errorCode,
153+
errorSource,
154+
it->second.code,
155+
it->second.name,
156+
static_cast<int>(it->second.type));
112157
}
113158

114159
protocol::ExecutionFailureInfo VeloxToPrestoExceptionTranslator::translate(
115-
const velox::VeloxException& e) {
160+
const velox::VeloxException& e) const {
116161
protocol::ExecutionFailureInfo error;
117-
// Line number must be >= 1
118162
error.errorLocation.lineNumber = e.line() >= 1 ? e.line() : 1;
119163
error.errorLocation.columnNumber = 1;
120164
error.type = e.exceptionName();
@@ -127,17 +171,15 @@ protocol::ExecutionFailureInfo VeloxToPrestoExceptionTranslator::translate(
127171
msg << " " << e.additionalContext();
128172
}
129173
error.message = msg.str();
130-
// Stack trace may not be available if stack trace capturing is disabled or
131-
// rate limited.
132174
if (e.stackTrace()) {
133175
error.stack = e.stackTrace()->toStrVector();
134176
}
135177

136178
const auto& errorSource = e.errorSource();
137179
const auto& errorCode = e.errorCode();
138180

139-
auto itrErrorCodesMap = translateMap().find(errorSource);
140-
if (itrErrorCodesMap != translateMap().end()) {
181+
auto itrErrorCodesMap = errorMap_.find(errorSource);
182+
if (itrErrorCodesMap != errorMap_.end()) {
141183
auto itrErrorCode = itrErrorCodesMap->second.find(errorCode);
142184
if (itrErrorCode != itrErrorCodesMap->second.end()) {
143185
error.errorCode = itrErrorCode->second;
@@ -151,7 +193,7 @@ protocol::ExecutionFailureInfo VeloxToPrestoExceptionTranslator::translate(
151193
}
152194

153195
protocol::ExecutionFailureInfo VeloxToPrestoExceptionTranslator::translate(
154-
const std::exception& e) {
196+
const std::exception& e) const {
155197
protocol::ExecutionFailureInfo error;
156198
error.errorLocation.lineNumber = 1;
157199
error.errorLocation.columnNumber = 1;

presto-native-execution/presto_cpp/main/common/Exception.h

Lines changed: 55 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
*/
1414
#pragma once
1515

16+
#include <folly/Singleton.h>
1617
#include <unordered_map>
1718
#include "presto_cpp/presto_protocol/core/presto_protocol_core.h"
1819
#include "velox/common/base/VeloxException.h"
@@ -35,20 +36,63 @@ inline constexpr auto kExceededLocalBroadcastJoinMemoryLimit =
3536
"EXCEEDED_LOCAL_BROADCAST_JOIN_MEMORY_LIMIT"_fs;
3637
} // namespace error_code
3738

39+
// Exception translator singleton for converting Velox exceptions to Presto
40+
// errors. This follows the same pattern as velox/common/base/StatsReporter.h.
41+
//
42+
// IMPORTANT: folly::Singleton enforces single registration per type.
43+
// - Only ONE registration of VeloxToPrestoExceptionTranslator can exist
44+
// - Duplicate registrations will cause program to fail during static init
45+
// - Extended servers must register a derived class
3846
class VeloxToPrestoExceptionTranslator {
3947
public:
40-
// Translates to Presto error from Velox exceptions
41-
static protocol::ExecutionFailureInfo translate(
42-
const velox::VeloxException& e);
48+
using ErrorCodeMap = std::unordered_map<
49+
std::string,
50+
std::unordered_map<std::string, protocol::ErrorCode>>;
4351

44-
// Translates to Presto error from std::exceptions
45-
static protocol::ExecutionFailureInfo translate(const std::exception& e);
52+
VeloxToPrestoExceptionTranslator();
4653

47-
// Returns a reference to the error map containing mapping between
48-
// velox error code and Presto errors defined in Presto protocol
49-
static std::unordered_map<
50-
std::string,
51-
std::unordered_map<std::string, protocol::ErrorCode>>&
52-
translateMap();
54+
virtual ~VeloxToPrestoExceptionTranslator() = default;
55+
56+
virtual protocol::ExecutionFailureInfo translate(
57+
const velox::VeloxException& e) const;
58+
59+
virtual protocol::ExecutionFailureInfo translate(
60+
const std::exception& e) const;
61+
62+
// For testing purposes only - provides access to the error map
63+
const ErrorCodeMap& testingErrorMap() const {
64+
return errorMap_;
65+
}
66+
67+
protected:
68+
void registerError(
69+
const std::string& errorSource,
70+
const std::string& errorCode,
71+
const protocol::ErrorCode& prestoErrorCode);
72+
73+
ErrorCodeMap errorMap_;
5374
};
75+
76+
// Global inline function APIs to translate exceptions (returns
77+
// ExecutionFailureInfo) Similar pattern to StatsReporter, but returns a value
78+
// instead of recording
79+
inline protocol::ExecutionFailureInfo translateToPrestoException(
80+
const velox::VeloxException& e) {
81+
const auto translator = folly::Singleton<
82+
VeloxToPrestoExceptionTranslator>::try_get_fast();
83+
VELOX_CHECK_NOT_NULL(
84+
translator,
85+
"VeloxToPrestoExceptionTranslator singleton must be registered");
86+
return translator->translate(e);
87+
}
88+
89+
inline protocol::ExecutionFailureInfo translateToPrestoException(
90+
const std::exception& e) {
91+
const auto translator = folly::Singleton<
92+
VeloxToPrestoExceptionTranslator>::try_get_fast();
93+
VELOX_CHECK_NOT_NULL(
94+
translator,
95+
"VeloxToPrestoExceptionTranslator singleton must be registered");
96+
return translator->translate(e);
97+
}
5498
} // namespace facebook::presto

0 commit comments

Comments
 (0)