feat: 외부 API 한도를 10% 단계마다 디스코드로 알린다 - #258
Conversation
- 봇이 아니라 웹훅이다. 우리는 보내기만 하고 읽을 것이 없어, 게이트웨이 연결·토큰·권한이 따라붙는 봇으로 얻는 것이 없다 - port 로 나눠 #220(정책 만료 알림)이 그대로 재사용하게 한다. 통로를 두 번 만들지 않는다 - prod 에서만 실제로 보낸다. 로컬에서 돌면 개발 중에 팀 채널이 울린다 - 로컬은 no-op 이 아니라 로그로 남긴다. 아무것도 안 하면 "언제 무엇이 나가는가" 를 운영에 올려 봐야 알게 되는데, 알림은 울려 봐야 맞는지 아는 기능이다 - URL 이 없어도 부팅은 된다 — 시크릿 없이 로컬이 뜨는 불변식을 지킨다
- 기존 규칙은 `이름=값` 을 찾는데 웹훅 토큰은 경로 조각이라 이름이 없어 안 걸렸다. 전송 실패 메시지에 URL 이 섞이면 그대로 샜을 자리다 - 이 URL 끝을 아는 사람은 누구나 우리 채널에 글을 쓸 수 있다
- #123 이 한도를 보이게 했지만 보러 가야 보인다. 로그의 70/90% warn 도 마찬가지로 실시간으로 보는 사람이 없어, 한도가 마르면 코스가 폴백으로 내려간 뒤에야 알았다 - record() 는 외부 호출마다 돈다. "70% 넘었다" 로 판정하면 그 뒤 모든 호출이 알림을 쏘므로, 어디까지 알렸는지를 어딘가 기억해야 한다 - 인메모리 대신 DB 컬럼으로 둔다. 재배포마다 리셋되면 이미 보낸 단계를 다시 보내는데, 이 테이블을 애초에 DB 로 둔 이유와 같은 함정이다 - 판정과 기록을 조건부 UPDATE 한 문장으로 합친다. 읽고 나서 쓰면 그 사이 다른 인스턴스가 같은 단계를 읽어 둘 다 보낸다 - 단계 상한을 10 으로 둔다. 초과분마다 오르면 안 고치는 동안 계속 울리고, 며칠이면 아무도 안 보게 되어 알림이 없는 것과 같아진다 - 단계 계산은 enum 이 소유한다. 한도가 작을수록 촘촘해지는 것이 의도다 — TMAP 50 은 5건마다, TAGO 10,000 은 1,000건마다 본다
- prod 에서만 실제 전송하므로 운영 환경변수로만 들어간다
- 단계 계산은 경계값을 망라한다. 놓치는 것보다 호출마다 쏘는 쪽이 더 나쁘다 — 며칠이면 아무도 안 보게 되어 알림이 없는 것과 같아진다 - 이미 알린 단계가 다시 선점되지 않는 것을 DB 로 확인한다. 인메모리였다면 여기서 통과한다 - 웹훅 토큰이 로그로 새지 않는지 잠근다 - 기록기 생성자가 바뀌어 직접 만드는 두 곳을 함께 고친다
|
Warning Review limit reached
Next review available in: 78 minutes You've used all free OSS reviews for now. Wait for the free limit to reset to keep reviewing this public repository. How can I continue?After more reviews become available, a review can be triggered using the To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based reviews. How do review limits work?CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability. For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window. Please refer docs for additional details. Review details⚙️ Run configurationConfiguration used: Path: .coderabbit.yaml Review profile: CHILL Plan: Pro Plus Run ID: 📒 Files selected for processing (17)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
Situation
#123 이 한도를 보이게 했지만, 보러 가야 보인다.
/api/v1/quotas를 아무도 열지 않으면 소진을 여전히 모른다. 로그에 70%·90% warn 이 있지만 그것도 마찬가지다 — 실시간으로 로그를 보고 있는 사람이 없다.한도가 마르면 조용히 나빠진다. 관광정보가 마른 날 운영 코스가 인허가 폴백으로 내려갔는데, 그걸 안 것은 한참 뒤였다. 폴백은 정상처럼 보이기 때문에 화면만 봐서는 알 수 없다.
가장 위험한 것은 TMAP 경유지 최적화 50이다. 코스 하나가 여러 번 부르면 하루에 코스 몇 개를 못 만든다.
Task
알림을 붙이는 것 자체는 어렵지 않다. 어려운 건 알림이 소음이 되지 않게 하는 것이다.
ExternalApiCallRecorder.record()는 외부 호출마다 돈다. "70% 넘었다" 로 판정하면 그 뒤 모든 호출이 알림을 쏜다. 며칠이면 아무도 안 보게 되고, 그러면 알림이 없는 것과 같다. 놓치는 것보다 이쪽이 더 나쁘다.Action
단계가 올라가는 순간에만 보낸다
step = used * 10 / limit으로 10% 단계를 구하고, 단계가 처음 올라갈 때만 보낸다.한도가 작을수록 촘촘해진다. 의도한 결과다 — 빡빡한 쪽을 자주 보게 된다.
단계 상한은 10이다. 한도를 넘겨도 더 오르지 않는다 — 초과분마다 단계가 오르면 안 고치는 동안 계속 울린다.
중복 방지를 인메모리에 두지 않는다
"어디까지 알렸나" 를 어딘가 기억해야 하는데, 인메모리 플래그는 재배포마다 리셋돼 이미 보낸 단계를 다시 보낸다.
external_api_call을 애초에 테이블로 둔 것과 같은 함정이다. 그 마이그레이션 주석이 이미 말하고 있다 — "인메모리로 두면 재시작마다 0 이 되어 실제보다 여유 있게 보인다. 배포가 잦은 날일수록 실제 소진에 가까운데 화면은 깨끗해지는, 정확히 반대 방향의 오차가 난다."notified_step컬럼을 두고 조건부 UPDATE 로 단계를 선점한다.판정과 기록이 한 문장이다. 읽고 나서 쓰면 그 사이에 다른 인스턴스가 같은 단계를 읽어 둘 다 보낸다. DB 가 행 잠금으로 갈라주게 두면 인스턴스가 몇이든 한 번만 나간다.
call_date가 키라 자정을 넘기면 새 행이 되어 단계도 자연히 리셋된다.요청 경로를 늦추지 않는다
record()는 코스 생성 도중에 불린다. 여기서 디스코드 응답을 기다리면 디스코드가 느린 날 사용자 요청이 함께 느려진다. 구독만 걸고 곧바로 돌아온다.실패는 삼키되 흔적을 남긴다. 알림을 못 보낸 것 때문에 코스 생성이 깨지면 안 되지만, 조용히 죽으면 알림이 없는 것과 같다.
봇이 아니라 웹훅
우리는 보내기만 하고 읽을 것이 없다. 봇은 게이트웨이 연결·토큰·권한이 따라붙는데 얻는 것이 없다.
Notifierport 로 나눠 #220(정책 만료 알림)이 그대로 재사용한다 — 통로를 두 번 만들지 않는다.prod/!prod)이라 어느 환경에서도 빈이 정확히 하나다웹훅 URL 이 로그로 샐 자리를 막았다
기존 마스킹은
이름=값을 찾는데 웹훅 토큰은 경로 조각이라 이름이 없다. 전송 실패 메시지에 URL 이 섞이면 그대로 샜을 자리다 — 이 URL 끝을 아는 사람은 누구나 우리 채널에 글을 쓸 수 있다.Result
자문 셋 (
CLAUDE.md)TINYINT) 추가. 호출당 UPDATE 한 번이 는다.후속
Notifier를 그대로 쓴다연관 이슈