2P by GN⁺ | ★ favorite | 댓글 1개
  • 단일 SaaS 애플리케이션의 Postgres 병목이 심해져 CPU 사용률이 60~80%에 머물렀고, 한때 100%까지 올라 짧은 장애로 이어짐
  • 더 큰 DB 인스턴스로 바꾸는 수직 확장은 이미 한계에 도달해, 같은 방식으로는 부하 증가를 더 흡수하기 어려웠음
  • 쓰기 샤딩과 마이크로서비스는 용량과 운영 탄력성을 높일 수 있지만, 백업·모니터링·마이그레이션·ORM·네트워크 토폴로지까지 복잡성 비용을 계속 남김
  • 실제로는 3개월간 무거운 쿼리 제거, Rails 코드 최적화, Postgres 튜닝을 진행하고 일부 비싼 읽기 전용 쿼리를 복제 DB로 분리함
  • 그 결과 주간 최대 DB CPU 사용률이 90%에서 30%로 줄어, 차세대 아키텍처로 넘어가기 전에 현재 시스템을 더 오래 활용할 여지가 생김

Postgres 병목과 수직 확장의 끝

  • 단일 SaaS 애플리케이션의 부하가 커지면서 Postgres 성능이 핵심 병목이 됨
    • CPU 사용률은 60~80% 구간에 머물렀음
    • 적어도 한 번은 100%까지 치솟아 짧은 장애가 발생함
  • 이전에는 DB가 바빠질 때마다 더 큰 인스턴스로 교체해 시간을 벌었음
    • 이 방식 덕분에 기능 개발 같은 다른 일에 집중할 수 있었음
    • 당시에는 이미 가장 큰 인스턴스를 사용 중이라 더 이상 수직 확장이 불가능했음

매력적이지만 비싼 차세대 아키텍처

  • 논의된 주요 선택지는 쓰기 샤딩마이크로서비스였음
  • 쓰기 샤딩은 독립 DB 클러스터를 두고, 파티셔닝 전략에 따라 데이터를 특정 DB에 쓰는 방식임
    • 잠재적으로 용량을 2~3자릿수까지 늘릴 수 있음
  • 마이크로서비스는 모놀리스를 여러 서비스로 나누고, 각 서비스가 자체 데이터 저장소를 갖도록 함
    • 서비스별 워크로드에 맞춰 데이터 저장소를 고를 수 있음
  • 두 방식 모두 장애 허용성과 운영 회복력 측면에서 선택지를 넓히지만, 당장의 목표는 DB 성능을 통제 가능한 상태로 되돌리는 것이었음

복잡성은 한 번 도입하면 계속 비용을 만든다

  • 복잡성 증가는 새 구조를 구현하는 비용을 넘어 이후의 주의력 비용으로 이어짐
  • DB 샤딩을 선택하면 앞으로의 기술 결정마다 새 복잡성을 함께 다뤄야 함
    • 백업
    • 모니터링
    • 마이그레이션
    • ORM
    • 네트워크 토폴로지
  • 마이크로서비스도 비슷한 부담을 만들며, 추가 아키텍처를 유지하느라 기능 전달이 늦어지거나 포기될 수 있음

먼저 현재 시스템에서 여유를 찾기

  • 큰 아키텍처 전환이 필요해 보일 때도 기존 시스템에는 추가 여유가 남아 있는 경우가 많음
  • 워크로드 조정, 성능 튜닝, 보조 시스템 추가만으로 몇 달 또는 몇 년의 시간을 벌 수 있음
  • 이런 선택지가 실행 가능하다면 차세대 시스템을 새로 구축하기 전에 먼저 시도할 만함

실제로 수행한 최적화

  • 첫 번째 작업에서는 두 엔지니어가 약 3개월 동안 주로 DB 성능 문제를 다룸
    • 단일한 묘책은 없었음
    • 텔레메트리로 무거운 쿼리를 찾음
    • Rails 코드베이스에서 쿼리 발생 지점을 파악함
    • 쿼리를 최적화하거나 제거함
    • 여러 Postgres 설정을 튜닝함
  • 두 번째 작업에서는 다른 두 엔지니어가 코드베이스를 수정해 일부 비싼 읽기 전용 쿼리를 복제 DB에서 실행하게 함
    • 웹 클라이언트의 폴링이 유발하는 가장 빈번한 SELECT 쿼리를 주 DB에서 분리함

