mcpField
F
frappe-impl-scheduler

frappe-impl-scheduler

github.com/impertio-studio开发198111 次安装30 周前更新
代码仓库

关于 frappe-impl-scheduler

面向 Frappe 开发者的定时任务与后台作业实现指南,涵盖调度事件和 frappe.enqueue 的选择、配置、测试及监控,并提供错误隔离和长任务分批模式。

主要功能

  1. 01区分定时事件与后台队列
  2. 02选择事件频率和队列
  3. 03测试与监控作业运行
  4. 04处理批次错误与长任务
  5. 05提供版本差异及实现示例

适用场景

  1. 01配置周期性清理或同步任务
  2. 02将用户触发的耗时操作入队
  3. 03测试调度任务并检查失败记录

安装

复制下面的命令,在终端中运行。

$npx skills add https://github.com/impertio-studio/frappe_claude_skill_package --skill frappe-impl-scheduler

安装后在 Claude Code 中输入/frappe-impl-scheduler确认已加载。

包含的文件

除 SKILL.md 外,安装时一起下载的参考资料和脚本。

references/
anti-patterns.md
decision-tree.md
examples.md
workflows.md

详细介绍

Frappe Scheduler 与后台作业 - 实现

实现定时任务和后台作业的工作流。准确语法请参见 frappe-syntax-scheduler。

版本:兼容 v14/v15/v16


主要选择:scheduler_events 还是 frappe.enqueue

WHAT ARE YOU BUILDING?
|
+-- Runs at fixed intervals/times?
|   +-- YES --> scheduler_events (hooks.py)
|   |           Task receives NO arguments
|   |           See: Workflow 1-2
|   |
|   +-- NO --> Triggered by user action or code?
|              +-- YES --> frappe.enqueue()
|              |           Pass any serializable data
|              |           See: Workflow 3-4
|              |
|              +-- NO --> Reconsider requirements
方面scheduler_eventsfrappe.enqueue
触发方式时间/间隔代码执行
定义位置hooks.pyPython 代码
参数无(必须不带参数)任意可序列化数据
适用场景每日清理、每小时同步用户触发的长时间任务
队列控制事件后缀(_long)queue= 参数
重启行为按计划运行Worker 重启后会丢失

应使用哪种 Scheduler 事件类型?

需求事件键队列
每次 scheduler tickallshort(绝不可超过 60 秒)
每小时(<5 分钟)hourlyshort
每小时(5-25 分钟)hourly_longlong
每日(<5 分钟)dailyshort
每日(5-25 分钟)daily_longlong
每周(<5 分钟)weeklyshort
每周(5-25 分钟)weekly_longlong
每月(<5 分钟)monthlyshort
每月(5-25 分钟)monthly_longlong
自定义计划cron["expr"]short

规则:任务超过 5 分钟时,务必使用 *_long 后缀。


frappe.enqueue 应使用哪个队列?

队列默认超时适用场景
short300 秒(5 分钟)快速操作(<1 分钟)
default300 秒(5 分钟)标准任务(1-5 分钟)
long1500 秒(25 分钟)繁重处理(>5 分钟)

规则:务必显式指定 queue=。绝不要依赖默认值。


实现步骤 1:Scheduler 事件

# myapp/tasks.py
import frappe

def daily_cleanup():
    """Daily cleanup - NO parameters allowed."""
    cutoff = frappe.utils.add_days(frappe.utils.nowdate(), -30)
    frappe.db.delete("Error Log", {"creation": ("<", cutoff)})
    frappe.db.commit()
# hooks.py
scheduler_events = {
    "daily": ["myapp.tasks.daily_cleanup"]
}

编辑 hooks.py 后:务必运行 bench migrate。


实现步骤 2:后台作业(frappe.enqueue)

# myapp/api.py
import frappe
from frappe.utils.background_jobs import is_job_enqueued

@frappe.whitelist()
def process_documents(doctype, filters):
    job_id = f"process_{doctype}_{frappe.session.user}"

    if is_job_enqueued(job_id):
        return {"message": "Already in progress"}

    frappe.enqueue(
        "myapp.tasks.process_batch",
        queue="long",
        timeout=1800,
        job_id=job_id,
        enqueue_after_commit=True,
        doctype=doctype,
        filters=filters
    )
    return {"status": "queued"}

测试定时任务

方法 1:bench execute(直接执行)
# Run the function directly (no queue involved)
bench --site mysite execute myapp.tasks.daily_cleanup
方法 2:bench scheduler(完整的 scheduler 测试)
# Check scheduler status
bench --site mysite scheduler status

# Enable scheduler
bench --site mysite scheduler enable

# Trigger all pending scheduler events NOW
bench --site mysite scheduler trigger

