High-level handlers MR1: Handler Executors
This MR is split out from the original High-level handlers MR !25 (closed), towards uis/devops/epics#321
These MRs are chained, with each MR targeting the previous MR's branch. They either need to be merged in order, or all at once from the final one (after changing it to target main).
The purpose of this MR is to add the foundational components that the higher-level handlers (coming in later MRs) build upon.
The higher-level handler functions we're adding over the course of these MRs have two main goals:
-
Automatically parse and validate incoming events, to give handler functions typed data in ready-to-use form.
- Developers using
ucam_faasshouldn't need to manually pick through event objects before they can start writing logic to handle their events.
- Developers using
-
Provide a consistent, low-friction way for handler functions to report their behaviour to observers.
- It should be clear whether a function failed or succeeded to handle an event, so that it can be automatically retried or have errors investigated manually
- It should be easy to test functions in development, without needing heavy integration test infrastructure
- It should be easy to report details of internal behaviour, to provide good operational observability in production
This MR is mostly concerned with the second goal, the later MRs provide the input parsing and validation functionality.
The primary part of this MR is the addition of the ucam_faas.handler_execution
module, which contains handler executor functions that provide the interface to
connect functions_framework to the ucam_faas handler functions.
The handler executor functions are the base level handlers to layer
higher-level, (more specific) handlers on top of. This module's handlers support
reporting handler function results using ExecutionInfo objects. The results
are made available in 3 ways:
- As HTTP responses to the HTTP request that triggered the event handler
- As structured (JSON) log records
- As the original
ExecutionInfovalue for unit tests (tests need not mock or use a fullfunctions_frameworkserver to run a function to test it)
ExecutionInfo
The ExecutionInfo type is central to realising the second goal. It's a
structured data object that contains properties to describe the execution of
handler functions in general — whether the execution succeeded or failed, how it
failed if it did, the event that triggered the execution.
It can be extended to contain information specific to a particular handler function, to describe domain-specific details. (For example, whether or not an action was taken as a result of the execution, which external things were affected, etc.)
It supports JSON serialisation, to allow the information to be transmitted in a structured JSON log record.
It supports automatic population of ExecutionInfo fields from the execution
environment of the handler function. For example, to automatically include
details of the event that triggered a function, without needing to manually
populate fields.
This design is intended to follow the Wide Events pattern;
A Practitioner's Guide to Wide Events
is a good starting point to get an overview of it. The idea is to have your
service send a single large event to describe a complete request/response cycle,
rather than lots of small logs along the way. The ExecutionInfo object is a
wide event, which you can populate with fields to describe the execution.
This gives you a complete picture of the execution in one object, which you can write unit tests against to assert about the behaviour of the function, or use to filter and visualise logs for operational observability.
Using the same object for both use cases should reduce developer effort compared to taking separate approaches to reporting for operational observability, reporting function success/failure for runtime error reporting, and introspecting behaviour for automated testing in development.
Issues
- This implements #31 (closed)
- This MR's Handler Executors support HTTP handlers, as described in #32. See:
- https://gitlab.developers.cam.ac.uk/uis/devops/lib/ucam-faas-python/-/blob/19-hlh-mr1-handler-executors/integration_tests/_handler_execution_http_handlers.py
- https://gitlab.developers.cam.ac.uk/uis/devops/lib/ucam-faas-python/-/blob/19-hlh-mr1-handler-executors/integration_tests/test_handler_execution.py#L112-126
- It doesn't provide a decorator to automatically register an HTTP handler, but this can be added later, in the same way that the
@cloud_event_handler_executordecorator (added in this MR) works.
- It also contributes towards most of the others, without fully implementing any specific issue