prefect

框架

PrefectHQ/prefect

用于构建弹性数据管道的 Python 工作流编排框架。

项目简介

Prefect 是一个用于构建弹性数据管道的 Python 工作流编排框架。通过装饰器将脚本转化为生产级工作流,支持调度、缓存、重试和事件驱动自动化。可通过自托管服务器或托管云面板监控工作流活动。

README 预览

\n\n\n    \n        \n    \n    \n        \n    \n    \n        \n    \n    \n        \n    \n    \n    \n        \n    \n    \n        \n    \n\n\n\n\n    \n        Installation\n    \n    ·\n    \n        Quickstart\n    \n    ·\n    \n        Build workflows\n    \n    ·\n    \n        Deploy workflows\n    \n    ·\n    \n        Prefect Cloud\n    \n\n\n# Prefect\n\nPrefect is a workflow orchestration framework for building data pipelines in Python.\nIt's the simplest way to elevate a script into a production workflow.\nWith Prefect, you can build resilient, dynamic data pipelines that react to the world around them and recover from unexpected changes.\n\nWith just a few lines of code, data teams can confidently automate any data process with features such as scheduling, caching, retries, and event-based automations.\n\nWorkflow activity is tracked and can be monitored with a self-hosted [Prefect server](https://docs.prefect.io/latest/manage/self-host/?utm_source=oss&utm_medium=oss&utm_campaign=oss_gh_repo&utm_term=none&utm_content=none) instance or managed [Prefect Cloud](https://www.prefect.io/cloud-vs-oss?utm_source=oss&utm_medium=oss&utm_campaign=oss_gh_repo&utm_term=none&utm_content=none) dashboard.\n\n> [!TIP]\n> Prefect flows can handle retries, dependencies, and even complex branching logic\n> \n> [Check our docs](https://docs.prefect.io/v3/get-started/index?utm_source=oss&utm_medium=oss&utm_campaign=oss_gh_repo&utm_term=none&utm_content=none) or see the example below to learn more!\n\n## Getting started\n\nPrefect requires Python 3.10+. To [install the latest version of Prefect](https://docs.prefect.io/v3/get-started/install), run one of the following commands:\n\n```bash\npip install -U prefect\n```\n\n```bash\nuv add prefect\n```\n\nThen create and run a Python file that uses Prefect `flow` and `task` decorators to orchestrate and observe your workflow - in this case, a simple script that fetches the number of GitHub stars from a repository:\n\n```pyt

常见问题 (5)

故障排除
如何修复 Prefect 的 schema 验证问题:根据嵌套属性顺序的不同,`None` 有时会通过验证,有时会失败并报错 "None is not of type 'string'"?

这是由 prefect.utilities.schema_tools.validation.process_properties() 中的一个 bug 引起的(已在 main 分支提交 148e9978 上的 Prefect 3.8.7.dev6 中复现)。该循环将 required_fields 重新绑定到嵌套对象的 required 列表,导致后续的同级属性会针对错误的列表进行验证。递归调用也未能转发 allow_none_with_default。

请升级到包含 issue #23189 修复的 Prefect 版本。如需从 main 分支进行开发安装:

python -m pip install --upgrade "prefect @ git+https://github.com/PrefectHQ/prefect.git"

如果你直接修补源代码,请为嵌套的 required 字段使用单独的变量,而不要重新赋值 required_fields:

if "properties" in schema:
nested_required = schema.get("required", [])
process_properties(
schema["properties"],
nested_required,
allow_none_with_default=allow_none_with_default,
)

不要在属性循环内使用 required_fields = schema.get("required", [])。

临时解决方法:在每个 schema 层级中,将嵌套对象属性放在所有同级属性之后;对于带默认值的嵌套字段,不要依赖 allow_none_with_default——请发送显式的默认值,或者仅在实际允许 None 时才使用 "type": ["string", "null"] 将该字段声明为可空。

来源 Issue #23189
故障排除
如何防止 Prefect runner 的 liveness probes 在 Prefect Cloud 维护期间重启 served flows?

此问题影响 Prefect 3.8.8。在 Cloud 维护期间,API 调用会返回 503,并附带 Prefect-Maintenance: true 和 Retry-After 响应头,因此 runner 会退避轮询。随后,其 /health 端点会在 PREFECT_RUNNER_SERVER_MISSED_POLLS_TOLERANCE × PREFECT_RUNNER_POLL_FREQUENCY(默认为 2 × 10 = 20 秒)之后返回 503,并且可能在首次轮询完成之前返回 500,从而导致存活探针重启 runner。

**永久修复:** 将 Prefect 升级到包含针对 issue #23326 的 runner 维护健康检查修复的版本:

pip install -U prefect

在已修复的版本中,当客户端遵循维护退避时,/health 会保持健康状态,并且在首次成功轮询之前不再报错。

**受影响版本的变通方案:** 将错过轮询的阈值设置为长于预期的维护窗口:

