커널 업데이트 재부팅, 실패해도 되돌릴 수 있게 — grub-reboot 와 스냅샷 순서
설치된 커널 패치는 재부팅 전까지 반영되지 않습니다. 커널 업데이트가 필요한 이유, 새 커널을 grub-reboot 로 한 번만 시험 부팅하는 원리, 스냅샷을 뜨는 순서, 정시 작업을 피한 시각 선택과 1초 단위 중단 측정(약 40초)까지 정리했습니다.
서버는 닫았는데 앱은 열려 있다고 믿는 half-open 연결이 풀에 남으면 요청이 15분 멈춥니다. TCP 재전송 계산, mysql2 유휴 정리기의 함정, keepalive 실측, tcp_retries2 조정, 호스트 자동 재시작 차단까지 원인과 방어를 정리했습니다.
서버를 끄기 전에 얼마나 기다려야 할까요. 헬스 체크 주기보다 짧게 기다리면 요청 27~42건이 실패했고, 주기보다 길게 기다리면 0건이었습니다. keep-alive 연결이 종료를 6초 넘게 늘리는 이유와 유예 시간 예산까지 실험 결과로 정리했습니다.
외부 서비스의 비밀 키를 데이터베이스에 보관할 때 필요한 것은 못 읽게 하는 것과 못 바꾸게 하는 것이었습니다. AES-256-GCM 과 AAD 로 다른 자리에 옮겨 붙인 암호문이 풀리지 않게 하고, 키 버전과 응답 설계까지 직접 실험으로 정리했습니다.
서버가 에러 문장을 보내면 그 문장이 약속이 되어, 문구를 다듬는 일이 곧 기능 변경이 됩니다. 에러를 코드와 파라미터로 보내고 문장은 화면이 만들게 바꾼 과정과 RFC 9457 형식, 서버가 보내는 문자열을 세 부류로 가르는 기준을 차례로 정리했습니다.
같은 순간에 실패한 요청들이 똑같이 기다리면 다시 한꺼번에 몰려옵니다. 고정 간격, 지수 백오프, 풀 지터를 시뮬레이션으로 비교했더니 무작위 지터 하나가 회복 시간을 10초에서 2초로 줄였습니다. Retry-After 해석 방법까지 함께 정리했습니다.
.csv 라는 이름의 파일이 실제로는 엑셀이었고, .xls 아래에는 서로 다른 형식이 섞여 있었습니다. 파일 맨 앞 몇 바이트의 매직 넘버로 형식을 가리고, 인코딩과 숫자 앞 0 을 지키는 포장까지 하나씩 벗겨서 읽는 과정을 예제 파일로 정리했습니다.
같은 순간을 넣었는데 9시간 다른 값이 저장됐습니다. DATETIME 은 숫자만 저장하고, 그 숫자가 어느 지역의 시계인지는 드라이버 설정이 정하기 때문입니다. Go 의 time.Time 과 데이터베이스 시계까지 함께 실험하며 그 원인을 직접 정리했습니다.
데이터베이스의 DECIMAL 은 정확하지만, 꺼내서 Number 로 바꾸는 순간 큰 금액이 반올림됩니다. mysql2 가 DECIMAL 을 문자열로 주는 이유와 Go 의 float64, 가장 작은 단위의 정수로 계산하는 방법을 직접 실험으로 정리했습니다.
여러 워커가 한 작업 목록을 나눌 때 SKIP LOCKED 로 경쟁은 풀렸지만, 잠금은 트랜잭션과 함께 사라졌습니다. 그래서 누가 언제까지 맡았는지를 행에 적는 시간 리스를 더했고, 멈춘 워커의 작업을 다른 워커가 이어받는 과정을 로컬에서 직접 재현했습니다.