← 개발 로그 목록

LearningCollector: 같은 파일 연속 수정 커밋 묶어서 중복 포스팅 방지

/ 4분 분량 / 개발 로그

블로그에 128번, 130번으로 거의 똑같은 내용의 글 두 개가 올라간 걸 발견했다. 둘 다 monitoring 레포의 Promtail 로그 수집 설정 얘기였는데, 하나는 "리팩토링", 하나는 "파이프라인 스테이지 추가"라는 이름만 다르게 붙어 있었다.

원인을 찾으려고 monitoring 레포 커밋 로그를 뒤져봤다. e38cf8f, 3f1305f, bed9fae 세 커밋이 8분 사이에 연달아 찍혀 있었고, 전부 같은 Promtail YAML 파일의 인덴트를 고친 커밋이었다. 오타 고치듯 짧은 간격으로 재작업한 흔적이었는데, LearningCollector 입장에서는 이걸 구분할 방법이 없었다.

_save_dev_commits를 보니 GitHub에서 가져온 own 커밋을 SHA 단위로만 중복 체크하고 있었다. SHA가 다르면 무조건 별개의 draft 후보였다. 그러니 같은 파일을 3번 고친 커밋이 있으면 그 파일 하나를 두고 블로그 글 초안이 3개 만들어지는 구조였다. 커밋 하나하나가 "이 작업을 왜 했는지"부터 다시 풀어 쓰는 전체 분량 글이라, 내용이 겹칠 수밖에 없었다.

해결 방향은 명확했다. SHA 단위 중복 체크로는 이 문제를 못 잡으니, 커밋을 묶어서 볼 기준이 하나 더 필요했다. "같은 레포에서 같은 파일 집합을 짧은 시간 안에 연속으로 건드렸는가"를 그 기준으로 잡았다. 파일 하나를 30분 안에 여러 번 고쳤다면 그건 별개의 작업이 아니라 하나의 즉흥 수정이 이어지고 있는 걸로 보는 게 합리적이라고 판단했다.

그래서 _collapse_rapid_followups를 추가했다. 커밋들을 (레포, 변경 파일 집합) 키로 그룹핑하고, 같은 그룹 안에서 커밋 시각을 정렬한 뒤 앞뒤 간격이 30분(RAPID_FOLLOWUP_WINDOW_SECONDS) 이내면 하나의 군집으로 묶는다. 군집 안에서는 마지막 커밋, 즉 최종 상태에 해당하는 커밋 하나만 draft 대상으로 남기고 나머지는 제외한다.

idxs_sorted = sorted(idxs, key=lambda i: commits[i].get("committedDate") or "")
cluster_last = idxs_sorted[0]
for i in idxs_sorted[1:]:
    prev_dt = _parse_committed_date(commits[cluster_last].get("committedDate"))
    cur_dt = _parse_committed_date(commits[i].get("committedDate"))
    if prev_dt and cur_dt and (cur_dt - prev_dt).total_seconds() <= RAPID_FOLLOWUP_WINDOW_SECONDS:
        dropped += 1
        cluster_last = i
    else:
        keep.add(cluster_last)
        cluster_last = i
keep.add(cluster_last)

간격 비교를 첫 커밋 기준으로 고정하지 않고 cluster_last를 계속 갱신하면서 비교하도록 했다. 그래야 8분, 8분, 8분 식으로 연달아 이어지는 경우뿐 아니라, 군집 중간에 조금 긴 텀이 있어도 전체적으로 이어지는 흐름이면 하나로 묶인다.

파일 목록을 못 가져온 커밋은 묶을 근거가 없으니 예외 처리했다. files가 빈 튜플이면 그룹핑 없이 무조건 keep에 넣도록 했다. 잘못된 정보로 억지로 묶어서 필요한 커밋을 지우는 것보다는, 판단할 근거가 없을 때는 그냥 원래대로 두는 쪽이 안전하다고 봤다.

이 로직은 _save_dev_commits 안에서 커밋 상세 조회(파일 목록 포함)가 끝난 직후, draft 생성 루프로 넘어가기 전에 끼워 넣었다. keep_indices로 걸러진 커밋과 파일 상세 리스트를 같이 필터링해서 이후 로직은 그대로 재사용할 수 있게 했다.

기존에 이미 올라간 128, 130번 중복 글은 이 PR과 별도로 DB에서 직접 정리했다. 코드 수정은 앞으로의 재발을 막는 것이고, 이미 생긴 결과물은 별개로 손봐야 하는 문제라 굳이 섞지 않았다.

커밋은 하나였다. 원인 추적과 수정을 한 번에 끝낸 셈인데, PR 본문에 원인(SHA 단위 중복 체크의 한계)과 해결 방식(파일 집합 + 시간 윈도우로 군집화)을 나눠서 적어둔 게, 나중에 이 로직을 왜 이렇게 짰는지 돌아볼 때 도움이 될 것 같다. 30분이라는 윈도우 값은 지금은 임의로 정한 수치라, 실제로 운영하면서 너무 짧거나 길다고 느껴지면 조정할 여지를 남겨뒀다.