MCP 서버 캐싱: 87번의 장애를 겪으며 배운 점

내 MCP 서버에는 캐싱이 필요 없다고 생각했다.

지식 베이스에는 수천 개의 항목만 있었고, 쿼리 속도도 빨랐다. 하지만 내 생각이 틀렸다.

87번의 운영 환경 장애와 3일간의 타임아웃 디버깅 끝에 뼈아픈 교훈을 얻었다. 모든 MCP 서버에는 캐싱이 필요하다. 규모가 작은 서버라도 마찬가지다.

어떤 일이 있었는지 설명하겠다.

서버는 노트를 찾기 위해 시맨틱 검색(semantic search)을 사용했다. 대부분의 경우 잘 작동했다. 하지만 문제가 발생하기 시작했다.

  • 요청 도중 연결이 끊김.
  • Claude Desktop이 60초 후 타임아웃 발생.
  • Nginx가 504 Gateway Timeout을 반환.

가장 최악인 점은? 반복되는 쿼리에서만 이 문제가 발생했다는 것이다.

사용자가 동일한 질문을 두 번 던지면, AI가 생각하는 동안 연결이 유휴(idle) 상태가 될 수 있다. 이때 프록시가 연결을 끊어버린다. 그러면 Claude는 자동으로 요청을 재시도한다. 이제 동일한 검색이 동시에 두 개나 실행되는 상황이 된다.

재시도가 여러 번 발생하면 데이터베이스 커넥션 풀(connection pool)이 고갈된다. 그리고 모든 시스템이 멈춰버린다.

AI의 최종 응답을 캐싱해서는 안 된다는 것을 깨달았다. MCP에서 그것은 너무 복잡하다. 대신, 가장 부하가 큰 부분인 시맨틱 검색과 콘텐츠 페칭(content fetching)을 캐싱해야 했다.

나는 2단계 캐싱 전략으로 전환했다:

• 1단계: 빈번한 쿼리를 위한 인메모리(In-memory) 캐시. 매우 빠르다. • 2단계: 재시작 시에도 공유되는 데이터를 위한 Redis 캐시.

또한 이를 제대로 구현하기 위해 다음 규칙들을 따랐다:

  • 쿼리 정규화: 쿼리를 소문자로 변환하고 불필요한 공백을 제거한다. 이렇게 하면 캐시 히트율(cache hit rate)이 높아진다.
  • 스트림이 아닌 결과를 캐싱: 검색 결과만 캐싱한다. 검색 과정이 전체 시간의 95%를 차지하기 때문이다.
  • 우아한 성능 저하(Graceful degradation): Redis가 다운되더라도 서버는 여전히 작동한다. 단지 데이터베이스에 직접 접근할 뿐이다. 캐싱은 최적화를 위한 것이지, 요청 성공을 위한 필수 조건은 아니다.

결과는 엄청났다.

평균 검색 시간이 320ms에서 12ms로 줄었다. 27배나 빨라진 것이다. 전체 캐시 히트율은 73%에 달한다. 대부분의 쿼리는 데이터베이스에 아예 접근조차 하지 않는다.

MCP 서버를 구축한다면 이렇게 하라:

  • 개인용: 인메모리 캐시를 사용하라. 무작위 타임아웃을 방지할 수 있다.
  • 공개 서비스용: Redis를 활용한 2단계 방식을 사용하라. 충돌을 방지하고 속도를 높여준다.

캐싱은 신뢰성에 관한 문제다. 재시도와 연결 실패의 악순환을 끊어준다.

Source: https://dev.to/kevinten10/mcp-server-caching-what-i-learned-adding-caching-to-my-mcp-knowledge-base-after-87-production-261b

Optional learning community: https://t.me/GyaanSetuAi