결과와 운영 원칙

  • 두 작업을 합쳐 DB의 주간 최대 CPU 사용률이 90%에서 30% 로 줄어듦
    • 주간 최대 CPU 사용률: {l:90,30}
  • CPU 여유가 크게 생겼고, 주 DB에서 부하를 덜어낼 수 있는 능력도 커짐
  • 코드베이스 여러 부분을 건드리고 여러 개발자가 협업하면서 기존 시스템에 대한 분산 지식도 쌓임
  • 복잡성이 항상 나쁜 것은 아니며, 언젠가는 DB 아키텍처의 근본 한계에 도달하기 전에 더 복잡한 구조로 넘어가야 함
  • 그 전까지는 현재 시스템을 먼저 짜내면 가능한 한 지루하고 단순한 시스템으로 더 오래 일할 수 있고, 비용과 실용성 면에서도 유리함

댓글과 토론

Hacker News 의견들
  • 데이터베이스 성능 문제를 보니 새 프로젝트에 대한 내 가장 뜨거운 생각은, 애플리케이션의 핫 경로가 조인을 전혀 쓰지 않게 설계하자는 것임
    저장공간은 싸니 전부 비정규화하고 트랜잭션 안에서 모두 갱신하면 됨. 조인을 없애면 얼마나 빨라지는지 정말 놀라움. 임시 분석 질의는 분석용 다른 데이터베이스로 복제하면 됨
    Amazon의 DynamoDB에는 복잡한 감정이 있지만, 제대로 쓰려면 먼저 사용 패턴을 계획하고 스키마를 나중에 잡아야 한다는 점은 관계형 데이터베이스에도 가져올 만함. 요즘은 분석 목적이 아니라면 조인이 불필요하다고까지 봄. 주요 데이터베이스는 ACID 속성이 있고 저장공간은 말도 안 되게 싸니 그냥 비정규화하면 됨
    핫 파티션을 막으려면 정수 대신 UUID에 가까운 것을 쓰는 편이 좋음. 만능은 아니고 단점도 있지만, 언젠가 무너질 정수의 뛰어난 성능보다 수평 확장 가능한 “항상 괜찮은” 성능에 익숙해질 수 있음
    더 뜨거운 생각으로는 모든 컬럼에 인덱스를 걸자는 것도 있지만, 그건 다른 날 얘기할 주제임

    • 솔직히 전혀 동의할 수 없음. 스타트업을 만들며 1~5년 차에는 성능에 거의 신경 쓰지 않았고, 6년 차에 특정 테이블 성능 문제가 터져 엔지니어 몇 달 치 작업으로 최적화했음
      기술 부채 관점에서는 처음부터 모든 것을 빠르게 만들려 했다면 훨씬 비쌌을 것임. 개발 속도가 크게 느려져 몇 번의 위기에서 실패했을 가능성이 큼
      대신 실제 필요보다 기계 비용을 월 몇천 달러 더 냈고, 엔지니어를 충분히 뽑지 못하고 기능 개발의 기회비용이 컸던 시기에 몇 달치 인력을 아꼈음. 어디가 병목일지 미리 알 수 없었기 때문에, 모든 것을 처음부터 빠르게 하려면 10~20배는 더 많은 작업이 필요했을 것임. 일부 병목은 의외였음
      조인은 대규모에서는 해로울 수 있지만, 대부분의 스타트업은 적어도 초기에 규모 문제가 없음. 비정규화는 좋은 최적화일 수 있지만, 변경 때마다 모든 복사본을 동기화하는 속도 비용을 치름. 누군가는 비정규화된 비정본 필드를 갱신하지 않는 버그를 만들어 사용자에게 오래된 데이터를 보여주게 됨. 보통은 조인을 쓰고 나중에 read-aside 캐시 등으로 최적화하는 편이, 스키마를 비틀어 놓는 것보다 총비용 면에서 싸다
    • 제대로 인덱스가 잡혀 있고 같은 서버 안에서 이뤄지는 조인이라면 성능에 큰 영향을 주면 안 됨. “조인을 없애면 훨씬 빨라진다”는 말은 조인을 올바르게 사용한다면 사실이 아님
      반대로 데이터 하나를 1곳이 아니라 20곳에 쓰는 것은 성능상 훨씬 느리고, 질의도 엄청나게 복잡하고 버그가 나기 쉬워짐. 18곳은 갱신했지만 2곳을 빼먹는 일이 생김
      비정규화의 장점으로 싼 저장공간을 들지만, 여기서 저장공간은 가장 작은 문제임. 훨씬 커진 버그 표면적과 나쁜 쓰기 성능이 진짜 문제이고, 그 쓰기 성능은 읽기 성능까지 쉽게 갉아먹을 수 있음
    • 애플리케이션에는 “높은(tall)” 애플리케이션과 “넓은(wide)” 애플리케이션이 있음. 데이터베이스 설계와 최적화에 대한 조언은 거의 전부 높은 애플리케이션을 위한 것임
      높은 애플리케이션은 기본적으로 한 가지 일만 하고, 나머지는 그 일을 보조함. 떠올릴 수 있는 대부분의 빅테크가 여기에 해당함. 데이터 모델에서 정말 중요한 구동 개념이 몇 개뿐임
      Facebook은 사실상 사람, 게시물, 광고만 있음. Netflix는 계정과 쇼 정도임. Amazon 제품은 판매자, 구매자, 상품이 핵심이고 물류용 몇 가지가 뒤에 더 있을 수 있음
      높은 애플리케이션이 많은 이유는 쉽기 때문임. 흔히 “엔터프라이즈”라고 불리는 넓은 애플리케이션보다 훨씬 쉬움. 엔터프라이즈 소프트웨어가 나쁜 이유는 어렵기 때문이고, 여기에 가장 덜 탐사된 영역과 막대한 기회가 있음. Oracle 같은 기존 플레이어들은 여기서 처참히 못하고 있으며, 높은 애플리케이션식 사고방식으로 들어가면 당신도 그럴 것임
      “조인을 절대 쓰지 말라”, “단일 테이블 중심으로 설계하라” 같은 조언은 높은 애플리케이션에는 말이 되지만, 넓은 애플리케이션에는 끔찍한 조언임. 아주 높은 애플리케이션 회사들이 핵심 역량 밖의 일을 시도하다가 처참히 실패하는 일이 종종 보이는데, 이 조언을 신성시하는 사람들로 채워져 있기 때문임
      이 조언은 이미 성공했고 쉬운 일을 하며 낮게 달린 과실을 다 딴 회사들을 위한 것임. 아직 성공의 희생자가 되지 않은 높은 애플리케이션조차 성능을 위해 데이터 모델을 난도질할 필요가 없음. 이미 거대하게 성공해 마지막 성능 한 방울을 짜내는 회사들이나 그런 고민을 하는데, 그런 회사들이야말로 조언이 가장 덜 필요함. “FAANG이 하니 너도 해야 한다”, “사용자가 10억 명이면 어쩔 건데?” 같은 높은 애플리케이션 중심 조언은, 수십억 명에게 광고를 보여주는 것보다 더 흥미로운 일을 하려는 사람들의 머리를 오염시킴
    • 지난 몇 년간 큰 테이블의 조인 성능 문제보다 개발자들이 비정규화에 기대서 생긴 고통을 더 많이 봄
      큰 조인은 나중에 구체화 뷰로 밀어 넣거나 컬럼 저장소로 ETL하는 등 성능을 고칠 방법이 있음. 하지만 누군가 subtotal_cents 컬럼을 Order, Invoice, Payment, NotificationEmail, UserProfileRecentOrders 모델에 복사해 두고, 296곳에서 참조하거나 갱신하게 만들면 정상 상태로 돌아오는 길이 길어짐
    • 조인 자체가 성능 병목인 경우는 본 적이 거의 없음. 관찰한 단일 원칙이 있다면 “테이블을 너무 크게 만들지 말라”임
      대개 문제는 이력 레코드 성격의 테이블에서 생김. 일상 운영에 필요한 데이터는 실제 테이블 안의 극히 일부인데, 어떤 인덱스가 있든 거대한 테이블에서의 작업은 느려지기 마련임. 인덱스를 더 추가하는 행위 자체도 문제가 됨
      적어도 전통적인 관계형 데이터베이스에서는 모든 컬럼에 인덱스를 거는 것만으로는 부족하고, 사용될 수 있는 올바른 컬럼 조합 인덱스를 잡아야 함. DynamoDB는 다를 수 있음
  • 가진 것을 최대한 짜내고, 한동안 짜낸 뒤에는 문제를 바라보는 방식을 바꿔 여기, 여기, 그리고 여기까지 짜내면 갑자기 남은 것이 많다는 걸 깨닫게 됨
    거대한 모놀리스를 두 달쯤 최적화해, PM과 팀이 더 이상 짜낼 게 없다고 보던 2천 RPS 미만 상태에서 하드웨어 교체로 3200 RPS 미만까지 올렸고, 며칠 손봐 4천 RPS, 조금 더 노력해 1만 RPS, 일주일쯤 뒤 4만 RPS까지 올렸음
    “이 정도면 충분하다, 더 갈 필요 없다”는 말이 나왔지만, 꽤 많은 것을 바꾸자 단일 장비에서 200만 RPS 이상으로 뛰었고, 한 달 뒤에는 낮은 지연시간으로 4천만 RPS 이상을 안정적으로 처리하게 됨. 아직도 조금 더 밀어붙일 여지는 있음
    지금은 끌어낼 수 있는 용량의 5%도 쓰지 않음. 단순히 문제를 생각하는 방식을 바꾼 것만으로 그 정도 변화가 있었음. 낡은 서버에서 새 서버로 옮긴 것만으로는 1800 RPS에서 3000 RPS 조금 넘는 수준으로 오른 게 전부였음. 하드웨어 추가는 근본 문제를 고치지 않았고, 복잡도 추가는 문제를 뒤로 미룬 것뿐이었음. 문제를 생각하는 방식을 바꾸자 문제 자체와 답이 바뀌었음

    • 모놀리스가 무엇을 그렇게 형편없이 하고 있었기에 그 정도 성능을 짜낼 수 있었는지 정말 궁금함. 질의가 엉망이었는지, 질의가 너무 많았는지, 캐시가 전혀 없었는지, 동시에 처리할 수 있는 일을 동기적으로 하고 있었는지 궁금함
    • 이 요청들의 내용이나 프로토콜이 무엇인지, 요청과 응답 각각의 평균 페이로드 크기가 어느 정도인지 공유해 줄 수 있는지 궁금함
  • “모놀리스를 여러 상호 연결된 서비스로 쪼개고, 각 서비스가 자기 조건에 맞게 확장 가능한 자체 데이터 저장소를 갖게 한다”는 말에 덧붙이면, 이 시점에 가능한 모든 마이크로서비스를 다 분리할 필요는 없음. “어떤 분리가 가장 큰 영향을 줄까?”를 물으면 됨
    내 경우에는 Mongo에서 일부 시계열 데이터를 Cassandra로 분리했음. Cassandra의 테이블 구조가 훨씬 잘 맞았음. 그 데이터셋은 잘 정의된 스키마를 가지고 있었고, Cassandra는 데이터를 훨씬 효율적으로 패킹할 수 있었음. 그 부분에서는 JSON 문서의 유연성이 필요 없었음
    그 데이터가 전체 데이터의 대부분이었기 때문에, 그 뒤로 Mongo는 꽤 만족스러운 상태가 됨. 필요한 분리는 단 하나였음. 기술적으로는 전후 모두 모놀리스였고, 같은 서비스가 두 데이터베이스에 쓰게 되었을 뿐임
    아이러니하게도 나중에 어떤 탁상공론형 아키텍트가 모든 데이터를 JSON 문서 저장소로 합치고 싶어 했고, “우리는 이미 그 길을 가 봤고 끝이 어딘지 안다”는 식의 논의가 여러 번 이어졌음

    • 마이크로서비스라는 아이디어가 너무 당연한 “해결책”처럼 던져지는 게 흥미로움. 그렇지 않음
      수직 확장이 당연한 첫 해법이어야 함. 많은 사람이 놓치는 점이고 글에서도 일부 다루지만, 수직 확장은 데이터베이스의 일관성을 깨지 않는 수평 확장에 가까움
      짜낼 수 있는 여지는 많고, 성능 문제가 생겼다고 해서 조인이나 데이터 검증을 무시하는 등의 반패턴을 쉽게 던질 필요가 있는 경우는 드묾
    • 글에서도 어떤 의미로는 분리를 했음. 특정 무거운 SELECT 질의를 복제본으로 넘겼기 때문임
    • 재미있게도 나는 반대 문제를 자주 겪음. 상대적으로 수명이 짧고 자주 갱신되는 데이터에 Cassandra가 왜 맞지 않는지 계속 설명해야 함. tombstone 때문임
    • 서버 하나가 두 데이터 저장소에서 읽을 수 있다는 점도 중요함. 형제 댓글에서 말한 읽기 복제본처럼, 같은 프로세스 안에서 Postgres와 Redis를 함께 읽지 못하게 막는 것은 없음
  • 개발자들이 EXPLAIN/ANALYZE 결과를 읽고 올바른 인덱싱과 질의 최적화를 할 줄 알면, 수많은 과설계 결정을 피할 수 있음
    질의를 기록하고, 매우 자주 실행되거나 실행 시간이 긴 것을 걸러내고, 자주 실행되는 것은 캐시하고, 무거운 것은 최적화하면 됨. 이를 체계적으로 하면 시스템이 더 건강해짐
    내 경험상 크게 도움이 되는 것들은 APM, 느린 질의 로그, 데이터베이스 읽기/쓰기 복제본, 파티셔닝과 샤딩임

    • explain 출력을 읽는 것 자체도 꽤 큰 과제일 수 있음. 데이터베이스는 완전히 별개의 세계이고, 직접 SQL을 거의 쓰지 않는다면 특히 그렇다
      https://explainmysql.com 같은 도구는 실제로 무엇을 최적화해야 하는지 더 명확히 보여줘서, 데이터베이스를 설정할 정도의 지식은 있지만 내부 사용 방식을 이해할 정도는 아닌 개발자에게 더 쉬운 체계가 됨
      누군가는 이미 스키마와 로그를 넣으면 마법처럼 개선할 SQL을 돌려주는 AI 시스템을 만들고 있을 것 같음. 믿을지는 모르겠지만, 많은 회사가 전담 DBA를 뽑기보다 그런 걸 쓰고 싶어 할 것임
    • SQL 실행 계획을 이해할 만한 좋은 자료가 있는지 궁금함. 현재 프로젝트에서 MS SQL Server 질의 성능 문제를 많이 겪고 있음
      질의마다 항상 인덱스 힌트를 지정해야 하는지 모르겠음. 인덱스가 존재하는데도 질의가 그 인덱스를 쓰지 않는 것처럼 보일 때가 있음. SQL 실행 계획을 쓰면 이 문제를 더 잘 이해할 수 있을 것 같음
    • “차세대 아키텍처” 세션을 몇 번이나 봤는지 셀 수 없는데, 대부분은 현재 구현에 대한 실사로 대체할 수 있었음
      나쁜 코드를 새 아키텍처로 고칠 수는 없음. 문제를 잠시 뒤로 미룰 뿐임
  • 좋아하는 말 중 하나가 떠오름. “원하는 군대나 나중에 갖고 싶어 하는 군대가 아니라, 지금 가진 군대로 전쟁에 나간다”
    이 말이 Donald Rumsfeld에게서 나왔다는 사실은 무시하고 싶을 수 있음. 그래도 “unknown unknowns” 같은 훌륭한 말들이 있긴 함
    팀에서 일할 때 이 말을 자주 떠올림. 모두가 완벽히 동의하거나 같은 이해와 공동 목표를 갖고 있지는 않음. 누군가는 비효율적이거나 내가 선호하지 않는 방식으로 일할 수 있음. 하지만 팀이 없는 것보다 있는 게 낫기 때문에, 가진 팀으로 목표를 달성할 최선의 방법을 찾아야 함
    시스템에도 잘 적용됨

    • Rumsfeld에게는 훌륭한 인용구가 꽤 많음. 대부분은 이라크 전쟁이 어떻게 그렇게 엉망이 되었는지 설명하는 맥락에서 나온 것들이고, 그 상황이야말로 그의 인용구만 보면 그가 제공할 수 있었을 것 같은 리더십이 절실했음
    • Rumsfeld라는 이름을 들을 때마다, 그가 10분 넘게 자신이 도마뱀이 아니라고 부인하기를 거부하던 장면이 떠오름
      https://www.youtube.com/watch?v=XH_34tqxAjA
    • “어떤 전투 계획도 적과 접촉한 뒤에는 살아남지 못한다”
      https://www.google.com/search?q=no+battle+plan+survives
    • Mattis의 “적도 한 표를 갖고 있다”도 현실을 상기시키는 좋은 말임. 사람들이 매우 화를 내긴 하지만, 보안, 프라이버시, DRM 같은 맥락에서 유용함
    • Steven Pressfield의 비슷한 문장도 좋아함. “운동선수는 고통 없이 깨어나는 날이 결코 오지 않는다는 것을 안다. 그는 다친 채로 경기해야 한다”
      이건 시스템보다는 우리 자신에게 더 잘 적용됨
  • 내 경험상 ORM 위에 만든 웹 앱에서는 데이터베이스 부하가 문제가 될 때 질의 최적화로 딸 수 있는 낮게 달린 과실이 엄청나게 많음
    “N+1 문제가 있는가” 같은 기본을 넘어서, ORM은 가끔 최적의 질의를 만들지 못함. 복잡한 프로덕션 웹 앱을 ORM 없이 만들고 싶지는 않지만, 때때로 ORM에서 탈출할 수 있어야 함
    프로덕션에서 실제 실행되며 가장 많은 리소스를 쓰는 질의를 프로파일링해야 함. 그 질의를 보고, 대상 테이블의 형태를 파악해야 함. ORM이 조인을 쓰고 있지만 실제로는 서브쿼리가 필요한 경우가 있고, 반대도 있음. 결과 일부를 미리 집계하거나 복잡한 조인에서 WHERE 조건을 조정해야 할 때도 있음
    반쯤 자주 실행되는 ORM 생성 질의가 데이터베이스를 죽이고 있었고, 실행에 20초 이상 걸리던 것이 몇 가지 작은 수정만으로 1초 미만이 된 경우도 봤음

    • 지금 Python ORM인 SQLAlchemy로 작업 중인데, INSERT에서 RETURNING을 쓰게 만드는 일이 사소하지 않음. 직관적이지 않은 expire_on_commit=False 옵션을 설정해야 함
      이 옵션도 RETURNING 사용을 보장하지 않고, 데이터베이스 드라이버와 데이터베이스가 지원하고 ORM이 그 드라이버/데이터베이스 조합에서 지원하면 사용하도록 되어 있음. 생성된 SQL이 로그에는 나오지만 실제로 검사할 API가 없어서, 자체 로그를 캡처해 긁어오지 않으면 테스트 스위트에서 RETURNING 사용을 강제할 방법이 없음. 다행히 Pytest 프레임워크 안에서는 아주 쉽긴 함
      ORM을 좋아하지만, 이런 건 여러 층위에서 답답할 만큼 복잡함. SQLAlchemy가 거대한 라이브러리라 모든 것이 쉬울 수 없다는 점도 이해함. 그래도 이 사례는 ORM 사용의 절충을 잘 보여줌
      Core에서 insert() 자체를 쓰면 원하는 대로 된다는 건 알고 있음. 여기서는 ORM 객체를 AsyncSession.add()하는 경우를 말하는 것임
    • ORM의 큰 문제는 객체지향이 대부분의 도메인에서 소프트웨어나 데이터를 조직하는 좋은 방식이 아니라는 데 있음
      대부분의 비즈니스 로직은 OOP보다 관계대수의 언어와 몇 가지 확장으로 표현하는 편이 더 나음
  • “복잡도 증가의 진짜 비용, 그리고 종종 훨씬 큰 비용은 주의력이다”라는 말은 곧 인지 부하이기도 함
    여전히 다운타임이 있는데 아무도 전체가 어떻게 동작하는지 모르는 마이크로서비스 시스템에서 일하는 데 지쳤음. 대부분은 실제로는 분산 모놀리스라 변경이 여러 서비스에 걸치고 순서대로 배포해야 함. 데이터는 복제해야 하고, 작업은 동기화해야 하며, 상태는 공유해야 함
    https://www.youtube.com/watch?v=y8OnoxKotPQ

    • “아무도 전체가 어떻게 동작하는지 모르는” 마이크로서비스는 매우 흔한 아키텍처 악취임. 원래 요지는 아무도 전체를 알 수 없고 알아야 해서도 안 된다는 데 있음
      그런데 시스템을 고치거나 수정하려면 누군가 전체를 알아야 한다면, 단일 책임이나 API를 통한 올바른 추상화 같은 규칙을 어겼다는 강한 신호임. 하지만 내 경험상 매우 흔함. N개의 마이크로서비스로 된 파이프라인을 디버깅하려면 종종 N개 서비스를 모두 로컬에서 실행하고 빌드해야 함
      엄밀히 말하면 이는 모놀리스에 네트워크 파티션과 무한한 빌드/배포 변동성을 더한 것임. 결국 어떤 인간 프로그래머의 능력도 넘어서는 극도로 어려운 작업 환경이라고 봄
  • 이 글이 좋음. 지난 몇 달간 매니저에게 같은 메시지를 전하려 했지만 별 성공은 없었음
    과부하된 Redshift 클러스터가 몇 번이나 무너졌고, 이미 RA3 노드로 최대치까지 올린 상태라 이제 “차세대 인프라”로 대규모 마이그레이션을 시작하려는 참임. 여기서 말하는 차세대 인프라는 CDK로 관리되는 Redshift 클러스터 세 개
    새 인프라는 현재 구성보다 훨씬 복잡하고, 모두가 기대하는 은탄환이 될지 확신하지 못하겠음

  • 지루한 해법임. 생명과 신체에 직결되는 초중요 시스템을 만드는 게 아니라면 기본 답이 되어야 함. 전체 소유 비용은 확실히 훨씬 낮아짐
    큰 중복 시스템을 운영할 리소스가 없다면, 중복 시스템이 추가한 복잡도 자체가 문제가 되는 경우를 너무 자주 봤음. 단순성에 집중하는 편이 낫다
    복잡도를 지원하려고 사람을 많이 더 붙여야 하는데 돈과 위험 평가상 그럴 필요가 없다면 더 단순한 쪽이 훨씬 낫다. 결국 앞으로 나아가려면 거대한 프로젝트가 필요해진 경우를 못 본 것은 아니지만, 때로는 그마저도 그 시점까지 복잡도를 계속 감당해 온 총합보다 작다고 생각함. 무엇을 만들고 있는지에 크게 달려 있음

  • 시스템에서 성능 최적화를 찾아 남은 성능을 짜낸 해법이 정말 즐거움
    Richard L. Sites의 책 _Understanding_Software_Dynamics_가 떠오름. 이 책은 지연시간 문제를 측정하고 고치는 법, 그리고 큰 규모에서 지연시간 감소가 얼마나 큰 절감을 만들 수 있는지를 가르침
    이런 문제를 측정하고 추론하기는 어렵지만, 해법은 종종 단순함. 예를 들어 9쪽에서 “[a] simple change paid for 10 years of my salary.”라고 말함
    언젠가 그렇게 영향력 있는 최적화를 해 보고 싶음

    • Google에서 그런 일을 한 번 이상 해 봤음. Google은 엄청난 양의 기계 리소스를 쓰고 훌륭한 성능 도구도 갖추고 있어서 비옥한 환경임
      https://research.google/pubs/pub36575/
      다만 주변에 똑똑한 사람도 많아서 큰 기회를 찾았다면 다른 사람이 뛰어들지 않은 이유가 보통 있음. 기술적 이유일 수도 있고 조직적 이유일 수도 있음
      후자의 예로, Google은 보통 리소스 압박이 있을 때가 아니면 이런 종류의 일을 크게 보상하지 않음. 어떤 최적화로 동료 보너스 약 100달러를 받았을 수는 있지만, 10% 커미션이나 승진, 10년 동안 출근하지 않고 월급을 받는 권리 같은 건 전혀 아니었음. 일반적으로 회사는 엔지니어가 비용 절감보다 매출 성장에 일하기를 더 선호함. 이 정책이 맞는지는 내 급여 등급을 넘어서는 일임
    • 최종 해법에서 마음에 걸리는 점은, 가능한 가장 큰 설정으로 인스턴스를 올린 뒤에야 질의를 최적화했다는 것임
      이제는 관리 콘솔에서 몇 번 클릭해 설정을 더 올릴 수 없게 되었고, 그래서 용량 문제를 해결하려고 실제로 머리를 써야 했던 것 같음. 코드의 특정 부분을 더 일찍 최적화했다면 그렇게 큰 인스턴스 설정이 애초에 필요 없었을지도 모름