5 · Cron jobs — ไม่มี in-process scheduler
บทนี้ยืนยันด้วยหลักฐานจากโค้ดว่า recruitment-api (branch: prod) ไม่มี cron job / background scheduler ในตัว process เลย — ต่างจาก benefit-api (มี @nestjs/schedule + CronTabService) และ 2way-api (มี src/crontab/ + 3 cron active เวลา 07:00)
ทุกงานที่ดู "คล้าย batch" (ส่งอีเมลประกาศผล, export-to-HRMI, ลด remainingManpower, PDPA delete cascade) ถูก trigger แบบ synchronous ภายใน HTTP request-response ไม่มี worker / queue / k8s CronJob ที่อ้างอิงจาก repo นี้
สรุปคำตัดสิน (หนึ่งบรรทัด)
| คำถาม | คำตอบ | หลักฐาน |
|---|---|---|
มี @nestjs/schedule ใน dependencies ไหม? | ไม่มี | recruitment-api/package.json (branch: prod) — grep @nestjs/schedule = 0 match |
มี @Cron / CronExpression / ScheduleModule ใน src/ หรือ libs/ ไหม? | ไม่มี | grep ทั้ง repo บน branch prod = 0 match |
มีโฟลเดอร์ src/crontab/ หรือ src/cron/ ไหม? | ไม่มี | โครงสร้างโฟลเดอร์ใน 01 Repository map |
| มี Bull / BullMQ / Agenda / node-cron ไหม? | ไม่มี | package.json dependencies ไม่มีชื่อเหล่านี้ |
| แล้วงาน batch ทำที่ไหน? | ใน request handler ของ feature module | ดูตารางด้านล่าง |
วิธีตรวจสอบซ้ำ (re-verify checklist)
รันบน branch prod ของ recruitment-api:
# 1) dependency
grep -n '@nestjs/schedule\|node-cron\|bullmq\|bull\|agenda' package.json
# คาดหวัง: ไม่มีผลลัพธ์
# 2) decorator / module
rg -n '@Cron|ScheduleModule|CronExpression|CronJob' src libs
# คาดหวัง: ไม่มีผลลัพธ์
# 3) โฟลเดอร์
ls src/crontab src/cron 2>&1
# คาดหวัง: No such file or directoryเทียบกับ repo ที่ มี cron จริง:
| Repo | มี @nestjs/schedule | มี @Cron active | โฟลเดอร์ |
|---|---|---|---|
benefit-api (prod) | ใช่ (package.json:33) | 2 jobs ใน cronTab.service.ts | src/crontab/ |
2way-api (prod) | ใช่ | Announce/Survey/Complaint @ 07:00 | src/crontab/ |
recruitment-api (prod) | ไม่ | 0 | ไม่มี |
ทำไมไม่มี cron ก็ยัง "ทำงานได้"
recruitment-api เป็น request-driven NestJS API — lifecycle ของธุรกิจถูกออกแบบให้ผูกกับ action ของผู้ใช้ (ทีมสรรหา / ผู้สมัคร / ทีมฐาน) ไม่ใช่ตารางเวลา:
[HTTP POST/PUT] ──► Controller ──► Service
│
├─ TypeORM save/update (main / ai)
├─ axios → workforce-api / vtrc-api
├─ nodemailer → SMTP (sync ใน request)
└─ PDF/Excel render (sync ใน request)ไม่มีชั้น "รอถึง 07:00 แล้วค่อยส่ง LINE / sync" แบบที่ 2way-api ทำ — ถ้าต้องการแจ้งเตือนตามเวลา ต้องพึ่งระบบอื่น (เช่น job-scheduler-api, email client ของผู้รับ, หรือ batch นอก repo นี้) ซึ่ง ยังไม่พบ caller จาก repo นี้ ในรอบเอกสารนี้
งานที่ "คล้าย batch" แต่เป็น sync ใน request
ตารางนี้รวม operation ที่ทีมมักเข้าใจผิดว่าเป็น cron — จริง ๆ แล้วถูกเรียกจาก endpoint:
| งาน | Trigger จริง | ไฟล์หลัก (branch: prod) | หมายเหตุ |
|---|---|---|---|
| ส่งอีเมลประกาศผลสอบ/สัมภาษณ์ | AnnouncementModule confirm | announcement.service.ts + libs/utils/announcementEmailService.ts | รันตอน user กด confirm ใน request |
| ส่งอีเมลสัญญา / export-to-HRMI | ContractModule update status | contract.service.ts + contractEmailService.ts | ไม่มี schedule ส่งซ้ำอัตโนมัติ |
ลด remainingManpower | สัญญา status === 'confirm' | contract.service.ts:740-763 | read-modify-write ใน request (ดู CORR-RECRUIT-01 ใน scorecard) |
| อัปเดต manpower ที่ workforce | create/update/delete job request | jobRequest.service.ts:948-991 | HTTP PUT ไป workforce-api |
| OTP ส่งอีเมล | GET /jobList/sent_otp/:email | jobList.service.ts + otpService.util.ts | @Public() + sync |
| PDPA delete cascade | DeleteHistoryModule | deleteHistory.service.ts | cascade ใน request เดียว |
| AI compare candidate | CandidateModule compare-by-AI | candidate.repositories.ts:1324-1338 | EXEC SP บน connection ai แบบ sync |
| Import blacklist จาก Excel | POST /blacklist/import_blacklist | blacklist.controller.ts:20-27 | @Public() — ไม่ใช่ cron |
สิ่งที่ ไม่ พบ (explicit negative evidence)
1) ไม่มี ScheduleModule.forRoot()
recruitment-api/src/app.module.ts (branch: prod):26-66 ลงทะเบียน:
ConfigModule- TypeORM 4 connection (
main,ai,vtrc,workforce) - 13 feature module
ไม่มี ScheduleModule ใน imports array — เทียบกับ benefit-api/src/app.module.ts ที่ import ScheduleModule.forRoot() ที่ root
2) ไม่มี provider ชื่อ *TaskService / CronTabService
โครงสร้าง src/modules/ มีแค่ feature module ตามธุรกิจ (jobRequest, candidate, contract, …) — ไม่มี module สำหรับ background task
3) ไม่มี external orchestrator ที่อ้างในโค้ด
- ไม่พบ import ไป
job-scheduler-api - ไม่พบ HTTP client เรียก path แบบ
/cronหรือ/batchของระบบอื่นจาก recruitment-api (นอกเหนือ workforce/vtrc ที่เป็น business API) bitbucket-pipelines.ymlเป็น CI/CD build+push image — ไม่ เป็น scheduled business job
4) ไม่มี Redis / queue สำหรับ delayed job
package.json ไม่มี ioredis, bull, bullmq, @nestjs/bull — ไม่มี delayed email / retry queue ในตัว service
ผลกระทบเชิง operations เมื่อ "ไม่มี cron"
| ด้าน | ผลต่อทีมดูแล |
|---|---|
| Deploy / restart | ไม่มี job ค้าง mid-flight จาก scheduler ใน process — ปลอดภัยกว่า repo ที่มี cron ยาว |
| Horizontal scale | ไม่ต้องกังวล leader election ของ @Cron (เพราะไม่มี) — แต่ต้องกังวล race ของ concurrent HTTP (เช่น remainingManpower) |
| Monitoring | ไม่มี metric "cron last success" — ต้อง monitor จาก HTTP latency / email SMTP fail ใน request log |
| Retry | ถ้าส่งอีเมล fail ตอน confirm สัญญา — ไม่มี auto-retry ตอน 07:00 ต้อง user กดใหม่หรือมี manual path |
| Nightly cleanup | ไม่มี job ลบไฟล์ชั่วคราวตามเวลา — มี utility remove-fileByTime เป็น helper ที่เรียกจากโค้ด ไม่ใช่ cron (libs/common/remove-fileByTime.ts) |
ความสัมพันธ์กับ health scorecard
ใน 06 Scorecard มิติ Background jobs ได้สถานะเขียว เพราะ:
- ไม่มี attack surface ของ unauthenticated cron HTTP trigger (ต่างจากบาง repo ที่เปิด
@Public()บน batch endpoint) - ไม่มี SQL string ใน cron ที่รันทุกเช้าโดยไม่มี review
- ความเสี่ยงย้ายไปอยู่ที่ sync path แทน (SEC-RECRUIT-02 บน AI compare, CORR-RECRUIT-01 บน manpower)
ถ้าอนาคตจะเพิ่ม cron (เช่น reminder สัมภาษณ์, expire ประกาศอัตโนมัติ) ควร:
- เพิ่ม
@nestjs/scheduleอย่างตั้งใจ + document ในบทนี้ - ใส่ leader election หรือรันบน replica เดียว
- อย่าเปิด HTTP
@Public()สำหรับ manual trigger โดยไม่มี auth - ห้าม raw SQL string concat จาก user input ใน task handler
เปรียบเทียบสั้นกับ sibling services
benefit-api ── @Cron ×2 (funeral/inheritance HRMI sync)
2way-api ── @Cron ×3 active (announce/survey/complaint @ 07:00)
+ PMS notification code มีแต่ @Cron ถูก comment
recruitment-api ── (ว่าง) ไม่มี scheduler ใน processข้อสรุปสำหรับผู้อ่าน Volume 6: เมื่อ debug "ทำไมอีเมลประกาศผลไม่ถูกส่งตอนกลางคืน" — คำตอบคือ ระบบไม่ได้ออกแบบให้ส่งตอนกลางคืน; ต้องมีคนกด confirm ใน back-office ช่วงที่มี request ถึง API
เอกสารอ้างอิงภายใน Volume
- 01 Repository map — โครงสร้างโฟลเดอร์ยืนยันไม่มี
crontab/ - 02 Core pipeline — ทุก stage เป็น HTTP-driven
- 03 Persistence — connection / entity (ไม่มี job table สำหรับ scheduler)
- 04 Auth + integrations — email/SMTP ถูกเรียกจาก service layer
- 06 Scorecard — debt ที่เกี่ยวกับ sync path แทน cron path
ไป บท 6 Scorecard →