-
Notifications
You must be signed in to change notification settings - Fork 7
Description
Problem
What is the appropriate way to handle errors in Roc? There are several important factors weighing in on this decision
- We are not bound by JDBC style of error handling.
- Postgresql has explicit documentation around "error classes" that allows us to model a great many Server errors without the need to create
Exceptions - From point 2., there are 278 explicit types of errors Postgresql can emit, and 41 "error classes" that these 278 fall into.
- From point 3., these 41 Error Class codes can be further classified as "Error", "Fatal", "Panic", "Warning", "Notice", "Debug", "Info", "Log" to denote the expected action of a client.
- "Adhere to the style of the original" suggests that we should not stray too far from the
Twitter.Future.exceptionstyle of handling. - The
Resulttype is more or less undefined at this point - currently it just holds some information returned from the DB, but this isn't firm in the slightest and I expect it to change dramatically as the code continues to reveal itself.
Currently, we are modeling any Errors that happen in the client ( for example, decoding failures ) as Failures, and any Exceptions that occur on the postgresql side as PostgresqlErrors
abstract class Failure extends Exception // Client side problems
abstract class PostgresqlError // Server side problemsPossible Solutions
The "kill -9"
Perhaps the easiest of these, we create a simple type to model any error from a Postgresql database. Upon receipt of any error, we immediately close the connection and return a
case class PostgresqlError extends Exception
Future.exception(new PostgresqlError())The "all. the. errors." approach
sealed abstract class PostgresqlError // does NOT extend Exception
case class UndefinedColumn extends PostgresqlErrorUnder this approach, we explicitly model each Postgresql Error type. As the errors are a closed set ( i.e., there is a well defined set of errors and a well defined behavior if, for some reason, an error is returned that does not appear in this set), this is certainly a possibility. The connection would not be closed, and the Result type would have a way to model a possible error ( perhaps as a disjunction? ).
However, the enormous number of error types makes this challenging and error prone for any user.
The "adhere to JDBC style" approach
In this scenario, we would map Postgreql Errors to their corresponding JDBC Exceptions, and then simply bail out in the client code:
// some decoding has occurred above
val error = InvalidColumnReference
Future.exception(new java.sql.SQLException(error))The "let's just cut the baby in half" approach
In this scenario, we would create Error Types based off the severity level. Errors that do not affect the connection state would simply be errors, while errors that do affect the connection state would also be Exceptions, i.e.
sealed abstract class PostgresqlError
case class Error extends PostgresqlError // example, "no table named FOO exists"
case class Fatal extends PostgresqlError with Exception // example, admin shutdown initiated
case class Panic extends PostgresqlError with Exception // example, OOM
case class Debug extends PostgresqlErrorNon-exception types would be returned as the Result type (still not sure how though), while any connection closing errors would close the connection, and then bail out via
Future.exception(new Fatal())Thoughts on this? I'm kind of torn between the "shove everything into a Future.exception" and "model all error types".