Saturation: How Your Software Will Fail at Scale

Lobsters · 2026.08.06

포화(Saturation): 규모에서 시스템이 무너지는 방식

소프트웨어 시스템이 실패하는 방식은 다양하지만, 그 중에서도 포화(Saturation) 는 가장 예측 가능하면서도 가장 많이 간과되는 실패 유형이다. 포화란 시스템의 특정 자원—CPU, 메모리, 스레드 풀, 데이터베이스 커넥션—이 처리 가능한 한계에 도달했을 때 발생한다. 트래픽이 서서히 증가하는 환경에서 포화는 갑작스럽게 찾아오는 것이 아니라, 오랫동안 쌓인 부채가 임계점을 넘는 순간 드러난다.

포화가 실무에서 위험한 이유

Java 웹 백엔드 환경에서 포화는 종종 다음과 같은 형태로 나타난다.

  • 스레드 풀 고갈: Tomcat 혹은 Jetty의 max thread 수를 초과하면 요청이 큐에 쌓이고, 결국 타임아웃과 503 응답이 쏟아진다.
  • DB 커넥션 풀 대기: HikariCP의 maximumPoolSize를 넘어선 요청은 connectionTimeout 설정 시간만큼 블로킹되다가 예외를 던진다.
  • 힙 메모리 압박: GC가 메모리를 확보하지 못하면 Full GC가 빈번해지고, 결국 OutOfMemoryError로 이어진다.
// HikariCP 포화 방지 예시: 적절한 풀 크기와 타임아웃 설정
HikariConfig config = new HikariConfig();
config.setMaximumPoolSize(20);
config.setConnectionTimeout(3000);      // 3초 초과 시 예외
config.setLeakDetectionThreshold(5000); // 5초 이상 반환 안 되면 경고

핵심은 포화가 발생하기 에 신호가 존재한다는 점이다. 평균 응답 시간이 서서히 늘어나거나, 큐 길이가 지속적으로 증가하거나, GC 빈도가 높아지는 것은 모두 포화의 전조다.

포화를 다루는 실무 전략

포화에 대응하는 방법은 단순히 자원을 늘리는 것이 아니다. 측정 → 한계 식별 → 제어 의 순서가 중요하다.

백프레셔(Back-pressure) 적용이 핵심 전략이다. 시스템이 감당할 수 없는 요청을 무한정 수용하는 대신, 명시적으로 거부하거나 대기열의 크기를 제한해야 한다. Resilience4j의 Bulkhead 패턴이나 RateLimiter는 이를 코드 레벨에서 구현하는 실용적인 수단이다.

// Resilience4j Bulkhead로 동시 실행 수 제한
BulkheadConfig bulkheadConfig = BulkheadConfig.custom()
    .maxConcurrentCalls(10)
    .maxWaitDuration(Duration.ofMillis(500))
    .build();

Bulkhead bulkhead = Bulkhead.of("orderService", bulkheadConfig);

또한 SLO(Service Level Objective) 기반의 포화 임계값 설정이 필요하다. CPU 사용률이 70%를 넘거나, 스레드 큐 대기가 일정 수치를 초과하면 알림이 발생하도록 Prometheus + Grafana 등을 활용해 가시성을 확보해야 한다. 자원이 100%가 되기 전에 이미 성능은 급격히 저하된다는 점을 기억해야 한다.

정리

  • 포화는 자원이 한계에 도달할 때 발생하며, 스레드 풀·DB 커넥션·메모리가 대표적인 병목 지점이다.
  • 임계점에 도달하기 전 전조 신호(응답 시간 증가, 큐 증가)를 모니터링으로 감지하는 것이 핵심이다.
  • 자원을 무한정 늘리기보다 백프레셔와 Bulkhead 패턴으로 시스템이 스스로를 보호하도록 설계해야 한다.
Source
Lobsters
원문 보기 →
← 목록으로 돌아가기