2026-10-01 대표 지시("이거는 안보내도 될것같아"). 켜면 발행된 사이트 **전체**가 대상이고
업장당 최대 30건이라 첫 회차에 AI 호출이 한꺼번에 일어난다 — 그 비용은 사람이 정할 일이지
기본값이 정할 일이 아니다. 글은 빌더의 [지금 생성하기] 로 사장님이 직접 만든다.
★ add_job 을 다시 지우지 않고 스위치로 뺀 이유: 바로 앞 커밋(4650125)이 고친 게
"등록 자체가 없는데 주석만 새벽에 채운다고 말하는" 상태였다. 지우면 그 상태로 돌아가
**끈 것과 빠뜨린 것이 구별되지 않는다** — 증상("메일이 안 온다")을 보고 발송 쪽을
뒤지게 된다. 지금은 꺼져 있다는 사실이 코드에 남는다.
★ 발송(09:00)은 이 스위치와 무관하게 항상 돈다 — 손으로 만든 글도 나가야 한다.
- scheduler/__init__: BLOG_AUTO_DRAFTS_ENABLED=1 일 때만 blog-drafts 등록
- docker-compose · .env.example: 스위치 전달, 기본 0
- docs/MINI_BLOG 2절: 기본 꺼짐과 그 이유, 예전에 등록이 없었다는 실측
테스트 5건(2건 신설 — 기본 꺼짐 · 켜면 등록됨), 전부 통과
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
72 lines
3.3 KiB
Python
72 lines
3.3 KiB
Python
"""백그라운드 스케줄러(크론) 패키지 — '언제'(when) 담당."""
|
|
import os
|
|
|
|
from apscheduler.schedulers.asyncio import AsyncIOScheduler
|
|
|
|
from common.logger import LOG
|
|
|
|
__all__ = ["start_scheduler", "shutdown_scheduler"]
|
|
|
|
_scheduler = None # AsyncIOScheduler | None
|
|
|
|
|
|
def _is_enabled() -> bool:
|
|
return os.environ.get("SCHEDULER_ENABLED", "0") == "1"
|
|
|
|
|
|
def start_scheduler():
|
|
"""lifespan startup 에서 호출."""
|
|
global _scheduler
|
|
if not _is_enabled():
|
|
LOG.i("[scheduler] disabled (SCHEDULER_ENABLED != 1)")
|
|
return
|
|
if _scheduler is not None:
|
|
return
|
|
|
|
# 한국시간 기준.
|
|
_scheduler = AsyncIOScheduler(timezone="Asia/Seoul")
|
|
# 1분이 아니라 5분이다.
|
|
from scheduler.jobs import sweep_social_posts
|
|
_scheduler.add_job(sweep_social_posts, 'interval', minutes=5, max_instances=1, coalesce=True)
|
|
if os.environ.get("GSC_ENABLED") == "1":
|
|
from services.search_console_service import run_scheduled_check
|
|
_scheduler.add_job(run_scheduled_check, "interval", minutes=10,
|
|
id="search-console", max_instances=1, coalesce=True)
|
|
|
|
from scheduler.jobs import (
|
|
sweep_alert_outbox, sweep_blog_drafts, sweep_blog_mail, sweep_queue_health,
|
|
)
|
|
_scheduler.add_job(sweep_alert_outbox, "interval", minutes=1,
|
|
id="alert-outbox", max_instances=1, coalesce=True)
|
|
_scheduler.add_job(sweep_queue_health, "interval", minutes=5,
|
|
id="queue-health", max_instances=1, coalesce=True)
|
|
# 미니 블로그 — 아침에 검수 통과분을 보낸다(docs/MINI_BLOG.md).
|
|
#
|
|
# ★★ 재고 **자동** 채우기는 기본으로 돌지 않는다(2026-10-01 대표 지시: "이거는 안보내도
|
|
# 될것같아"). 글은 빌더 화면의 [지금 생성하기] 로 사장님이 직접 만든다.
|
|
# 켜면 발행된 사이트 **전체**가 대상이고 업장당 최대 30건이라, 첫 회차에 AI 호출이
|
|
# 한꺼번에 일어난다 — 그 비용을 감수할지는 사람이 정할 일이지 기본값이 정할 일이 아니다.
|
|
#
|
|
# ★ 그래서 `add_job` 을 지우지 않고 스위치로 격리했다. 예전에는 **등록 자체가 없었고**
|
|
# 주석만 "새벽에 채운다" 라고 말했다(실측 2026-09-30) — 그 상태는 "끈 것" 과 "빠뜨린 것"
|
|
# 이 구별되지 않아, 증상("메일이 안 온다")을 보고 발송 쪽을 뒤지게 된다.
|
|
# 실제로는 보낼 글이 0건이었다. 지금은 꺼져 있다는 사실이 코드에 남는다.
|
|
if os.environ.get("BLOG_AUTO_DRAFTS_ENABLED") == "1":
|
|
# 발송(09:00)보다 앞서야 그날 아침에 나갈 재고가 있다.
|
|
_scheduler.add_job(sweep_blog_drafts, "cron", hour=4, minute=10,
|
|
id="blog-drafts", max_instances=1, coalesce=True)
|
|
_scheduler.add_job(sweep_blog_mail, "cron", hour=9, minute=0,
|
|
id="blog-mail", max_instances=1, coalesce=True)
|
|
_scheduler.start()
|
|
LOG.i("[scheduler] started (KST: SNS 승인 만료·중단 복구)")
|
|
LOG.i(f"[scheduler] started (KST: {len(_scheduler.get_jobs())}개 잡)")
|
|
|
|
|
|
def shutdown_scheduler():
|
|
"""lifespan shutdown 에서 호출."""
|
|
global _scheduler
|
|
if _scheduler is not None:
|
|
_scheduler.shutdown(wait=False)
|
|
_scheduler = None
|
|
LOG.i("[scheduler] stopped")
|