Implement a startup script which runs once on microservice start.

This commit is contained in:
Seán Healy
2026-05-08 20:45:49 +01:00
parent 13b82d66c6
commit c823a73bc4
6 changed files with 246 additions and 21 deletions

View File

@@ -9,6 +9,9 @@ assumptions about a microservice:
1. A microservice listens to incoming requests on its own dedicated and
singular queue (RabbitMQ).
2. Incoming requests are in the form of a 64-bit unsigned integer (`u64`).
2. Microservices can run optional startup work via a `startup` function, which
takes three arguments: a `read_file` function, a `write_file` function, and
a database ORM `connection`.
2. Microservices process requests via a `process` function, which takes four
arguments: the incoming request (`u64`), a `read_file` function, a
`write_file` function, and a database ORM `connection`.
@@ -85,6 +88,15 @@ from slingshot_microservice.typing import ReadFileFn, WriteFileFn
from slingshot_microservice import Microservice
def startup(
read_file: ReadFileFn,
write_file: WriteFileFn,
connection: Connection,
) -> Generator[tuple[int, bool | int | str], None, None]:
if False:
yield (0, True)
def process(
request: int,
read_file: ReadFileFn,
@@ -100,7 +112,12 @@ def process(
yield (request, True)
microservice = Microservice("simple-py-microservice", "sys-map.slingshot.cv", process)
microservice = Microservice(
"simple-py-microservice",
"sys-map.slingshot.cv",
startup,
process,
)
microservice.start()
```
@@ -113,7 +130,8 @@ editors and type-checkers:
|---|---|
| `ReadFileFn` | Callable returned by `read_file(key, id)` behaves like `BinaryIO` |
| `WriteFileFn` | Callable returned by `write_file(key, id)` behaves like `BinaryIO` |
| `ProcessFn` | The generator signature expected by `Microservice` with `(request, read_file, write_file, connection)` |
| `StartupFn` | The generator signature expected by `Microservice` startup hook with `(read_file, write_file, connection)` |
| `ProcessFn` | The generator signature expected by `Microservice` process callback with `(request, read_file, write_file, connection)` |
| `CaseVariable` | `bool \| int \| str` valid case variable types |
### Publishing Wheels
@@ -154,11 +172,20 @@ fn process(
Ok(vec![(request, "case_a".to_string())])
}
fn startup(
read_file: &ReadFileFn,
write_file: &WriteFileFn,
connection: &mut PgConnection,
) -> Result<Vec<(u64, String)>, AnyError> {
Ok(Vec::new())
}
fn main() {
// Create a new microservice instance with the processing function
// Create a new microservice instance with startup and process functions
let microservice = Microservice::new(
"simple-microservice",
"sys-map.example.com",
startup,
process
);
@@ -220,7 +247,11 @@ actual secrets with `pass show <key>` before constructing the S3 client.
When the microservice first starts up, it makes a request to the configuration
service to get the queue metadata. Then it starts to listen to the inbound
queue. Inbound requests are processed by the user-programmed `process`
queue. Before consuming inbound requests, the user-programmed `startup`
function runs once with `(read_file, write_file, connection)` and may return
zero or more `(result_id, case_variable)` tuples for normal outbound routing.
Inbound requests are then processed by the user-programmed `process`
function, which is called with `(request, read_file, write_file, connection)`
and returns a set of tuples of the form `(result_id, case_variable)`.
@@ -247,6 +278,10 @@ The output queue routing step looks like this:
Peudocode:
```
for each (result_id, case_variable) in startup(read_file, write_file, connection):
for each outbound_queue in config.out[case_variable]:
send result_id to outbound_queue
for each (result_id, case_variable) in process(request, read_file, write_file, connection):
for each outbound_queue in config.out[case_variable]:
send result_id to outbound_queue