지연
어떤 일을 시작하고 그 결과를 받을 때까지 걸린 시간입니다. 요청을 보낸 뒤 답이 올 때까지 기다린 그 시간입니다. 무엇을 시작으로 잡고 무엇을 끝으로 잡느냐에 따라 같은 이름의 값이 달라집니다.
상세
가게에서 주문을 넣고 접시가 나올 때까지 기다린 시간이 지연입니다. 가게에서는 주문한 순간이 시작이라는 것을 서로 압니다. 시스템에서는 그 시작을 어느 사건으로 잡을지부터 적어 두어야 합니다.
지연은 한 사건이 일어난 시점과 그에 대응하는 다른 사건이 일어난 시점 사이의 경과 시간입니다. 그래서 재기 전에 두 사건을 못 박아야 합니다. 못 박는 자리가 옮겨가면 값도 옮겨갑니다.
RFC(Request for Comments) 7679 는 단방향 지연을 이렇게 적습니다. 보내는 쪽이 어떤 패킷의 첫 비트를 실제 시각 T 에 받는 쪽으로 내보냈고, 받는 쪽이 그 패킷의 마지막 비트를 실제 시각 T+dt 에 받았다면, 시각 T 의 단방향 지연은 dt 입니다. 시작 사건은 첫 비트를 내보낸 것입니다. 끝 사건은 마지막 비트가 도착한 것입니다.
같은 표준은 끝 사건이 일어나지 않은 경우도 적어 둡니다. 받는 쪽이 손실 판정 대기 시간 Tmax 안에 그 패킷을 받지 못했다면 단방향 지연은 정의되지 않습니다. 비공식적으로는 무한이라고 적습니다.
RFC 2681 은 같은 낱말을 다른 끝 사건에 붙입니다. 보내는 쪽이 첫 비트를 실제 시각 T 에 내보내고, 받는 쪽이 그 패킷을 받은 즉시 패킷 하나를 되돌려 보내고, 보내는 쪽이 그 마지막 비트를 실제 시각 T+dt 에 받았다면, 그것이 시각 T 의 왕복 지연 dt 입니다.
sequenceDiagram
participant 보내는쪽
participant 받는쪽
보내는쪽->>받는쪽: 첫 비트를 시각 T 에 내보냄
Note over 받는쪽: 마지막 비트 도착 · 여기서 멈추면 단방향 지연
받는쪽->>보내는쪽: 받은 즉시 되돌려 보냄
Note over 보내는쪽: 마지막 비트 도착 · 여기서 멈추면 왕복 지연
두 정의는 시작 사건이 같고 끝 사건이 다릅니다. 그래서 같은 경로를 두고도 어느 쪽으로 쟀는지에 따라 다른 값이 나옵니다. 지연이라는 값 하나만으로는 무엇을 잰 것인지 알 수 없습니다. 어디서 시작해 어디서 멈춘 시간인지가 값에 딸려 있어야 합니다.
배경
시스템이 초당 몇 건을 처리하는지만 세면 요청 하나를 넣은 쪽이 얼마나 기다렸는지는 안 보입니다. 처리한 건수와 한 건에 걸린 시간은 서로 다른 것을 셉니다. 건수를 올려도 한 건이 걸린 시간이 따라 줄어들지는 않습니다. 기다리는 쪽의 불편은 건수가 아니라 시간에 붙어 있습니다.
그래서 수요의 크기와 한 건에 걸린 시간을 따로 세게 됐습니다. Google SRE(Site Reliability Engineering, 사이트 신뢰성 공학) Book 의 「Monitoring Distributed Systems」 장은 감시의 네 가지 황금 신호로 지연과 트래픽과 오류와 포화를 듭니다. 지연은 요청 하나를 처리하는 데 걸리는 시간이라고 적습니다. 트래픽은 시스템에 걸리는 수요의 크기이고 웹 서비스에서는 보통 초당 HTTP(HyperText Transfer Protocol, 하이퍼텍스트 전송 규약) 요청 수로 잰다고 적습니다. 같은 장은 지연 증가가 흔히 포화의 선행 지표라고도 적습니다.
재기 시작하자 두 번째 불편이 나왔습니다. 같은 책은 감시 체계를 맨바닥에서 세울 때 평균 지연 같은 평균값을 잣대로 삼고 싶어진다고 적습니다. 평균 지연 100 밀리초로 초당 1,000 건을 처리하는 웹 서비스라면 요청의 1 퍼센트가 쉽게 5초씩 걸릴 수 있다고 적습니다. 사용자가 그런 서비스 여럿에 기대어 한 화면을 그린다면 어느 백엔드의 99 분위 값이 프런트엔드의 중앙값 응답이 될 수 있다고 덧붙입니다. 그래서 실제 지연 값 대신 지연 구간별 요청 수를 세라고 적습니다. 0에서 10 밀리초, 10에서 30 밀리초, 30에서 100 밀리초 하는 식입니다. 딘과 바로소는 2013년 글에서 대화형 서비스에서는 시스템의 크기와 복잡도가 커지거나 전체 사용률이 올라갈수록 지연 분포의 꼬리를 낮게 유지하기가 어렵다고 적습니다. 중간 규모에서는 중요하지 않던 일시적인 고지연 구간이 큰 규모에서는 서비스 성능 전체를 지배하게 될 수 있다고 적습니다. 그 글은 그런 시스템을 지연 꼬리를 견디는 시스템이라고 부릅니다. 평균 옆에 분위수와 꼬리 지연이라는 이름이 따로 선 자리입니다.
예시
첫 바이트까지의 시간
MDN 은 TTFB(Time to first byte, 첫 바이트까지의 시간)를 브라우저가 페이지를 요청한 시점과 서버로부터 첫 바이트를 받는 시점 사이의 시간이라고 적습니다. 이 시간에는 DNS(Domain Name System, 도메인 이름 체계) 조회가 들어갑니다. TCP(Transmission Control Protocol, 전송 제어 규약) 핸드셰이크로 연결을 세우는 것도 들어갑니다. 요청이 HTTPS(HyperText Transfer Protocol Secure, 보안 하이퍼텍스트 전송 규약)로 나가면 TLS(Transport Layer Security, 전송 계층 보안) 핸드셰이크도 들어갑니다. 같은 문서는 TTFB 를 요청의 시작과 응답의 시작 사이에 걸린 시간이라고 다시 적고 단위를 밀리초로 답니다.
브라우저에서 이 값을 읽는 자리는 이렇습니다.
const ttfb = performance.getEntriesByType("navigation")[0].responseStart;
PerformanceNavigationTiming 의 responseStart 속성이 그 값입니다. 시작 사건과 끝 사건이 브라우저 쪽 인터페이스의 속성 이름으로 박혀 있는 자리입니다.
Server-Timing 헤더
HTTP 응답 헤더 Server-Timing 은 요청과 응답 한 주기에 대한 성능 잣대를 사용자 에이전트에 전달합니다. 백엔드 서버에서 잰 시간 값을 브라우저 개발자 도구나 PerformanceServerTiming 인터페이스에 드러내는 데 씁니다. 데이터베이스 읽기와 쓰기, CPU(Central Processing Unit, 중앙처리장치) 시간, 파일 시스템 접근이 그 예로 적혀 있습니다.
Server-Timing: cpu;dur=2.4
Server-Timing: db;dur=53, app;dur=47.2
이름 뒤의 dur 이 그 구간에 걸린 시간입니다. 아래 줄은 한 응답에 잣대 두 개를 실은
것입니다. db 구간이 53 이고 app 구간이 47.2 입니다. 한 요청 안에서 지연이 어느 구간에 얼마나
쌓였는지를 서버가 직접 적어 보내는 자리입니다.
PostgreSQL 의 log_min_duration_statement
PostgreSQL 은 완료된 문 하나하나의 소요 시간을 로그에 남기는 설정을 둡니다. 문이 지정한 시간 이상 돌았을 때만 남깁니다.
log_min_duration_statement = 250ms
문서는 250ms 로 두면 250 밀리초 이상 걸린 SQL(Structured Query Language, 구조화 질의 언어) 문이 전부 로그에 남는다고 적습니다. 단위 없이 적은 값은 밀리초로 읽습니다. 0 으로 두면 모든 문의 소요 시간을 찍습니다. 기본값인 -1 은 이 기록을 끕니다. 같은 문서는 이 설정을 켜는 것이 애플리케이션에서 최적화되지 않은 질의를 추적하는 데 도움이 될 수 있다고 적습니다. 지연에 문턱을 걸어 그 위만 남기는 방식입니다.
ping 의 왕복 지연
RFC 2681 은 주석에서 ping 이 자기 정의 아래 왕복 측정으로 인정된다고 적습니다. 60바이트 패킷의 ICMP(Internet Control Message Protocol, 인터넷 제어 메시지 규약) 에코 요청과 응답을 Type-P 로 잡은 경우입니다. 같은 문서는 이 잣대의 최솟값이 전파 지연과 전송 지연만으로 생기는 지연을 가늠하게 해 준다고 적습니다. 최솟값은 지나는 경로에 부하가 적을 때 겪게 될 가능성이 큰 지연을 가늠하게 해 주기도 합니다. 최솟값을 넘는 값들은 그 경로에 있는 혼잡을 가늠하게 해 줍니다.
경계
실패한 요청에 걸린 시간도 지연에 넣나요. 넣습니다. 다만 성공한 요청의 지연과 한 통에 담지 않습니다.
Google SRE Book 은 성공한 요청의 지연과 실패한 요청의 지연을 구분하는 것이 중요하다고 적습니다. 데이터베이스나 다른 핵심 백엔드와의 연결이 끊겨 생긴 HTTP 500 오류는 아주 빨리 응답될 수 있습니다. 그런데 HTTP 500 은 실패한 요청을 가리킵니다. 그래서 500 을 전체 지연에 섞어 넣으면 오해를 부르는 계산이 나올 수 있다고 적습니다.
그렇다고 오류를 통계에서 걸러내 버리는 쪽도 아닙니다. 같은 책은 오류를 그냥 걸러내는 대신 오류 지연을 추적하는 것이 중요하다고 적습니다. 그래서 판정은 이렇게 섭니다. 실패한 요청에 걸린 시간도 지연입니다. 성공한 요청의 지연과 같은 통에 담기지 않을 뿐입니다.
관련 항목
지연을 정의하는 표준과 그 두 갈래
같이 재는 신호
트래픽 · 오류 · 포화 · 응답 시간 · 초당 요청 수 · 처리량
분포를 재는 지표
평균 · 중앙값 · 분위수 · 꼬리 지연 · 히스토그램 · 서비스 수준 목표
첫 바이트까지의 시간을 이루는 단계
DNS 조회 · TCP 핸드셰이크 · TLS 핸드셰이크
웹에서 이 값을 드러내는 이름
첫 바이트까지의 시간 · Server-Timing · PerformanceNavigationTiming · PerformanceServerTiming · 브라우저 · 사용자 에이전트 · 개발자 도구 · 인터페이스
지연이 쌓이는 백엔드 자원
지연을 기록·추적하는 데이터베이스 설정
PostgreSQL · log_min_duration_statement · 애플리케이션
ping 으로 재는 왕복 지연과 그 원인
다른 이름: latency · 레이턴시 · 지연 시간