Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 14 additions & 0 deletions presto-native-execution/presto_cpp/main/PrestoMain.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
#include <gflags/gflags_declare.h>
#include <glog/logging.h>
#include "presto_cpp/main/PrestoServer.h"
#include "presto_cpp/main/common/Exception.h"
#include "presto_cpp/main/common/Utils.h"
#include "velox/common/base/StatsReporter.h"

Expand All @@ -37,3 +38,16 @@ folly::Singleton<facebook::velox::BaseStatsReporter> reporter([]() {
return new facebook::velox::DummyStatsReporter();
});
#endif

// Initialize singleton for the exception translator.
// NOTE: folly::Singleton enforces that only ONE registration per type can
// exist. If another file tries to register VeloxToPrestoExceptionTranslator
// again, the program will fail during static initialization with a duplicate
// registration error. Extended servers should register a DERIVED class instead:
// folly::Singleton<VeloxToPrestoExceptionTranslator> customTranslator([]() {
// return new CustomExceptionTranslator(); // derived class
// });
folly::Singleton<facebook::presto::VeloxToPrestoExceptionTranslator>
exceptionTranslator([]() {
return new facebook::presto::VeloxToPrestoExceptionTranslator();
});
4 changes: 2 additions & 2 deletions presto-native-execution/presto_cpp/main/PrestoTask.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -97,9 +97,9 @@ protocol::ExecutionFailureInfo toPrestoError(std::exception_ptr ex) {
try {
rethrow_exception(ex);
} catch (const VeloxException& e) {
return VeloxToPrestoExceptionTranslator::translate(e);
return translateToPrestoException(e);
} catch (const std::exception& e) {
return VeloxToPrestoExceptionTranslator::translate(e);
return translateToPrestoException(e);
}
}

Expand Down
242 changes: 142 additions & 100 deletions presto-native-execution/presto_cpp/main/common/Exception.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -16,105 +16,149 @@