# Run specific event type
bench --site mysite execute frappe.utils.scheduler.trigger --args "['daily']"
方法 3:bench console(交互式)
bench --site mysite console
>>> frappe.enqueue("myapp.tasks.my_task", queue="short", now=True)
# now=True executes synchronously for testing
方法 4:检查 Scheduled Job Type
1. Go to: Setup > Scheduled Job Type
2. Find: myapp.tasks.daily_cleanup
3. Verify: Frequency correct, Stopped = No
4. Click "Run Now" to trigger manually

监控

Scheduled Job Log(UI)
Setup > Scheduled Job Log
- Shows every scheduler run with status
- Filter by: status (Success/Failed), creation date
- Check execution time to detect slow tasks
RQ Dashboard
# Start RQ monitor (development)
bench --site mysite rq-dashboard
# Opens at http://localhost:9181

# Show background job status
bench --site mysite show-pending-jobs
bench --site mysite show-failed-jobs
程序化健康检查
def scheduler_health_check():
    failed = frappe.db.count("Scheduled Job Log", {
        "status": "Failed",
        "creation": [">=", frappe.utils.add_to_date(None, hours=-1)]
    })
    if failed > 5:
        frappe.sendmail(
            recipients=["[email protected]"],
            subject="Scheduler Alert: Many failures",
            message=f"{failed} scheduler jobs failed in last hour"
        )

定时任务中的错误处理

按记录隔离错误
def sync_all_orders():
    orders = get_pending_orders()
    success, errors = 0, 0

    for order in orders:
        try:
            sync_to_external(order)
            success += 1
        except Exception as e:
            errors += 1
            frappe.db.rollback()
            frappe.log_error(
                f"Sync failed for {order}: {e}",
                "Order Sync Error"
            )
    frappe.db.commit()
    frappe.logger("sync").info(f"{success} ok, {errors} errors")

规则:务必使用 try-except 包裹每条记录的处理。绝不要让单条记录的失败中断整个批次。


长时间运行作业模式

自我链式模式(任务超过 25 分钟)
def process_batch(offset=0, batch_size=500, total=None):
    if total is None:
        total = frappe.db.count("Sales Invoice", {"custom_processed": 0})

    records = frappe.get_all("Sales Invoice",
        filters={"custom_processed": 0},
        pluck="name", limit=batch_size)

    if not records:
        return  # Done

    for name in records:
        process_single(name)
    frappe.db.commit()

    remaining = frappe.db.count("Sales Invoice", {"custom_processed": 0})
    if remaining > 0:
        frappe.enqueue(
            "myapp.tasks.process_batch",
            queue="long",
            offset=offset + batch_size,
            batch_size=batch_size,
            total=total
        )

规则:任务超过 25 分钟时,务必拆分为自我链式批次。


常见实现模式

电子邮件摘要(每周汇总)
# hooks.py
scheduler_events = {
    "cron": {
        "0 8 * * 1": ["myapp.newsletter.send_weekly_digest"]
    }
}

完整实现请参见 references/examples.md 中的示例 4。

数据清理(每日维护)
scheduler_events = {
    "daily_long": ["myapp.maintenance.daily_database_maintenance"]
}

批量删除模式请参见 references/examples.md 中的示例 1。

报告生成(用户触发)
frappe.enqueue(
    "myapp.tasks.generate_report",
    queue="long",
    timeout=3600,
    job_id=f"report::{frappe.session.user}",
    user=frappe.session.user
)

进度报告请参见 references/workflows.md 中的工作流 6。


关键规则

  1. Scheduler 任务不接收任何参数——使用设置或硬编码值
  2. 修改 hooks.py 后务必运行 bench migrate——注册事件的必要步骤
  3. 作业以 Administrator 身份运行——务必显式提交
  4. 分批提交——绝不要逐条记录提交(每 100-500 条记录提交一次)
  5. 用户触发的作业务必使用 job_id——防止重复
  6. 在文档事件中使用 enqueue_after_commit=True——确保数据已存在
  7. Scheduler 事件应保持精简——将繁重工作放入后台队列

版本差异

方面v14v15v16
Tick 间隔240 秒60 秒60 秒
作业去重参数job_namejob_idjob_id
enqueue_doc()是是是
自定义队列否是是

参考文件

文件内容
workflows.md8 种分步实现模式
decision-tree.md详细的决策流程图
examples.md5 个完整可运行的示例
anti-patterns.md14 个需要避免的常见错误

另请参阅

  • frappe-syntax-scheduler - hooks 和 enqueue 的精确语法参考
  • frappe-errors-serverscripts - 错误处理模式
  • frappe-impl-hooks - Hook 配置模式
  • frappe-ops-bench - 用于管理 scheduler 的 Bench 命令
  • frappe-ops-performance - 后台作业性能调优
  • frappe-testing-unit - 测试计划任务逻辑

评论

支持 Markdown