diff --git a/core/app_server.py b/core/app_server.py index f38ab5947a..1d821ac8d0 100644 --- a/core/app_server.py +++ b/core/app_server.py @@ -11,7 +11,7 @@ import flask from flask import Response, make_response, url_for from flask_pydantic_spec import FlaskPydanticSpec -from psycopg2 import DatabaseError +from psycopg2 import DatabaseError, OperationalError from werkzeug.exceptions import HTTPException import core @@ -21,9 +21,10 @@ from core.model import Identifier from core.problem_details import * from core.service.logging.configuration import LogLevel +from core.util.http import RemoteIntegrationException from core.util.log import LoggerMixin from core.util.opds_writer import OPDSMessage -from core.util.problem_detail import ProblemDetail +from core.util.problem_detail import ProblemDetail, ProblemError if TYPE_CHECKING: from api.util.flask import PalaceFlask @@ -214,12 +215,13 @@ def handle(self, exception: Exception) -> Response | HTTPException: # By default, the error will be logged at log level ERROR. log_method = self.log.error - # Okay, it's not a database error. Turn it into a useful HTTP error - # response. - if hasattr(exception, "as_problem_detail_document"): - # This exception can be turned directly into a problem - # detail document. - document = exception.as_problem_detail_document(self.debug) + # Try to turn the exception into a useful HTTP error response. + if isinstance(exception, (RemoteIntegrationException, ProblemError)): + if isinstance(exception, RemoteIntegrationException): + document = exception.as_problem_detail_document(self.debug) + else: + document = exception.problem_detail + if not self.debug: document.debug_message = None else: @@ -234,6 +236,17 @@ def handle(self, exception: Exception) -> Response | HTTPException: # WARN. log_method = self.log.warning response = make_response(document.response) + elif isinstance(exception, OperationalError): + # This is an error, but it is probably unavoidable. Likely it was caused by + # the database dropping our connection which can happen then the database is + # restarted for maintenance. We'll log it at log level WARN. + log_method = self.log.warning + body = ( + tb + if self.debug + else "Service temporarily unavailable. Please try again later." + ) + response = make_response(body, 503, {"Content-Type": "text/plain"}) else: # There's no way to turn this exception into a problem # document. This is probably indicative of a bug in our