본문으로 건너뛰기
cccskills
무료GitHub에 공개

grain-periodic-task

Create scheduled background jobs that run on cron schedules

한국어 요약은 준비 중입니다. 원문 설명을 표시하고 있습니다.

설치 방법 보기

포함된 파일(1)

  • SKILL.md6.3 KB

SKILL.md(원문)

설치하기 전에 에이전트에게 전달되는 지시 내용을 확인할 수 있습니다.

Grain Periodic Task Pattern

Periodic tasks are scheduled background jobs that run on cron schedules. They use defperiodic to emit trigger events, and a corresponding defprocessor handles the actual work.

See Pattern Compendium section 1.4.1 for the authoritative reference.

Two-File Pattern

Periodic tasks are split into two parts:

  1. periodic_tasks.clj -- defperiodic emits a lightweight trigger event per tenant on schedule
  2. todo_processors.clj -- defprocessor subscribes to the trigger event and does the real work

The framework iterates over all tenants automatically. The defperiodic handler receives [tenant-id time] (NOT [context time]) and returns {:result/events [...]}. The framework appends the events per-tenant.

Periodic Task File (core/periodic_tasks.clj)

(ns ai.obney.orc.my-service.core.periodic-tasks
  "Scheduled trigger events for my-service."
  (:require [ai.obney.grain.event-store-v3.interface :refer [->event]]
            [ai.obney.grain.periodic-task.interface :refer [defperiodic]]))

(defperiodic :my-service send-reminders
  {:schedule {:cron "0 * * * *" :timezone "America/Chicago"}}
  "Emit reminder-check trigger for each tenant every hour."
  [_tenant-id time]
  {:result/events
   [(->event {:type :my-service/reminder-check-triggered
              :tags #{[:reminder-check (random-uuid)]}
              :body {:triggered-at (str time)}})]})

The generated var name is my-service-send-reminders.

Corresponding Processor (core/todo_processors.clj)

(ns ai.obney.orc.my-service.core.todo-processors
  (:require [ai.obney.orc.my-service.core.read-models :as rm]
            [ai.obney.grain.command-processor-v2.interface :as cp]
            [ai.obney.grain.time.interface :as time]
            [ai.obney.grain.todo-processor-v2.interface :refer [defprocessor]]
            [com.brunobonacci.mulog :as u]))

