Skip to content

Commit ed9cde1

Browse files
committed
Improve docs
1 parent 6f078df commit ed9cde1

File tree

16 files changed

+317
-393
lines changed

16 files changed

+317
-393
lines changed

.pre-commit-config.yaml

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -8,12 +8,12 @@ repos:
88
- id: end-of-file-fixer
99
- id: check-added-large-files
1010
args: ["--maxkb=1000"]
11+
- repo: https://github.com/astral-sh/ruff-pre-commit
12+
rev: v0.15.10
13+
hooks:
14+
- id: ruff-check
15+
- id: ruff-format
1116
# the below hooks are disabled for now
12-
# - repo: https://github.com/astral-sh/ruff-pre-commit
13-
# rev: v0.9.6
14-
# hooks:
15-
# - id: ruff
16-
# - id: ruff-format
1717
# - repo: local
1818
# hooks:
1919
# - id: ty

docs/user-guide.md

Lines changed: 117 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -36,15 +36,27 @@ In order to work with pygeoapi-prefect, you need to set up:
3636

3737
- a Prefect server;
3838
- pygeoapi;
39-
- at least one Prefect worker.
39+
- (if you want to support async execution) at least one Prefect worker.
4040

4141

4242
### Prefect server
4343

4444
After installation, you need to have a Prefect server running and to set some environment variables
4545
so that the Prefect Manager is able to communicate with it.
4646