export PREFECT_RUNNER_POLL_FREQUENCY=10
export PREFECT_RUNNER_SERVER_MISSED_POLLS_TOLERANCE=60

这样可以在 /health 报告不健康之前允许 600 秒(60 × 10)的时间。

对于 Kubernetes,还应让探针容忍相同的时间窗口,并为在维护期间启动的进程使用启动探针:

livenessProbe:
  httpGet:
    path: /health
    port: 8080
  periodSeconds: 10
  failureThreshold: 60
  timeoutSeconds: 5
startupProbe:
  httpGet:
    path: /health
    port: 8080
  periodSeconds: 10
  failureThreshold: 60

启动探针很重要,因为 flow.serve()/serve() 会在 runner Web 服务器启动之前注册部署,因此当启动过程正在等待维护结束时,/health 可能会拒绝连接。

来源 Issue #23326
故障排除
为什么 `prefect deploy` 在 Windows 上的流程选择器中显示反斜杠路径?

flow picker 使用 str(path) 将 pathlib.Path 转换为字符串,这会使用 Windows 原生的反斜杠。Entrypoint 应使用 POSIX 风格的正斜杠,例如 flows/hello.py:my_flow。

修复方法:在 prefect/_internal/ast_utils.py 中的 find_prefect_decorated_functions_in_file 函数内,将:

"filepath": str(path)

替换为:

"filepath": path.as_posix()

正斜杠路径在 Windows 上可以正常工作,并能生成预期的 path/to/file.py:function_name entrypoint 格式。

临时解决方法:
1. 选择手动 entrypoint 选项,输入使用正斜杠的路径:path/to/file.py:function_name。
2. 如果反斜杠已保存到 prefect.yaml 中,请手动将其替换为正斜杠。
3. 将 Prefect 升级到包含 POSIX 路径修复的版本。

来源 Issue #23309
故障排除
如何防止 Prefect worker 的存活探针在 Prefect Cloud 维护期间重启 worker?

在 Prefect 3.8.7 中,Cloud 维护响应(带有 Prefect-Maintenance: true 和 Retry-After 的 503)会阻塞 worker 轮询。大约 300 秒后(PREFECT_WORKER_QUERY_SECONDS × 30),/health 返回 503,导致 Kubernetes 重启 worker。重启后,在 setup() 完成之前健康检查端口也不可用,从而产生反复的探针失败。

使用官方 prefect-worker Helm chart 的临时解决办法:在已知的维护窗口期间禁用 liveness probe:

livenessProbe:
  enabled: false

使用以下命令应用:

helm upgrade worker prefect/prefect-worker -f values.yaml

或者:

helm upgrade worker prefect/prefect-worker --set livenessProbe.enabled=false

维护窗口结束后重新启用该探针。增加探针阈值或轮询间隔并不可靠,因为 worker 处于维护退避(maintenance back-off)状态的时间可能超过任何固定阈值。

永久修复方案:升级到包含针对 worker 的 PR #23230 的 Prefect 版本。该修复在评估健康状态时会跟踪维护退避的结束时间,并在 setup() 之前启动健康检查服务器,因此 /health 在维护期间保持可用。使用 Runner 或 flow.serve() 的部署还需要单独的 runner 健康检查后续修复。

来源 Issue #23234
故障排除
如何修复使用 --limit 时 Prefect worker 崩溃并提示 "this borrower is already holding one of this CapacityLimiter's tokens" 的问题?

此问题影响 3.8.7 和 3.8.8 等版本的 Prefect worker。一次轮询可能会返回 worker 已在执行的 flow run。submission ID 在基础设施启动后会被清除,但 CapacityLimiter 令牌会一直被持有,直到运行退出。worker 只捕获 anyio.WouldBlock,因此重复令牌引发的 RuntimeError 会终止其轮询循环。

首选修复方案:升级到包含 issue #23340 修复的 Prefect 补丁版本,然后重启 worker:

python -m pip install -U prefect
prefect version

在补丁版本中,重复提交会被记录并跳过,而不会导致 worker 崩溃。

如果无法升级,可采用以下临时变通方案:

1. 重启已崩溃的 worker 以恢复轮询。
2. 从 worker 命令中移除 --limit。这样可以绕开限制器,但也会移除本地并发上限。
3. 作为源代码补丁,在 prefect/workers/base.py 的 _submit_scheduled_flow_runs 中包裹限制器的获取操作,以便跳过重复令牌:

try:
    self._limiter.acquire_on_behalf_of_nowait(flow_run.id)
except RuntimeError as exc:
    if "already holding one of this CapacityLimiter's tokens" in str(exc):
        logger.info(
            "Duplicate submission of flow run %s detected; skipping",
            flow_run.id,
        )
        continue
    raise
except anyio.WouldBlock:
    # Keep the existing WouldBlock handling here
    ...

应用补丁后重启 worker。

来源 Issue #23340