အမျိုးအစားနှစ်ခု
အလုပ် မည်သို့ run ရမည်နှင့် ကိုက်သော ပုံစံကို ရွေးချယ်ရပါသည်။
- Process cron (@CronJob) — @Service resolve ဖြစ်သောအခါ စတင်သော in-process schedule; Node cron package (ဤ process တွင်သာ)
- BullMQ cron (CronJobService) — Redis queue + Postgres (cron_jobs) metadata; retry၊ repeat limit၊ multi-instance ဘေးကင်းပါသည်
မည်သည့်အချိန်တွင် မည်သည်ကို သုံးမည်
- BullMQ CronJobService — domain sweep၊ reminder၊ reconcile၊ restart ခံနိုင်ပြီး instance အများနှင့် မှန်ကန်ရမည့် အလုပ်
- Process @CronJob — single process ပေါ်ရှိ ပေါ့ပါး housekeeping သို့မဟုတ် Redis မလိုသော in-memory tick
- အလုပ်အသစ်အတွက် @quan-erp-plugins/cron-schedular-backend မသုံးရပါ — တူညီသော API များသည် core တွင် CronJobService အဖြစ် ရှိပါသည်
1. Process cron (@CronJob)
DI-resolved @Service ပေါ်ရှိ method ကို decorate လုပ်ရပါသည်။ Class resolve ဖြစ်သောအခါ core က expression ဖြင့် process-local CronJob စတင်ပါသည်။ DB row / Redis / retry မရှိပါ — service load လုပ်သော process တိုင်း သီးခြား tick လုပ်ပါသည်။
- expression (လိုအပ်) — cron pattern၊ ဥပမာ 0 * * * *
- name (optional) — log အတွက် label
- Class သည် @Service() ဖြစ်ရပါမည် (သို့မဟုတ် DI-resolved) နှင့် module providers တွင် မှတ်ပုံတင်ရပါမည်
1import { CronJob, Module, Service } from "@quan-erp/shared-backend-core";
2import metadata from "../../module.metadata.json" with { type: "json" };
3
4@Service()
5export class LocalHousekeepingService {
6 @CronJob({ expression: "0 * * * *", name: "HourlyLocalCleanup" })
7 async cleanup() {
8 // runs on this Node process only
9 }
10}
11
12@Module({
13 name: metadata.name,
14 providers: [LocalHousekeepingService],
15 controllers: [],
16 entities: [],
17})
18export class MyPluginModule {}2. BullMQ cron (CronJobService)
Job များကို Redis ပေါ်ရှိ BullMQ မှ enqueue လုပ်ပါသည်။ Schedule metadata ကို Postgres (cron_jobs) တွင် သိမ်းပါသည်။ Boot တွင် active schedule များကို Redis သို့ ပြန် load လုပ်ပါသည်။ Callback များသည် in-memory သာဖြစ်သဖြင့် boot တိုင်း listener ပြန်တွဲရပါမည်။
CronJobService ကို inject လုပ်ပါ
Builtin core service ကို ContainerRegistryManager.BUILTIN_PLUGIN ဖြင့် inject လုပ်ရပါသည်။
1import {
2 ContainerRegistryManager,
3 CronJobService,
4 Inject,
5 Service,
6} from "@quan-erp/shared-backend-core";
7
8@Service()
9export class MaturedSweepService {
10 @Inject(CronJobService, ContainerRegistryManager.BUILTIN_PLUGIN)
11 private cronJobService: CronJobService;
12}BullMQ job မှတ်ပုံတင်ခြင်း
Unique (pluginName, jobName) pair ဖြင့် register ခေါ်ရပါသည်။ pluginName အဖြစ် metadata.name ကို အသုံးပြုရပါသည်။ register က DB row ကို upsert လုပ်ပြီး Redis scheduler ကို (ပြန်)ဖန်တီးပါသည်။
1import {
2 ContainerRegistryManager,
3 CronJobService,
4 Inject,
5 Service,
6} from "@quan-erp/shared-backend-core";
7import metadata from "../../module.metadata.json" with { type: "json" };
8
9@Service()
10export class MaturedSweepService {
11 @Inject(CronJobService, ContainerRegistryManager.BUILTIN_PLUGIN)
12 private cronJobService: CronJobService;
13
14 async registerJob() {
15 await this.cronJobService.register({
16 cronExpression: "0 2 * * *",
17 pluginName: metadata.name,
18 jobName: "matured-daily-sweep",
19 status: "active",
20 startDate: new Date(),
21 data: { reason: "daily-sweep" },
22 retry: { attempt: 3, delay: 5_000, type: "exponential" },
23 callback: async (job) => {
24 await this.runSweep(job.data);
25 },
26 });
27 }
28
29 private async runSweep(data: unknown) { /* … */ }
30}Restart ပြီးနောက် listener များ ပြန်တွဲပါ
Boot တွင် schedule များကို Postgres မှ Redis သို့ ပြန် load လုပ်ပါသည်။ Callback များကို မသိမ်းပါ — @OnAllModuleLoaded တွင် အမြဲ ပြန်မှတ်ပုံတင်ရပါသည် (သို့မဟုတ် register ဖြင့် callback ပြန်ပေးရပါသည်)။
1import {
2 ContainerRegistryManager,
3 CronJobService,
4 Inject,
5 OnAllModuleLoaded,
6 Service,
7} from "@quan-erp/shared-backend-core";
8import metadata from "../../module.metadata.json" with { type: "json" };
9
10@Service()
11export class MaturedSweepService {
12 @Inject(CronJobService, ContainerRegistryManager.BUILTIN_PLUGIN)
13 private cronJobService: CronJobService;
14
15 @OnAllModuleLoaded()
16 async onAllModuleLoaded() {
17 this.cronJobService.addEventListener(
18 metadata.name,
19 "matured-daily-sweep",
20 async (job) => { await this.runSweep(job.data); },
21 );
22 }
23
24 private async runSweep(data: unknown) { /* … */ }
25}Stop / remove (BullMQ)
- stop(pluginName, jobName) — scheduler ကို ခဏရပ်ပြီး inactive အဖြစ် မှတ်ပါသည်
- remove(pluginName, jobName) — Redis scheduler၊ DB row နှင့် listener များကို ဖျက်ပါသည်
- stopAllByPluginName / removeAllByPluginName — uninstall အတွက် bulk helper များ
Query helpers
1import {
2 ContainerRegistryManager,
3 CronJobService,
4 Inject,
5 Service,
6} from "@quan-erp/shared-backend-core";
7import metadata from "../../module.metadata.json" with { type: "json" };
8
9@Service()
10export class MaturedSweepService {
11 @Inject(CronJobService, ContainerRegistryManager.BUILTIN_PLUGIN)
12 private cronJobService: CronJobService;
13
14 async listJobs() {
15 const job = await this.cronJobService.getByPluginNameWithJobName(
16 metadata.name,
17 "matured-daily-sweep",
18 );
19
20 const jobs = await this.cronJobService.getByPluginName({
21 pluginName: metadata.name,
22 skip: 0,
23 limit: 50,
24 status: "active",
25 });
26
27 return { job, jobs };
28 }
29}CreateCronJobDTO
- cronExpression (လိုအပ်) — ဥပမာ 0 2 * * *
- pluginName / jobName (လိုအပ်) — key သည် `${pluginName}/${jobName}`
- status — active | inactive
- data — BullMQ job.data ပေါ်ရှိ payload
- startDate — အစောဆုံး စတင်ချိန် (initial delay)
- repeat.limit — အများဆုံး အကြိမ်ရေ
- retry.attempt / delay / type (fixed | exponential)
- callback — register အချိန်တွင် optional in-memory listener
အကြံပြု BullMQ pattern
- 01
Inject
ContainerRegistryManager.BUILTIN_PLUGIN ဖြင့် CronJobService။
- 02
Boot တွင် မှတ်ပုံတင်ခြင်း
@OnAllModuleLoaded တွင် register({ pluginName: metadata.name, jobName, cronExpression, … })။
- 03
Boot တိုင်း listener တွဲပါ
register ပေါ်တွင် callback နှင့်/သို့မဟုတ် addEventListener — schedule သည် restart ကို ခံနိုင်သော်လည်း handler မခံနိုင်ပါ။
- 04
Cleanup
Uninstall / disable တွင် orphan Redis scheduler မကျန်အောင် stop သို့မဟုတ် remove လုပ်ရပါသည်။
1import {
2 ContainerRegistryManager,
3 CronJobService,
4 Inject,
5 OnAllModuleLoaded,
6 Service,
7} from "@quan-erp/shared-backend-core";
8import metadata from "../../module.metadata.json" with { type: "json" };
9
10const JOB_NAME = "inventory-nightly-reconcile";
11
12@Service()
13export class InventoryReconcileCronService {
14 @Inject(CronJobService, ContainerRegistryManager.BUILTIN_PLUGIN)
15 private cronJobService: CronJobService;
16
17 @OnAllModuleLoaded()
18 async onAllModuleLoaded() {
19 await this.cronJobService.register({
20 cronExpression: "0 3 * * *",
21 pluginName: metadata.name,
22 jobName: JOB_NAME,
23 startDate: new Date(),
24 callback: async () => { await this.reconcile(); },
25 });
26 }
27
28 private async reconcile() { /* domain work */ }
29}