포스트

2023-11-24-TIL

2023-11-24-TIL

Today I Learned

Kernel Panic

FTP 커넥션이 과도하게 발생하면서 Kernel Panic이 발생한 현상이 있었다고 한다. Kernel Panic이 정확히 무엇이고 언제 발생할 수 있는가?

위키백과는 커널 패닉을 운영체제 커널이 안전하게 복구할 수 없거나 계속 실행하면 큰 데이터 손실 위험이 있는 치명적 내부 오류를 발견했을 때 취하는 안전장치로 설명한다. Windows의 블루스크린(stop error)에 해당한다. 패닉 루틴은 콘솔에 오류 메시지를 출력하고, 사후 분석을 위해 커널 메모리 이미지를 디스크에 덤프한 뒤, 수동 재부팅을 기다리거나 자동으로 재부팅한다.

원인은 하드웨어 고장이나 운영체제의 소프트웨어 버그다. 문서는 시스템이 불안정한 상태에서 계속 돌면 보안 침해나 데이터 손상 위험이 있으므로 차라리 멈추는 것이라고 설명한다. 리눅스에는 덜 심각한 오류인 kernel oops도 있다. 이때는 문제를 일으킨 프로세스만 죽이고 커널은 계속 동작한다.

연결이 과도하게 몰리는 상황 자체가 곧바로 패닉의 원인이 되지는 않는다. 보통은 그 부하가 드러낸 커널이나 드라이버 버그, 메모리 고갈 같은 조건이 실제 원인이다. 링크된 Server Fault 질문처럼 패닉 메시지 자체에 단서가 담기는 경우가 많으므로, 원인을 좁히려면 패닉 메시지와 커널 덤프, 직전 커널 로그를 함께 확인해야 한다.

  • https://forum.filezilla-project.org/viewtopic.php?t=42356
  • https://serverfault.com/questions/862048/kernel-panic-not-syncing-attempted-to-kill-init-after-mount
  • https://forum.netgate.com/topic/29275/kernel-panic

Stub for Testing

테스트 더블(test double)은 테스트에서 실제 객체 대신 쓰는 모든 가짜 객체를 부르는 말이다. 당근페이 기술 블로그 글은 송금 기능을 예로 다섯 종류를 설명한다.

  • Dummy: 생성자 인자를 채우려고 넘기기만 하고 실제로는 쓰지 않는 객체
  • Fake: 실제처럼 동작하지만 단순하게 구현한 객체. 운영 DB 대신 쓰는 인메모리 저장소가 예다.
  • Stub: 정해진 입력에 미리 정해 둔 응답을 돌려주는 객체
  • Spy: 호출 횟수나 인자를 기록해 두었다가 나중에 확인할 수 있게 하는 객체
  • Mock: 기대한 호출이 일어났는지를 검증하는 객체

글이 강조하는 차이는 검증 방식이다. Stub은 상태 검증, 즉 어떤 입력에 어떤 결과가 나오는지를 확인하고, Mock은 행동 검증, 즉 협력 객체를 어떻게 호출했는지를 확인한다. Mock으로 구현 세부(어떤 메서드를 몇 번 부르는지)까지 고정하면 리팩터링할 때마다 테스트가 깨지기 쉬우므로, 결과를 확인하면 되는 곳에는 Stub이나 Fake로 상태를 검증하는 편이 테스트를 덜 깨지게 만든다.

  • https://medium.com/daangn/%ED%9A%A8%EC%9C%A8%EC%A0%81%EC%9D%B8-%ED%85%8C%EC%8A%A4%ED%8A%B8%EB%A5%BC-%EC%9C%84%ED%95%9C-stub-%EA%B0%9D%EC%B2%B4-%ED%99%9C%EC%9A%A9%EB%B2%95-5c52a447dfb7

Multipart Separator

