Discourse 2026.10.0-latest.1 / Sidekiq 7.3.10 / Ruby 4.0.9
요약
2026년 10월 26일 자체 호스팅 포럼을 Discourse 2026.2.0-latest(Ruby 3.3.8)에서 2026.10.0-latest.1(Ruby 4.0.9, Rails 8.1.4, Sidekiq 7.3.10)로 업그레이드한 이후, Jobs.enqueue_in을 사용하여 큐잉된 잡(job)이 간헐적으로 그리고 조용히 소실되는 현상이 발생합니다: 만료 시간(scoring time)에 schedule zset에서 잡이 사라지지만 perform은 결코 실행되지 않습니다. 재시도도 없고, 데드 레터(Dead Letter Queue)에도 없으며, 기록된 예외도 없습니다(Logster::RedisStore를 사용하므로 삼켜진 예외는 확인할 수 있는데, 아무것도 없습니다). 또한 stat:failed는 0으로 유지됩니다. 9번의 통제된/실제 발생 사례에서 관찰된 소실률은 다음과 같습니다: 4개 소실 / 5개 실행(약 40 %).
환경
- 공식 도커 설치(
./launcher rebuild), 단일 사이트 - Discourse
2026.10.0-latest.1, 이미지discourse/base:2.0.20261026-0138 - Ruby 4.0.9 (2026-10-23), Rails 8.1.4, Sidekiq 7.3.10
UNICORN_SIDEKIQS=1(Sidekiq이Demon::Sidekiq에 의해 감시되며, pitchfork에 의해 포크됨)- Discourse redis와 분리된 전용 Redis 데이터베이스(
Discourse.sidekiq_redis_config)에 Sidekiq 상태 저장 - 관찰 기간 동안 Sidekiq 프로세스는 정상 상태: 0회 재시작,
[0 of 5 busy], 큐 비어있음,retry = 0,dead = 0,stat:failed = 0 - OOM(Out Of Memory) 없음, dmesg 이벤트 없음, 컨테이너 재시작 없음
관찰 결과 (2026-10-30, 실사용 사례 + 통제된 실험)
영향을 받은 잡은 Jobs::NotifyMailingListSubscribers로, Jobs.enqueue_in(SiteSetting.email_time_window_mins.minutes, ...) (600초)를 통해 큐잉되며 → 점수(due time)로 schedule zset에 추가됩니다.
| 만료 시간 (UTC) | 잡 | 결과 |
|---|---|---|
| 15:33:05 | 실사용 (게시물 282) | 소실 — schedule에서 사라짐, 실행된 적 없음 |
| 15:50:10 | 통제 (post_id 999999 → 조용히 조기 반환) | 실행 (+1 stat:processed ~8 초 후 만료) |
| 16:04:18 | 실사용 (게시물 283) | 실행 (SMTP 오류 기록됨, 아래 참조) |
| 16:21:44 | 실사용 ×2 + 통제 | 모두 실행 |
| 17:34:39 | 실사용 ×2 | 소실 ×2 |
| 17:42:39 | 통제 | 실행 (+1, 만료 후 11 초) |
| 17:55:59 | 실사용 | 소실 |
각 “소실” 잡에 대해 수집된 증거:
zrange schedule에 페이로드가 더 이상 포함되지 않습니다 (zrem이 발생한 것), 그리고retry,dead, 또는 어떤 큐(queue:low등 모두 0)에도 존재하지 않으며,stat:processed속성에도 없습니다.- 만료 윈도우 동안 300ms마다 샘플링한
pg_stat_activity워처는 Sidekiq 연결에서 실행된 SQL을 전혀 기록하지 못했습니다 —execute가 가장 먼저 수행하는 초기SELECT ... FROM posts조차 포함되지 않았습니다. Logster::RedisStore(__LOGSTER__MAP)에는 이 잡들에 대한 새로운 항목이 없으며, 실행된 실행의 경우 항목이 기록됩니다 (병렬로 수정한 다른 무관한 문제의 SMTP 오류).- Sidekiq 프로세스 제목은
[0 of 5 busy]로 유지되며; Sidekiq 재시작 없음 (부팅 이후 동일 PID), OOM 없음. - 소실된 잡에 대해
SkippedEmailLog횟수는 변경되지 않았습니다 (실행되고 실패하는 실행에서는 해당 테이블에 행이 기록됩니다).
"실행"된 실행은 긍정적으로 확인되었습니다: 사용자별 SMTP 오류 항목이 생성되었으며(SkippedEmailLog.custom_reason에 캡처됨), 따라서 잡 클래스와 페이로드 형식은 소실된 경우와 실행된 경우 간에 동일합니다 (동일한 클래스, 동일한 low 큐, post_id만 다름).
이전 설치(Discourse 2026.2.0-latest, Ruby 3.3.8)에서는 이 동작이 나타나지 않았습니다: 동일한 지연된 메일링 리스트 잡들은 2026-10-01과 2026-10-21에 신뢰할 수 있게 전달되었습니다(EmailLog + 다운스트림 MTA 큐로 확인됨).
적용된 완화 조치
이 사이트에 대해 email_time_window_mins = 0으로 설정하여 enqueue_in(0)이 at 키를 건너뛰고 잡을 직접 해당 큐로 푸시(client_push without at)하도록 함으로써, 예약 폴러(scheduled poller)를 완전히 우회합니다. 이 변경 후, 예상된 EmailLog 행 6개가 즉시 생성되었고 메일은 다운스트림 MTA에 의해 수락되었습니다.
수락한 트레이드오프: 메일링 리스트 알림은 이제 배치 +10분 대신 즉시 전송됩니다.
질문
- 이것은 2026.10 / Sidekiq 7.3.10 조합(또는 Ruby 4.0 하의
Demon::Sidekiq/ 예약 폴러)에서의 알려진 회귀(regression)인가요? - 다음 발생 시 추가로 어떤 진단 정보를 수집하길 원하시나요 (예:
DISCOURSE_LOG_SIDEKIQ=1잡 계측 활성화, Sidekiq 예외 백트레이스, 폴러 스레드 덤프)?