47-
As the most basic setup, just start a local Prefect server
47+
As the most basic setup:
48+
49+
generate an env file for configuring Prefect, ensuring that you
50+
enable [persistence of results](https://docs.prefect.io/v3/advanced/results).
51+
52+
```shell
53+
cat << EOF > prefect-env.env
54+
PREFECT_API_URL=http://127.0.0.1:4200/api
55+
PREFECT_RESULTS_PERSIST_BY_DEFAULT=true
56+
EOF
57+
```
58+
59+
Then start a local Prefect server
4860

4961
=== "uv"
5062

@@ -79,6 +91,51 @@ server:
7991
The Prefect Manager accepts the following configuration parameters, all of which are optional.
8092
These should be specified as properties of the `server.manager` object:
8193

94+
- `enable_sync_job_execution: bool = False` - Whether to accept execution requests that run synchronously.
95+
96+
!!! NOTE
97+
98+
This setting is disabled by default and we recommend keeping it disabled - Executing syncronously risks
99+
overloading the server in case a large number of requestes is received.
100+
101+
If needed, the metadata of
102+
each processor is allowed to override this setting, making it possible to selectively enable sync
103+
execution for those processors in which it is a useful execution mode.
104+
105+
#### Controlling processor execution modes via pygeoapi config
106+
107+
By default, pygeoapi does not allow modying a processor's metadata in its configuration and assumes that
108+
all parameters are to be set together with the processor code. This has the disadvantage of making it
109+
impossible to toggle a processor's support for sync execution on and off without modifying the source code.
110+
In order to ease configuration, `PrefectManager` will look for a `job_control_options` configuration key on
111+
its own configuration file, which has the same meaning as the `jobControlOptions` metadata key, _i.e._ it
112+
should be a list of strings with at least one entry and can contain `sync-execute` and `async-execute` entries.
113+
114+
In the following example we disable sync mode in the manager but re-enable it in the `hello-world`
115+
processor configuration:
116+
117+
```yaml
118+
# pygeoapi configuration file
119+
server:
120+
manager:
121+
name: pygeoapi_prefect.PrefectManager
122+
enable_sync_job_execution: false
123+
resources:
124+
hello-world:
125+
type: process
126+
processor:
127+
name: HelloWorld
128+
job_control_options:
129+
- sync-execute
130+
```
131+
132+
133+
- `enable_async_job_execution: bool = True` - Whether to accept execution requests that run asynchronously.
134+
This is enabled by default and is the recommended way to operate. Async execution defers the scheduling of
135+
processor execution to the Prefect scheduler, allowing for more stable operation, as the scheduler can control
136+
when to execute jobs depending on the load. Similarly to the previous parameter, this can also be
137+
overridden on a per-processor basis.
138+
82139
- `use_deployment_for_sync_requests: bool = False` - Whether to call native pygeoapi processors via the
83140
Prefect deployment interface or not. If this is enabled:
84141

@@ -88,7 +145,11 @@ These should be specified as properties of the `server.manager` object:
88145
- This means that job execution is slower, as there is a temporal overhead that is introduced by
89146
the coordination that happens between the Prefect scheduler service and the Prefect worker
90147

91-
If this is disabled, then jobs are executed immediately and run in the same process as pygeoapi
148+
If this is disabled (the default), then jobs are executed immediately and run in the same process as
149+
pygeoapi.
150+
151+
Note that regardless of this being enabled or not, all pygeoapi processes execution requests are tracked by
152+
Prefect and can be monitored using the Prefect UI.
92153

93154
- `sync_job_execution_timeout_seconds: int = 60` - How much time to give a sync job to finish its
94155
processing before declaring it as failed
@@ -115,10 +176,62 @@ resources:
115176
name: HelloWorld
116177
```
117178

179+
The `PrefectManager` wraps these processors inside a Prefect [flow](https://docs.prefect.io/v3/concepts/flows) and
180+
is able to execute them by generating Prefect flow runs whenever an execution request is received. Execution can be
181+
either sync or async, as both types are supported by the Prefect manager.
182+
183+
184+
###### Sync execution
185+
186+
Whenever pygeoapi receives a sync processor execution request, such as:
187+
188+
```shell
189+
curl \
190+
-X POST \
191+
"http://localhost:5000/processes/hello-world/execution" \
192+
-H "Content-Type: application/json" \
193+
-d '{"inputs": {"name": "Joe"}}'
194+
```
195+
196+
This is turned into a Prefect flow run and is executed locally, in the same process as pygeoapi. In other words,
197+
the pygeoapi processor is wrapped as a Prefect flow and is run by the Prefect manager, using regular function calls.
198+
199+
Being managed by Prefect, this means.
200+
201+
202+
###### Async execution
203+
204+
When pygeoapi receives an async processor execution request, such as:
205+
206+
```shell
207+
curl \
208+
-X POST \
209+
"http://localhost:5000/processes/hello-world/execution" \
210+
-H "Content-Type: application/json" \
211+
-H "Prefer: respond-async" \
212+
-d '{"inputs": {"name": "Joe"}}'
213+
```
214+
215+
This is turned into a Prefect flow and is executed via the respective processor deployment
216+
118217

119218
##### custom prefect-aware processes
120219

121-
TBD
220+
If you prefer, you can write Prefect flows, deploy them using any of the multiple techniques supported by Prefect
221+
and then adapt them to run as pygeoapi processors.
222+
223+
In order to do so, your Prefect flows need to implement the
224+
[protocol](https://typing.python.org/en/latest/spec/protocol.html)
225+
defined in `pygeoapi_prefect.PygeoapiPrefectFlowProtocol`
226+
227+
```python
228+
from prefect import flow
229+
230+
@flow()
231+
def my_custom_flow()
232+
233+
```
234+
122235

123236
#### Launching pygeoapi
124237

example-config.yml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -18,8 +18,8 @@ server:
1818
url: https://tile.openstreetmap.org/{z}/{x}/{y}.png
1919
attribution: '&copy; <a href="https://openstreetmap.org/copyright">OpenStreetMap contributors</a>'
2020
manager:
21-
name: pygeoapi_prefect.manager.PrefectManager
22-
use_deployment_for_sync_requests: true
21+
name: pygeoapi_prefect.PrefectManager
22+
# use_deployment_for_sync_requests: false
2323
# name: TinyDB
2424
# connection: /tmp/pygeoapi-process-manager.db
2525
output_dir: /tmp/

pyproject.toml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,9 @@ prefect = "pygeoapi_prefect.cli:root"
3535
[tool.uv.sources]
3636
pygeoapi = { path = "../../pygeoapi", editable = true }
3737

38+
[tool.ty.analysis]
39+
allowed-unresolved-imports = ["pygeoapi.**"]
40+
3841
[build-system]
3942
requires = ["hatchling"]
4043
build-backend = "hatchling.build"

src/pygeoapi_prefect/__init__.py

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,5 @@
11
from .manager import PrefectManager
22
from .process import BasePrefectProcessor
3+
from .protocols import PygeoapiPrefectFlowProtocol
34

4-
__all__ = [
5-
"BasePrefectProcessor",
6-
"PrefectManager",
7-
]
5+
__all__ = ["BasePrefectProcessor", "PrefectManager", "PygeoapiPrefectFlowProtocol"]

src/pygeoapi_prefect/cli.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ def root(): ...
2424
@root.command(name="deploy-static")
2525
@click.option("-c", "--pygeoapi-config", type=Path, envvar="PYGEOAPI_CONFIG")
2626
def deploy_processors_locally(
27-
pygeoapi_config: Path,
27+
pygeoapi_config: Path,
2828
):
2929
"""Deploy pygeoapi processes via Prefect, locally."""
3030
with pygeoapi_config.open() as fh:

src/pygeoapi_prefect/examples/hi_prefect_world.py

Lines changed: 6 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
"""Example pygeoapi process"""
22

33
from prefect import (
4-
Flow,
54
flow,
65
get_run_logger,
76
task,
@@ -19,7 +18,6 @@
1918
OutputExecutionResultInternal,
2019
InternalProcessDescription,
2120
ProcessInput,
22-
ProcessJobControlOption,
2321
ProcessOutput,
2422
)
2523
from pygeoapi_prefect.process import BasePrefectProcessor
@@ -84,8 +82,8 @@ def generate_status_info(
8482
job_id: str, process_id: str, result_path: str, result_media_type: str
8583
):
8684
return JobStatusInfoInternal(
87-
jobID=job_id,
88-
processID=process_id,
85+
job_id=job_id,
86+
process_id=process_id,
8987
status=JobStatus.successful,
9088
generated_outputs={
9189
"result": OutputExecutionResultInternal(
@@ -107,19 +105,19 @@ class HiPrefectWorldProcessor(BasePrefectProcessor):
107105
"name": ProcessInput(
108106
title="Name",
109107
description="Some name you think is cool. It will be echoed back.",
110-
schema={"type": "string"},
108+
schema_={"type": "string"},
111109
keywords=["cool-name"],
112110
),
113111
"message": ProcessInput(
114112
title="Message",
115113
description="An optional additional message to be echoed to the world",
116-
schema={"type": "string"},
117-
minOccurs=0,
114+
schema_={"type": "string"},
115+
min_occurs=0,
118116
),
119117
},
120118
outputs={
121119
"result": ProcessOutput(
122-
schema={
120+
schema_={
123121
"type": "string",
124122
"contentMediaType": "text/plain",
125123
},

src/pygeoapi_prefect/examples/simple_prefect.py

Lines changed: 6 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,6 @@
1616
JobStatusInfoInternal,
1717
InternalProcessDescription,
1818
ProcessInput,
19-
ProcessJobControlOption,
2019
ProcessOutput,
2120
OutputExecutionResultInternal,
2221
)
@@ -54,8 +53,8 @@ def simple_flow(
5453
result_path = f"{job_id}/output-result.txt"
5554
file_system.write_path(result_path, result_value.encode("utf-8"))
5655
return JobStatusInfoInternal(
57-
jobID=job_id,
58-
processID=process_description.id,
56+
job_id=job_id,
57+
process_id=process_description.id,
5958
status=JobStatus.successful,
6059
generated_outputs={
6160
"result": OutputExecutionResultInternal(
@@ -81,19 +80,19 @@ class SimpleFlowProcessor(BasePrefectProcessor):
8180
"name": ProcessInput(
8281
title="Name",
8382
description="Some name you think is cool. It will be echoed back.",
84-
schema={"type": "string"},
83+
schema_={"type": "string"},
8584
keywords=["cool-name"],
8685
),
8786
"message": ProcessInput(
8887
title="Message",
8988
description="An optional additional message to be echoed to the world",
90-
schema={"type": "string"},
91-
minOccurs=0,
89+
schema_={"type": "string"},
90+
min_occurs=0,
9291
),
9392
},
9493
outputs={
9594
"result": ProcessOutput(
96-
schema={
95+
schema_={
9796
"type": "string",
9897
"contentMediaType": "text/plain",
9998
},

src/pygeoapi_prefect/exceptions.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
class PygeoapiPrefectError(Exception): ...
1+
class PygeoapiPrefectError(Exception): ...
22

33

44
class InvalidProcessorDefinitionError(PygeoapiPrefectError): ...

0 commit comments

Comments
 (0)