(defprocessor :my-service reminder-runner
  {:topics #{:my-service/reminder-check-triggered}}
  "Process pending reminders when trigger fires."
  [context]
  (let [items (rm/get-pending-items context)]
    (u/log ::reminder-runner :count (count items))
    (doseq [item items]
      (cp/process-command
       (assoc context :command {:command/id (random-uuid)
                                :command/timestamp (time/now)
                                :command/name :my-service/send-reminder
                                :item-id (:id item)})))
    {}))

Complete Example: Membership Billing

;; periodic_tasks.clj
(defperiodic :membership billing-check
  {:schedule {:cron "0 6 * * *" :timezone "America/Chicago"}}
  "Daily billing check trigger."
  [_tenant-id time]
  {:result/events [(->event {:type :membership/billing-check-triggered
                             :tags #{[:billing-check (random-uuid)]}
                             :body {:triggered-at (str time)}})]})

;; todo_processors.clj
(defprocessor :membership billing-runner
  {:topics #{:membership/billing-check-triggered}}
  "Process memberships due for billing."
  [context]
  (let [today (str (java.time.LocalDate/now))
        due (rm/get-memberships-due-for-billing context today)]
    (doseq [m due]
      (cp/process-command
       (assoc context :command {:command/id (random-uuid)
                                :command/timestamp (time/now)
                                :command/name :membership/initiate-billing
                                :membership-id (:membership-id m)})))
    {}))

Schedule Formats

FormatExampleDescription
Cron{:cron "0 * * * *" :timezone "America/Chicago"}Standard cron (minute hour day month weekday)

Common cron patterns:

  • "0 * * * *" -- every hour on the hour
  • "*/15 * * * *" -- every 15 minutes
  • "0 3 * * *" -- daily at 3 AM
  • "0 6 * * *" -- daily at 6 AM
  • "0 0 * * 1" -- weekly on Monday at midnight

CAS for Idempotency

When a trigger event must be processed at most once, use CAS inside the defprocessor:

(defprocessor :my-service digest-sender
  {:topics #{:my-service/digest-triggered}}
  "Send daily digest -- at most once per user per day."
  [context]
  (let [users (rm/get-users-needing-digest context)]
    (doseq [user users]
      (es/append (:event-store context)
                 {:tenant-id (:tenant-id context)
                  :events [(->event {:type :my-service/digest-sent
                                     :tags #{[:user (:user-id user)]}
                                     :body {:user-id (:user-id user)
                                            :date (str (java.time.LocalDate/now))}})]
                  :cas {:types #{:my-service/digest-sent}
                        :tags #{[:user (:user-id user)]}
                        :predicate-fn (fn [existing]
                                        (empty? (into [] existing)))}}))
    {}))

The CAS predicate checks that no :my-service/digest-sent event exists for this user with these tags. If another instance already emitted one, the append silently fails.

Auto-Registration and Wiring

Both defperiodic and defprocessor register in global registries automatically. No manual registry maps are needed.

Interface.clj only needs bare side-effect requires:

(ns ai.obney.orc.my-service.interface
  (:require [ai.obney.orc.my-service.core.commands]
            [ai.obney.orc.my-service.core.queries]
            [ai.obney.orc.my-service.core.todo-processors]   ;; bare require
            [ai.obney.orc.my-service.core.periodic-tasks]    ;; bare require
            [ai.obney.orc.my-service.core.read-models :as rm]))

;; No periodic-tasks or todo-processors re-export needed
(def get-item rm/get-item)

No manual wiring is needed. pt/start-periodic-triggers! discovers periodic tasks from the global registry. The control plane discovers todo processors the same way.

Reference Files

  • components/membership-service/src/.../core/periodic_tasks.clj -- Billing check trigger
  • components/scheduling-service/src/.../core/periodic_tasks.clj -- Scheduling triggers
  • components/user-service/src/.../core/periodic_tasks.clj -- User maintenance
  • docs/contributors/CONTRIBUTOR-GRAIN-PATTERNS.md section 1.4.1 -- Authoritative periodic task reference

리뷰

아직 리뷰가 없습니다. 사용해 본 소감을 남겨 주세요.

댓글

로그인 없이 쓸 수 있습니다. 사용 후기, 질문, 작동하지 않은 환경 등을 편하게 남겨 주세요.

아직 댓글이 없습니다. 첫 댓글을 남겨 보세요.

보내면 바로 공개됩니다. 이메일 주소나 전화번호 같은 개인 정보는 쓰지 마세요. URL은 2개까지입니다.

0 / 1,000자

이 양식은 reCAPTCHA로 보호되며 Google 개인정보처리방침 및 서비스 약관이 적용됩니다.

같은 저장소의 스킬

allium

무료

Give your AI agents something more useful than a prompt. Velocity through clarity.

한국어 요약은 준비 중입니다. 원문 설명을 표시하고 있습니다.

ObneyAI/orc222026년 10월 8일 업데이트

distill

무료

Extract an Allium specification from an existing codebase. Use when the user has existing code and wants to distil behaviour into a spec, reverse engineer a specification from implementation, generate a spec from code, turn implementation into a behavioural specification, or document what a codebase does in Allium terms.

한국어 요약은 준비 중입니다. 원문 설명을 표시하고 있습니다.

ObneyAI/orc222026년 10월 8일 업데이트

elicit

무료

Run a structured discovery session to build an Allium specification through conversation. Use when the user wants to create a new spec from scratch, elicit or gather requirements, capture domain behaviour, specify a feature or system, define what a system should do, or is describing functionality and needs help shaping it into a specification.

한국어 요약은 준비 중입니다. 원문 설명을 표시하고 있습니다.

ObneyAI/orc222026년 10월 8일 업데이트

Implement command handlers that validate state and emit events using the Grain framework

한국어 요약은 준비 중입니다. 원문 설명을 표시하고 있습니다.

ObneyAI/orc222026년 10월 8일 업데이트

Implement query handlers that read from event-sourced read models

한국어 요약은 준비 중입니다. 원문 설명을 표시하고 있습니다.

ObneyAI/orc222026년 10월 8일 업데이트

Build read models by reducing events into queryable state

한국어 요약은 준비 중입니다. 원문 설명을 표시하고 있습니다.

ObneyAI/orc222026년 10월 8일 업데이트

ObneyAI의 스킬 모두 보기

이 스킬의 문제 신고