Skip to content

Commit

Permalink
[Ledger API error codes] Extracted common errors and groups [DPP-607] (
Browse files Browse the repository at this point in the history
…#10890)

* Extracted common error implementations
* ErrorGroups
* TransactionError hierarchy
* LedgerApiErrors
* PackageServiceError
* ProtoDeserializationError
* PruningServiceError
* SubmissionErrors

CHANGELOG_BEGIN
CHANGELOG_END

* Adapt extracted error groups and definitions to adhere to local tech stack:
* Use //ledger/error:error core API
* Use DAML SDK logging stack
* Manual rebase to latest changes from Canton

* Extracted RejectionGenerator from Canton

* Adapted RejectionGenerator
* Added //ledger/error:error to artifacts

* Pass correlationId to errors
* Pass parameters as implicits to shave some lines and improve readability

* Workaround for encountered Scala/JDK8 bug

* Addressed Ratko's review comments
  • Loading branch information
tudor-da authored Sep 16, 2021
1 parent 308f938 commit b6a6bf7
Show file tree
Hide file tree
Showing 16 changed files with 1,349 additions and 16 deletions.
1 change: 1 addition & 0 deletions daml-lf/validation/BUILD.bazel
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ da_scala_library(
visibility = [
"//compiler/scenario-service:__subpackages__",
"//daml-lf:__subpackages__",
"//ledger/participant-integration-api:__subpackages__",
],
deps = [
"//daml-lf/data",
Expand Down
35 changes: 34 additions & 1 deletion ledger/error/src/main/scala/com/daml/error/BaseError.scala
Original file line number Diff line number Diff line change
Expand Up @@ -66,8 +66,10 @@ trait BaseError extends LocationMixin {
* Do not use this to change the contract of the error categories. Non-retryable errors shouldn't
* be made retryable. Only use it for adjusting the retry intervals.
*/
private[error] def retryable: Option[ErrorCategoryRetry] = code.category.retryable
def retryable: Option[ErrorCategoryRetry] = code.category.retryable

/** Controls whether a `definite_answer` error detail is added to the gRPC status code */
def definiteAnswerO: Option[Boolean] = None
}

trait LocationMixin {
Expand Down Expand Up @@ -98,4 +100,35 @@ object BaseError {
}
.toMap
}

abstract class Impl(
correlationId: Option[String],
override val cause: String,
override val throwableO: Option[Throwable] = None,
)(implicit override val code: ErrorCode)
extends BaseError {

/** The logging context obtained when we created the error, usually passed in as implicit */
def loggingContext: LoggingContext

def logger: ContextualizedLogger

/** Flag to control if an error should be logged at creation
*
* Generally, we do want to log upon creation, except in the case of "nested" or combined errors,
* where we just nest the error but don't want it to be logged twice.
*/
def logOnCreation: Boolean = true

def log(): Unit = logWithContext(logger, correlationId)(loggingContext)

def asGrpcError: StatusRuntimeException = {
code.asGrpcError(this, logger, correlationId)(loggingContext)
}

// Automatically log the error on generation
if (logOnCreation) {
log()
}
}
}
12 changes: 12 additions & 0 deletions ledger/error/src/main/scala/com/daml/error/ErrorCategory.scala
Original file line number Diff line number Diff line change
Expand Up @@ -346,6 +346,18 @@ object ErrorCategory {
)
with ErrorCategory

@Deprecated
object IsAbortShouldBePrecondition
extends ErrorCategoryImpl(
grpcCode = Some(Code.ABORTED),
logLevel = Level.INFO,
retryable = None,
securitySensitive = false,
asInt = 14,
rank = 3,
)
with ErrorCategory

implicit val orderingErrorType: Ordering[ErrorCategory] = Ordering.by[ErrorCategory, Int](_.rank)

}
Expand Down
37 changes: 23 additions & 14 deletions ledger/error/src/main/scala/com/daml/error/ErrorCode.scala
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@

package com.daml.error

import com.daml.error.ErrorCode.{loggingValueToString, truncateResourceForTransport}
import com.daml.error.ErrorCode.{StatusInfo, loggingValueToString, truncateResourceForTransport}
import com.daml.logging.entries.LoggingValue
import com.daml.logging.{ContextualizedLogger, LoggingContext}
import io.grpc.Status.Code
Expand Down Expand Up @@ -63,7 +63,8 @@ abstract class ErrorCode(val id: String, val category: ErrorCategory)(implicit
def asGrpcError(err: BaseError, logger: ContextualizedLogger, correlationId: Option[String])(
loggingContext: LoggingContext
): StatusRuntimeException = {
val (codeInt, message, contextMap) = getStatusInfo(err, correlationId, logger)(loggingContext)
val StatusInfo(codeInt, message, contextMap, _) =
getStatusInfo(err, correlationId, logger)(loggingContext)

// Provide error id and context via ErrorInfo
val errInfoBuilder = com.google.rpc.ErrorInfo
Expand Down Expand Up @@ -119,7 +120,7 @@ abstract class ErrorCode(val id: String, val category: ErrorCategory)(implicit
// Build status
val statusBuilder = com.google.rpc.Status
.newBuilder()
.setCode(codeInt)
.setCode(codeInt.value())
.setMessage(message)

(Seq(errInfo) ++ retryInfo.toList ++ requestInfo.toList ++ resourceInfo)
Expand All @@ -133,6 +134,12 @@ abstract class ErrorCode(val id: String, val category: ErrorCategory)(implicit
new ErrorCode.ApiException(ex.getStatus, ex.getTrailers)
}

def formatContextAsString(contextMap: Map[String, String]): String =
contextMap.view
.filter(_._2.nonEmpty)
.map { case (k, v) => s"$k=$v" }
.mkString(", ")

/** log level of the error code
*
* Generally, the log level is defined by the error category. In rare cases, it might be overridden
Expand Down Expand Up @@ -170,26 +177,27 @@ abstract class ErrorCode(val id: String, val category: ErrorCategory)(implicit
}
}

private[error] def getStatusInfo(
def getStatusInfo(
err: BaseError,
correlationId: Option[String],
logger: ContextualizedLogger,
)(loggingContext: LoggingContext): (Int, String, Map[String, String]) = {
)(loggingContext: LoggingContext): StatusInfo = {
val message =
if (code.category.securitySensitive)
s"${BaseError.SECURITY_SENSITIVE_MESSAGE_ON_API}: ${correlationId.getOrElse("<no-correlation-id>")}"
else
code.toMsg(err.cause, correlationId)

val codeInt = category.grpcCode
.getOrElse {
logger.warn(s"Passing non-grpc error via grpc $id ")(loggingContext)
Code.INTERNAL
}
.value()

val contextMap =
getTruncatedContext(err, loggingContext) + ("category" -> category.asInt.toString)

(codeInt, message, contextMap)
StatusInfo(codeInt, message, contextMap, correlationId)
}

private[error] def getTruncatedContext(
Expand Down Expand Up @@ -235,12 +243,6 @@ abstract class ErrorCode(val id: String, val category: ErrorCategory)(implicit
}
Some(loggedAs ++ apiLevel)
}

private[error] def formatContextAsString(contextMap: Map[String, String]): String =
contextMap.view
.filter(_._2.nonEmpty)
.map { case (k, v) => s"$k=$v" }
.mkString(", ")
}

object ErrorCode {
Expand Down Expand Up @@ -269,8 +271,15 @@ object ErrorCode {
extends StatusRuntimeException(status, metadata)
with NoStackTrace

case class StatusInfo(
codeGrpc: io.grpc.Status.Code,
message: String,
contextMap: Map[String, String],
correlationId: Option[String],
)

/** Truncate resource information such that we don't exceed a max error size */
private[error] def truncateResourceForTransport(
def truncateResourceForTransport(
res: Seq[(ErrorResource, String)]
): Seq[(ErrorResource, String)] = {
res
Expand Down
6 changes: 5 additions & 1 deletion ledger/error/src/main/scala/com/daml/error/ErrorGroup.scala
Original file line number Diff line number Diff line change
Expand Up @@ -4,5 +4,9 @@
package com.daml.error

abstract class ErrorGroup()(implicit parent: ErrorClass) {
implicit val errorClass: ErrorClass = parent.extend(getClass.getSimpleName.replace("$", ""))
private val fullClassName: String = getClass.getName
// Hit https://github.com/scala/bug/issues/5425?orig=1 here: we cannot use .getSimpleName in deeply nested objects
// TODO error codes: Switch to using .getSimpleName when switching to JDK 9+
implicit val errorClass: ErrorClass =
parent.extend(fullClassName.substring(fullClassName.lastIndexOf("$")))
}
3 changes: 3 additions & 0 deletions ledger/participant-integration-api/BUILD.bazel
Original file line number Diff line number Diff line change
Expand Up @@ -39,10 +39,12 @@ compile_deps = [
"//daml-lf/transaction",
"//daml-lf/transaction:transaction_proto_java",
"//daml-lf/transaction:value_proto_java",
"//daml-lf/validation",
"//language-support/scala/bindings",
"//ledger-api/rs-grpc-akka",
"//ledger-api/rs-grpc-bridge",
"//ledger/caching",
"//ledger/error:error",
"//ledger/ledger-api-akka",
"//ledger/ledger-api-auth",
"//ledger/ledger-api-client",
Expand Down Expand Up @@ -85,6 +87,7 @@ compile_deps = [

scala_compile_deps = [
"@maven//:com_github_scopt_scopt",
"@maven//:org_typelevel_cats_core",
"@maven//:com_typesafe_akka_akka_actor",
"@maven//:com_typesafe_akka_akka_stream",
"@maven//:org_playframework_anorm_anorm",
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
// Copyright (c) 2021 Digital Asset (Switzerland) GmbH and/or its affiliates. All rights reserved.
// SPDX-License-Identifier: Apache-2.0

package com.daml.platform.apiserver.error

case class CorrelationId(id: Option[String]) extends AnyVal
case object CorrelationId {
def none: CorrelationId = CorrelationId(None)
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
// Copyright (c) 2021 Digital Asset (Switzerland) GmbH and/or its affiliates. All rights reserved.
// SPDX-License-Identifier: Apache-2.0

package com.daml.platform.apiserver.error

import com.daml.error.{ErrorClass, ErrorGroup}

object ErrorGroups {

private implicit val errorClass: ErrorClass = ErrorClass.root()

abstract class ProtoDeserializationErrorGroup extends ErrorGroup

object ParticipantErrorGroup extends ErrorGroup() {
abstract class PackageServiceErrorGroup extends ErrorGroup()
abstract class PruningServiceErrorGroup extends ErrorGroup()
object TransactionErrorGroup extends ErrorGroup() {
// Errors emitted by Ledger Api server
abstract class LedgerApiErrorGroup extends ErrorGroup()
// TransactionSubmissionErrors are routing errors resulting from the transaction processor
abstract class SubmissionErrorGroup extends ErrorGroup()
}
}
}
Loading

0 comments on commit b6a6bf7

Please sign in to comment.