multipart 본문의 각 파트는 boundary 문자열로 구분한다. RFC 1341의 multipart 절에 정의된 규칙은 이렇다.

  • Content-Type 헤더의 boundary 파라미터로 구분 문자열을 정한다.
  • 각 파트 앞에는 하이픈 두 개(--)와 boundary로 이루어진 줄이 온다. 이 줄은 반드시 줄의 처음(CRLF 다음)에서 시작한다.
  • 마지막 파트 뒤에는 boundary 끝에 하이픈 두 개를 더 붙인 닫는 줄이 온다.
  • boundary는 어떤 파트 내용에도 나타나면 안 된다. 그래서 보내는 쪽이 충분히 긴 임의 문자열을 고른다.
1
2
3
4
5
6
7
8
9
10
11
12
Content-Type: multipart/form-data; boundary=gc0p4Jq0M2Yt08jU534c0p

--gc0p4Jq0M2Yt08jU534c0p
Content-Disposition: form-data; name="title"

hello
--gc0p4Jq0M2Yt08jU534c0p
Content-Disposition: form-data; name="file"; filename="a.txt"
Content-Type: text/plain

file content
--gc0p4Jq0M2Yt08jU534c0p--

curl -F나 브라우저가 boundary를 자동으로 만들어 주므로, 직접 HTTP 요청을 만들 때 Content-Type을 손으로 multipart/form-data로만 지정하고 boundary를 빠뜨리면 서버가 본문을 해석하지 못한다.

  • https://www.w3.org/Protocols/rfc1341/7_2_Multipart.html
  • https://everything.curl.dev/http/multipart
  • https://stackoverflow.com/questions/3508338/what-is-the-boundary-in-multipart-form-data

Pessimistic Lock vs Optimistic Lock

낙관적 잠금은 충돌이 드물다고 보고 잠금 없이 진행한 뒤 커밋 시점에 @Version 값으로 충돌을 감지한다. 비관적 잠금은 충돌이 잦다고 보고 읽는 시점에 DB 잠금을 먼저 건다. Baeldung 글에 정리된 JPA의 비관적 잠금 모드는 이렇다.

  • PESSIMISTIC_READ: 공유 잠금을 건다. 다른 트랜잭션도 읽을 수는 있지만 수정이나 삭제는 막는다.
  • PESSIMISTIC_WRITE: 배타 잠금을 건다. 보통 SELECT ... FOR UPDATE로 실행되고, 다른 트랜잭션은 이 행을 잠그거나 수정할 수 없다.
  • PESSIMISTIC_FORCE_INCREMENT: 배타 잠금을 걸면서 버전 값도 올린다.

잠금을 얻지 못하면 PessimisticLockException(트랜잭션 롤백) 또는 LockTimeoutException(문장 단위 롤백)이 난다. 선택 기준은 충돌 빈도와 재시도 비용이다. 재고 차감처럼 같은 행에 동시 수정이 몰리면 낙관적 잠금은 실패와 재시도가 반복되므로 비관적 잠금이 낫고, 충돌이 드문 일반적인 수정에는 잠금 대기가 없는 낙관적 잠금이 낫다.

  • https://www.baeldung.com/jpa-pessimistic-locking
  • https://isntyet.github.io/jpa/JPA-%EB%B9%84%EA%B4%80%EC%A0%81-%EC%9E%A0%EA%B8%88(Pessimistic-Lock)/
  • https://junhyunny.github.io/spring-boot/jpa/junit/jpa-pessimitic-lock/
  • https://velog.io/@znftm97/%EB%8F%99%EC%8B%9C%EC%84%B1-%EB%AC%B8%EC%A0%9C-%ED%95%B4%EA%B2%B0%ED%95%98%EA%B8%B0-V2-%EB%B9%84%EA%B4%80%EC%A0%81-%EB%9D%BDPessimistic-Lock
  • https://velog.io/@lsb156/JPA-Optimistic-Lock-Pessimistic-Lock
이 글은 저작권자의 CC BY 4.0 라이선스를 따릅니다.

댓글

아직 댓글이 없습니다