namespace facebook::presto {

std::unordered_map<
std::string,
std::unordered_map<std::string, protocol::ErrorCode>>&
VeloxToPrestoExceptionTranslator::translateMap() {
static std::unordered_map<
std::string,
std::unordered_map<std::string, protocol::ErrorCode>>
errorMap = {
{velox::error_source::kErrorSourceRuntime,
{{velox::error_code::kMemCapExceeded,
{0x00020007,
"EXCEEDED_LOCAL_MEMORY_LIMIT",
protocol::ErrorType::INSUFFICIENT_RESOURCES}},

{velox::error_code::kMemAborted,
{0x00020000,
"GENERIC_INSUFFICIENT_RESOURCES",
protocol::ErrorType::INSUFFICIENT_RESOURCES}},

{velox::error_code::kSpillLimitExceeded,
{0x00020006,
"EXCEEDED_SPILL_LIMIT",
protocol::ErrorType::INSUFFICIENT_RESOURCES}},

{velox::error_code::kMemArbitrationFailure,
{0x00020000,
"MEMORY_ARBITRATION_FAILURE",
protocol::ErrorType::INSUFFICIENT_RESOURCES}},

{velox::error_code::kMemArbitrationTimeout,
{0x00020000,
"GENERIC_INSUFFICIENT_RESOURCES",
protocol::ErrorType::INSUFFICIENT_RESOURCES}},

{velox::error_code::kMemAllocError,
{0x00020000,
"GENERIC_INSUFFICIENT_RESOURCES",
protocol::ErrorType::INSUFFICIENT_RESOURCES}},

{velox::error_code::kInvalidState,
{0x00010000,
"GENERIC_INTERNAL_ERROR",
protocol::ErrorType::INTERNAL_ERROR}},

{velox::error_code::kGenericSpillFailure,
{0x00010023,
"GENERIC_SPILL_FAILURE",
protocol::ErrorType::INTERNAL_ERROR}},

{velox::error_code::kUnreachableCode,
{0x00010000,
"GENERIC_INTERNAL_ERROR",
protocol::ErrorType::INTERNAL_ERROR}},

{velox::error_code::kNotImplemented,
{0x00010000,
"GENERIC_INTERNAL_ERROR",
protocol::ErrorType::INTERNAL_ERROR}},

{velox::error_code::kUnknown,
{0x00010000,
"GENERIC_INTERNAL_ERROR",
protocol::ErrorType::INTERNAL_ERROR}},

{presto::error_code::kExceededLocalBroadcastJoinMemoryLimit,
{0x0002000C,
"EXCEEDED_LOCAL_BROADCAST_JOIN_MEMORY_LIMIT",
protocol::ErrorType::INSUFFICIENT_RESOURCES}}}},

{velox::error_source::kErrorSourceUser,
{{velox::error_code::kInvalidArgument,
{0x00000000,
"GENERIC_USER_ERROR",
protocol::ErrorType::USER_ERROR}},
{velox::error_code::kUnsupported,
{0x0000000D, "NOT_SUPPORTED", protocol::ErrorType::USER_ERROR}},
{velox::error_code::kUnsupportedInputUncatchable,
{0x0000000D, "NOT_SUPPORTED", protocol::ErrorType::USER_ERROR}},
{velox::error_code::kArithmeticError,
{0x00000000,
"GENERIC_USER_ERROR",
protocol::ErrorType::USER_ERROR}},
{velox::error_code::kSchemaMismatch,
{0x00000000,
"GENERIC_USER_ERROR",
protocol::ErrorType::USER_ERROR}},
{velox::error_code::kInvalidArgument,
{0x00000000,
"GENERIC_USER_ERROR",
protocol::ErrorType::USER_ERROR}}}},

{velox::error_source::kErrorSourceSystem, {}}};
return errorMap;
VeloxToPrestoExceptionTranslator::VeloxToPrestoExceptionTranslator() {
// Register runtime errors
registerError(
velox::error_source::kErrorSourceRuntime,
velox::error_code::kMemCapExceeded,
{.code = 0x00020007,
.name = "EXCEEDED_LOCAL_MEMORY_LIMIT",
.type = protocol::ErrorType::INSUFFICIENT_RESOURCES});

registerError(
velox::error_source::kErrorSourceRuntime,
velox::error_code::kMemAborted,
{.code = 0x00020000,
.name = "GENERIC_INSUFFICIENT_RESOURCES",
.type = protocol::ErrorType::INSUFFICIENT_RESOURCES});

registerError(
velox::error_source::kErrorSourceRuntime,
velox::error_code::kSpillLimitExceeded,
{.code = 0x00020006,
.name = "EXCEEDED_SPILL_LIMIT",
.type = protocol::ErrorType::INSUFFICIENT_RESOURCES});

registerError(
velox::error_source::kErrorSourceRuntime,
velox::error_code::kMemArbitrationFailure,
{.code = 0x00020000,
.name = "MEMORY_ARBITRATION_FAILURE",
.type = protocol::ErrorType::INSUFFICIENT_RESOURCES});

registerError(
velox::error_source::kErrorSourceRuntime,
velox::error_code::kMemArbitrationTimeout,
{.code = 0x00020000,
.name = "GENERIC_INSUFFICIENT_RESOURCES",
.type = protocol::ErrorType::INSUFFICIENT_RESOURCES});

registerError(
velox::error_source::kErrorSourceRuntime,
velox::error_code::kMemAllocError,
{.code = 0x00020000,
.name = "GENERIC_INSUFFICIENT_RESOURCES",
.type = protocol::ErrorType::INSUFFICIENT_RESOURCES});

registerError(
velox::error_source::kErrorSourceRuntime,
velox::error_code::kInvalidState,
{.code = 0x00010000,
.name = "GENERIC_INTERNAL_ERROR",
.type = protocol::ErrorType::INTERNAL_ERROR});

registerError(
velox::error_source::kErrorSourceRuntime,
velox::error_code::kGenericSpillFailure,
{.code = 0x00010023,
.name = "GENERIC_SPILL_FAILURE",
.type = protocol::ErrorType::INTERNAL_ERROR});

registerError(
velox::error_source::kErrorSourceRuntime,
velox::error_code::kUnreachableCode,
{.code = 0x00010000,
.name = "GENERIC_INTERNAL_ERROR",
.type = protocol::ErrorType::INTERNAL_ERROR});

registerError(
velox::error_source::kErrorSourceRuntime,
velox::error_code::kNotImplemented,
{.code = 0x00010000,
.name = "GENERIC_INTERNAL_ERROR",
.type = protocol::ErrorType::INTERNAL_ERROR});

registerError(
velox::error_source::kErrorSourceRuntime,
velox::error_code::kUnknown,
{.code = 0x00010000,
.name = "GENERIC_INTERNAL_ERROR",
.type = protocol::ErrorType::INTERNAL_ERROR});

registerError(
velox::error_source::kErrorSourceRuntime,
presto::error_code::kExceededLocalBroadcastJoinMemoryLimit,
{.code = 0x0002000C,
.name = "EXCEEDED_LOCAL_BROADCAST_JOIN_MEMORY_LIMIT",
.type = protocol::ErrorType::INSUFFICIENT_RESOURCES});

// Register user errors
registerError(
velox::error_source::kErrorSourceUser,
velox::error_code::kInvalidArgument,
{.code = 0x00000000,
.name = "GENERIC_USER_ERROR",
.type = protocol::ErrorType::USER_ERROR});

registerError(
velox::error_source::kErrorSourceUser,
velox::error_code::kUnsupported,
{.code = 0x0000000D,
.name = "NOT_SUPPORTED",
.type = protocol::ErrorType::USER_ERROR});

registerError(
velox::error_source::kErrorSourceUser,
velox::error_code::kUnsupportedInputUncatchable,
{.code = 0x0000000D,
.name = "NOT_SUPPORTED",
.type = protocol::ErrorType::USER_ERROR});

registerError(
velox::error_source::kErrorSourceUser,
velox::error_code::kArithmeticError,
{.code = 0x00000000,
.name = "GENERIC_USER_ERROR",
.type = protocol::ErrorType::USER_ERROR});

registerError(
velox::error_source::kErrorSourceUser,
velox::error_code::kSchemaMismatch,
{.code = 0x00000000,
.name = "GENERIC_USER_ERROR",
.type = protocol::ErrorType::USER_ERROR});
}

void VeloxToPrestoExceptionTranslator::registerError(
const std::string& errorSource,
const std::string& errorCode,
const protocol::ErrorCode& prestoErrorCode) {
auto& innerMap = errorMap_[errorSource];
auto [it, inserted] = innerMap.emplace(errorCode, prestoErrorCode);
VELOX_CHECK(
inserted,
"Duplicate errorCode '{}' for errorSource '{}' is not allowed. "
"Existing mapping: [code={}, name={}, type={}]",
errorCode,
errorSource,
it->second.code,
it->second.name,
static_cast<int>(it->second.type));
}

protocol::ExecutionFailureInfo VeloxToPrestoExceptionTranslator::translate(
const velox::VeloxException& e) {
const velox::VeloxException& e) const {
protocol::ExecutionFailureInfo error;
// Line number must be >= 1
error.errorLocation.lineNumber = e.line() >= 1 ? e.line() : 1;
error.errorLocation.columnNumber = 1;
error.type = e.exceptionName();
Expand All @@ -127,17 +171,15 @@ protocol::ExecutionFailureInfo VeloxToPrestoExceptionTranslator::translate(
msg << " " << e.additionalContext();
}
error.message = msg.str();
// Stack trace may not be available if stack trace capturing is disabled or
// rate limited.
if (e.stackTrace()) {
error.stack = e.stackTrace()->toStrVector();
}

const auto& errorSource = e.errorSource();
const auto& errorCode = e.errorCode();

auto itrErrorCodesMap = translateMap().find(errorSource);
if (itrErrorCodesMap != translateMap().end()) {
auto itrErrorCodesMap = errorMap_.find(errorSource);
if (itrErrorCodesMap != errorMap_.end()) {
auto itrErrorCode = itrErrorCodesMap->second.find(errorCode);
if (itrErrorCode != itrErrorCodesMap->second.end()) {
error.errorCode = itrErrorCode->second;
Expand All @@ -151,7 +193,7 @@ protocol::ExecutionFailureInfo VeloxToPrestoExceptionTranslator::translate(
}

protocol::ExecutionFailureInfo VeloxToPrestoExceptionTranslator::translate(
const std::exception& e) {
const std::exception& e) const {
protocol::ExecutionFailureInfo error;
error.errorLocation.lineNumber = 1;
error.errorLocation.columnNumber = 1;
Expand Down
66 changes: 55 additions & 11 deletions presto-native-execution/presto_cpp/main/common/Exception.h
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
*/
#pragma once

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

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

// Translates to Presto error from std::exceptions
static protocol::ExecutionFailureInfo translate(const std::exception& e);
VeloxToPrestoExceptionTranslator();

// Returns a reference to the error map containing mapping between
// velox error code and Presto errors defined in Presto protocol
static std::unordered_map<
std::string,
std::unordered_map<std::string, protocol::ErrorCode>>&
translateMap();
virtual ~VeloxToPrestoExceptionTranslator() = default;

virtual protocol::ExecutionFailureInfo translate(
const velox::VeloxException& e) const;

virtual protocol::ExecutionFailureInfo translate(
const std::exception& e) const;

// For testing purposes only - provides access to the error map
const ErrorCodeMap& testingErrorMap() const {
return errorMap_;
}

protected:
void registerError(
const std::string& errorSource,
const std::string& errorCode,
const protocol::ErrorCode& prestoErrorCode);

ErrorCodeMap errorMap_;
};

// Global inline function APIs to translate exceptions (returns
// ExecutionFailureInfo) Similar pattern to StatsReporter, but returns a value
// instead of recording
inline protocol::ExecutionFailureInfo translateToPrestoException(
const velox::VeloxException& e) {
const auto translator =
folly::Singleton<VeloxToPrestoExceptionTranslator>::try_get_fast();
VELOX_CHECK_NOT_NULL(
translator,
"VeloxToPrestoExceptionTranslator singleton must be registered");
return translator->translate(e);
}

inline protocol::ExecutionFailureInfo translateToPrestoException(
const std::exception& e) {
const auto translator =
folly::Singleton<VeloxToPrestoExceptionTranslator>::try_get_fast();
VELOX_CHECK_NOT_NULL(
translator,
"VeloxToPrestoExceptionTranslator singleton must be registered");
return translator->translate(e);
}
} // namespace facebook::presto
Loading
Loading