터무니없는 성공
(marginalia.nu)- Marginalia Search는 최근 변경으로 RAM 요구량을 절반으로 줄이고, 업그레이드 중 검색 엔진을 내려야 했던 운영 부담을 크게 낮춤
- 장애는 최신 릴리스 이후 색인 전환 과정에서 드러났고, URL 데이터베이스를 업그레이드 때 지우는 구조가 장시간 오프라인으로 이어짐
- URL 저장소는 거대한 MariaDB 테이블·인덱스 중심에서 단일 SQLite 데이터베이스와 프로세스 생성 64비트 URL ID 구조로 바뀜
- 역색인 생성은 밀집 term ID용 lexicon과 대규모 랜덤 쓰기를 버리고, 작은 preindex를 만든 뒤 병합하는 방식으로 전환됨
- 새 구조는 나쁜 배포에서도 몇 시간 안에 복구할 수 있는 파일 백업을 가능하게 하고, Wikipedia처럼 잘 변하지 않는 큰 데이터 처리 결과를 재사용하기 쉽게 만듦
한 주 만에 바뀐 검색 엔진 운영 조건
- Marginalia Search는 한 주 동안의 코딩으로 여러 장기 문제를 동시에 줄임
- 검색 엔진의 RAM 요구량이 절반으로 감소함
- 업그레이드 중 시스템을 오프라인으로 내려야 하는 필요가 사라짐
- 색인 가능한 문서 수의 하드 한계가 제거됨
- 코퍼스에 포함 가능한 키워드 수의 소프트 한계가 4배로 늘어남
- 낮은 성능의 하드웨어에서도 시스템을 운영할 수 있게 유지하는 것이 장기 목표였고, 이번 변경은 32GB RAM 개발 머신이 여유롭게 느껴지던 이전 개선보다 더 큰 폭의 개선임
장애를 키운 기존 업그레이드 구조
- Marginalia Search는 최신 릴리스 문제로 거의 일주일 동안 오프라인 상태가 됨
- 직접 원인은 비교적 사소한 확장성 문제들의 연쇄였지만, 정상적이면 1일, 문제가 있으면 2~3일 걸리는 프로세스를 몇 차례 재시작해야 했음
- 대부분의 재시작은 RAM 부족 때문에 발생함
- 장애가 사용자에게 크게 드러난 이유는 색인 전환 중 시스템을 오프라인으로 내려야 했기 때문임
- 기존 구조에서는 업그레이드 중 URL 데이터베이스가 지워졌고, 10억 행에 가까운 테이블에 행을 빠르게 삽입·갱신하는 작업이 병목이 됨
- 새 데이터를 적재하는 프로세스가 많은 RAM을 사용함
- 같은 방식으로 RAM을 쓰는 색인 서비스와 동시에 실행하기 어려웠음
- 기본 키가 32비트 정수라 20억을 넘기기 어려운 한계가 있었음
URL 데이터베이스 재설계
- URL 데이터베이스는 검색 엔진의 오래된 구성 요소였고, 초기 설계 결정이 현재 시스템 구조와 맞지 않게 됨
- 핵심에는 새 색인을 적재할 때만 쓰이고 기본 키 조회로만 읽히는 두 테이블이 있었음
- URL 테이블은 각 URL에 고유 숫자 ID를 할당함
- PAGE_DATA 테이블은 색인된 링크의 제목과 설명 같은 정보를 담음
- 도메인 내 경로 고유성을 유지하는 추가 인덱스가 있었지만, 그 역할에 비해 매우 컸음
- 새 구조는 두 테이블을 단일 테이블 SQLite 데이터베이스로 합치고, 로더 프로세스가 고유 ID 생성을 맡으며, 색인하지 않는 URL 목록은 별도 테이블로 다룸
- MariaDB와 SQLite를 함께 쓰는 방식은 관례적 선택은 아니지만, 두 저장소가 서로 다른 필요를 담당함
- 시스템에는 상대적으로 작은 영속적 세계관이 필요함
- 동시에 테라바이트 단위 상태를 플러그 앤드 플레이처럼 교체할 수 있어야 함
- DOCUMENT와 DOMAIN 테이블은 데이터베이스를 가로질러 느슨하게 관련되지만 실제 조인은 필요하지 않음
- 재난 상황에서는 DOMAIN 테이블의 중요한 부분을 DOCUMENT 테이블에서 재구성할 수 있음
인덱스 없이 64비트 URL ID 만들기
- 기존 URL 테이블은 32비트 ID를 사용했고, 정수 오버플로에 가까운 상태가 알려진 확장성 문제였음
- URL 데이터베이스를 지우던 이유 중 하나도 ID 컬럼이 넘치지 않게 하기 위해서였음
- 단순 카운터만으로는 URL 반복을 처리할 수 없고, 본 적 있는 모든 URL 컬렉션을 메모리에 유지하는 방식도 피해야 했음
- 새 방식은 데이터베이스가 ID를 할당하지 않고 64비트 ID를 직접 구성함
- 하위 26비트는 순번으로 사용함
- 26~56비트는 문서 ID로 사용함
- 최상위 비트들은 색인 정렬 트릭을 위해 예약함
- 이 배치는 도메인당 약 6,700만 문서를 허용함
- 영어 Wikipedia의 약 10배 규모임
- 도메인은 20억 개까지 허용함
- Marginalia가 웹 어디에서든 참조를 본 도메인 수의 거의 100배임
- 데이터 정합성 책임은 데이터베이스에서 데이터 생성 프로세스로 이동하지만, 그쪽에서 강제하는 비용이 훨씬 낮음
URL 재설계가 줄인 메모리 부담
- 거대한 인덱스와 핫·콜드 URL 데이터가 섞인 큰 테이블을 제거하면서 MariaDB 서버는 더 이상 36GB RAM을 필요로 하지 않음
- MariaDB에 남는 핫 데이터는 최대 수백 MB 수준이고, 서버에 할당할 RAM도 2GB 이상 필요하지 않을 가능성이 큼
- SQLite 데이터도 기본 키용 인덱스는 필요하지만, 실제 핫한 인덱스 부분은 1GB 미만일 것으로 예상됨
- 색인하는 데이터 양이 한 자릿수 규모로 줄어듦
- 인덱스 대상은 단일 64비트 long임
- 추가 유니크 제약이 없음
- 로더 출력이 파일 묶음이 되면서 자동 백업을 만들기 쉬워짐
- 나쁜 배포가 발생해도 이전처럼 거의 일주일이 아니라 몇 시간 안에 복구할 수 있음
역색인 생성 병목
- 로더는 URL 데이터베이스와 문서 중심의
(document, words[])저널을 출력하고, 검색에는 이를(word, documents[])형태로 전치한 색인이 필요함 - 기존 방식은 키워드 문자열을 조밀한 term ID에 매핑하는 lexicon을 사용함
- 첫 단어는 ID 0, 다음 단어는 ID 1을 받는 방식임
- 문자열은 64비트 해시로 long에 매핑됨
- 구조는 사실상 long-to-int 오픈 해시맵임
- 이 구조는 규모가 커지면 쉽게 가득 참
- Java는 20억 개를 넘는 배열을 허용하지 않음
- 사용 중인 구현은 10억 개 수준에서 이미 막힘
- 10억 엔트리에서는 RAM 12GB를 사용함
- 운영 중인 index service는 60GB RAM을 사용하고, 대부분은 오프힙 메모리임
- 온힙 크기가 32GB를 넘어 CompressedOOPs를 쓰기 어려운 점도 문제였음
기존 역색인 알고리듬의 쓰기 증폭
- 기존 알고리듬은 가장 큰 word ID를 찾고, 그 크기만큼 counts 배열을 만든 뒤, 단어별 문서 위치를 계산해 메모리 매핑 파일에 문서 데이터를 씀
- 이 방식은 word ID의 도메인이 조밀하게 채워져 있다는 전제 때문에 lexicon이 필요했음
- 더 큰 병목은 약 1TB 데이터를 거의 완전히 무작위 순서로 메모리 매핑 파일에 쓰는 과정임
- SSD는 읽기에서는 랜덤 접근을 잘 처리하지만, 작은 쓰기에서는 쓰기 증폭(write amplification) 문제가 생김
- SSD는 디스크의 한 바이트를 갱신할 때도 전체 페이지를 지우고 다시 써야 함
- 인접 쓰기를 모으는 캐시가 있지만, 테라바이트 규모의 무작위 쓰기에는 효과가 없음
- 1TB를 쓰는 동안 드라이브는 약 0.5PB를 쓰는 상황이 됨
- 임시 완화책으로
RandomWriteFunnel이 있었음- 쓰기를 작은 파일 버킷으로 먼저 나눔
- 이후 큰 파일을 순서대로 씀
- 원래 방식보다 낫지만 여전히 매우 느림
작은 preindex를 만들고 병합하기
- 새 역색인 생성은 모든 데이터가 메모리에 들어가는 작은 색인을 여러 개 만든 뒤 병합하는 방식임
- 정렬된 리스트 병합은 빠르고 기계식 하드디스크에도 맞는 작업임
- 이 방식에서는 lexicon이 필요 없어짐
- term ID로 64비트 문자열 해시를 직접 사용할 수 있음
- 초기에 생긴 문제는 입력이 압축되어 있다는 점과 색인 병합 방식이었음
- 압축 입력의 일부를 빠르게 읽는 대신, 여러 개의 작은 파일을 만들고 하나씩 읽음
- 색인을 만든 뒤 병합하는 대신, 색인을 만들기 전에 데이터를 병합함
- 새 추상화인
preindex는 단어 ID 배열, 카운트 배열, 문서 데이터 배열로 구성됨 - 입력이 작기 때문에 preindex 대부분은 RAM에서 만들 수 있고, 생성하면서 디스크에 커밋한 뒤 후속 병합 단계에서 최종 preindex를 만듦
- 최종 preindex는 기존처럼 words 테이블과 각 document block에 정적 btree 인덱스를 추가해 역색인으로 바뀌며, 이 과정은 비교적 빠름
운영과 데이터 재사용에 생긴 변화
- lexicon 제거로 RAM 요구량이 줄었고, 새 역색인 생성은 기존 알고리듬보다 더 빠름
- 기존 lexicon은 처리된 데이터 묶음마다 별도의 방언을 만들었음
- 같은 단어가 한 실행에서는 어떤 ID를 받고, 다른 실행에서는 다른 ID를 받을 수 있었음
- 이 때문에 모든 데이터를 동시에 처리해야 했음
- lexicon을 포기하면서 서로 다른 데이터 묶음을 병합할 수 있게 됨
- Wikipedia처럼 크고 드물게 바뀌는 데이터의 처리 결과를 재사용하고, 더 자주 변하는 색인 데이터와 결합할 수 있음
- 이번 변경은 거의 모든 알려진 확장성 문제와 운영상의 불편을 줄였고, 더 많은 부수 효과를 검토할 수 있는 상태를 만듦
댓글과 토론
Hacker News 의견들
-
나머지 세상과 정반대 방향으로 가는 프로젝트에서 이런 성공 사례를 보면 정말 기분이 좋아짐
Engildification이라고 부르고 싶은데, 이런 건 더 많아져야 함
Sleeping At Night 글도 마음에 들었고, 최근의 “Lie Still in Bed” 글과 함께 수면 훈련 문제를 고쳐보는 아주 단순한 선택지처럼 보임- 규모의 함수처럼 보임. 제품 뒤의 팀이나 회사가 클수록 엔시티피케이션 계수와 가능성도 커짐
Marginalia 검색 엔진은 작성자가 최근 전업으로 전환했고, 아는 한 팀 규모가 1명이라 엔시티피케이션 위험에서 가장 멀리 떨어져 있음. 오히려 이런 규모에서 보석이 만들어지고, 창의성·기발함·비전이 빛남
이 댓글은 “책상 일 그만두고 독립해서 일하자” 모임의 후원을 받았습니다 - 최근 글 3개를 보면 정말 신선함
리소스 소모를 절반으로 줄였고, 더 작은 화면에서 전보다 생산성이 높아졌으며, 밤에는 통나무처럼 깊이 잠 - 올해 수면 문제로 고생 중이라, 다른 사람에게 효과가 있었던 방법을 아는 게 꽤 유용함
관심 있는 사람을 위한 링크: https://www.marginalia.nu/log/86-sleep/ - engildification이라는 표현이 마음에 듦
- 그런 사례는 실제로 있음. 다만 Google로는 절대 찾지 못할 뿐임
- 규모의 함수처럼 보임. 제품 뒤의 팀이나 회사가 클수록 엔시티피케이션 계수와 가능성도 커짐
-
참고로 Hacker News에서도 봤을 법한 유료 검색 엔진 Kagi는 Marginalia를 데이터 소스 중 하나로 씀
https://help.kagi.com/kagi/search-details/search-sources.htm...
“non-commercial” 렌즈를 쓰면 Kagi 자체 색인과 몇몇 독립 소스의 결과와 함께 이런 결과가 우선됨 -
이 글을 보니, 인간은 인위적 제약 없이는 뛰어난 것을 만들기 어려운 존재가 아닐까 싶어짐
Marginalia가 터무니없이 효율적인 이유는 Victor가 실행 하드웨어와 메모리 용량을 의도적으로 제한하기 때문임
그냥 32GiB를 더 붙이면 당분간은 굴러가겠지만 비효율적인 설계는 남고, 나중에 더 복잡해진 상태에서 같은 문제가 터져 고치기 어려워질 수 있음
이 가정이 맞다면 요즘 소프트웨어가 부풀고 느리고 버그 많은 이유도 설명됨. 개별 소프트웨어가 독립적으로는 한계에 거의 닿지 않으니, 각 조각은 비효율적이어도 최신 M2 Pro와 GiB급 연결로 문제 지점을 계속 앞질러 가는 것임
결론은 스스로를 제한하는 편이 장기적으로 자신과 모두에게 낫다는 쪽일 수 있음- 대부분은 우선순위 문제임
많은 애플리케이션에서는 작은 최적화에 그렇게 많은 시간을 쓰는 게 말이 안 됨. 서버에 32GiB RAM을 50달러 미만으로 추가할 수 있는데, 개발자 40시간 이상을 시간당 최소 20달러로 쓰는 선택과 비교하면 비즈니스 관점에서는 답이 뻔함. 게다가 웹사이트가 일주일 내내 내려가 있었는데, 그 자체만으로도 대부분의 사업은 죽을 수 있음
기술자들은 이런 깊은 파고들기를 좋아해서 사소한 코드도 몇 년씩 미세 최적화하고 싶어 하지만, 끝없는 야크 털깎기로는 돈을 못 냄. 코드가 극소수의 기계에서만 돈다면 보통 그럴 가치가 없음. 이런 최적화는 유지보수하기 더 어려운 코드로 끝나는 경우도 많음
요즘 사용자 기기에서 도는 앱의 소프트웨어 비대화는 개발자 기기와 사용자 기기의 불일치에서 많이 온다고 봄. 개발자는 일을 하려면 고성능 워크스테이션이 필요하고 그걸로 기본 테스트도 하지만, 사용자는 5년 전 중급 이하였던 기기에서 실행함
관리자에게 “메모리 150MB를 절약할 수 있다”는 팔기 어렵지만, “메모리 150MB를 절약하면 사용자 10%에게 앱 성능이 끔찍함에서 간신히 쓸 만함으로 올라간다”는 팔 수 있음 - 이 관점에 동의함. 제약은 기지를 낳고, 데모신 결과물에만 해당하지 않음. 확장성 문제는 언젠가 만나게 되고, 늦게보다 일찍 다루는 편이 훨씬 쉬움. 소프트웨어가 Raspberry Pi에서 잘 돈다면 실제 서버에서는 말도 안 되게 빠를 것임
예전에는 실제로 그렇게 소프트웨어를 만들었음. 그래서 Pentium 1 같은 기계에서도 오늘날 기대하는 대부분을 갖춘 운영체제가 잘 돌 수 있었음. 반면 지금은 모든 축에서 리소스가 문자 그대로 천 배쯤 많은 스마트폰에서도 웹페이지 스크롤이 버벅임. Word 95 팀은 늘 한계와 성능 절충에 부딪혔고, 그 결과는 명확히 작동하거나 작동하지 않았음
그냥 RAM을 더 추가했다면 여전히 열등한 설계에 갇혔을 것이고, 곧 또 더 많은 RAM을 사야 했을 것임. 이번 변화의 놀라운 점은 리소스 사용량만 줄인 게 아니라, 여유 RAM 덕분에 디스크 캐시가 늘어 시스템이 더 유능하고 빨라졌다는 데 있음
[1] 예를 들어 이것은 단일 Pi에서 돌고, 업데이트를 허용하지 않기 때문에 운영 중인 Wikipedia보다 훨씬 빠름: https://encyclopedia.marginalia.nu/article/Hacker_News - 나도 좋아하는 가설임
그래서 오래된 컴퓨터가 좋게 느껴졌고, 오래된 게임도 그렇게 훌륭했을 수 있음
우리가 다루는 시스템의 복잡성과 관련이 있을지도 모름. RAM, 물리적 공간, 음식, 재료, 시간, 돈 같은 자원이 제한되어 있으면 어떻게 쓸지 계획해야 하고, 똑똑해질 수밖에 없음
사실상 무한한 자원이 있으면 만들고 싶은 대로 만들 수 있지만 최종 상태를 신경 쓸 필요가 줄어듦. 그냥 시작하고, 돌아갈 때 보면 됨
진짜 게이머는 아니지만, 몇 KB/MB ROM 카트리지 안에 그렇게 많은 감정과 모험과 즐길 시간을 담아낸 인간의 능력에는 늘 놀람. Ocarina Of Time ROM은 내가 iPhone으로 찍은 최근 사진 8장 정도 크기밖에 안 됨 - American Airlines는 1970년대 중반에 항공권 발권·예약 시스템인 SABRE를 System/360 메인프레임 두 대에서 돌렸고, 처리량은 초당 수천만 명령어 수준에 불과했음
Raspberry Pi 2는 초당 40억 개가 넘는 Dhrystone 명령을 처리할 수 있고, Pi 4는 초당 100억 개가 넘음
물론 현대 기준으로 1970년대 중반 SABRE는 항공사의 핵심 시스템치고 꽤 기본적이었지만, 이론적으로는 단일 Pi 2에서 100개 넘는 항공사의 단순화된 시스템을 동시에 돌리는 것도 가능함
현대 프로그램은 최적화와 매우 멀리 떨어져 있음. 수학 연산이 많은 경우를 제외하면 1000배나 10000배 개선도 가능함 - 개별적으로 한계에 닿지 않는다는 지점이 좋은 포인트임
Microsoft가 이 문제를 크게 겪는다고 봄. 5년 전 3000달러짜리 노트북도 Teams 통화, Office 몇 개, 탭 30개짜리 브라우저를 동시에 돌리면 받아들이기 어려울 만큼 느려짐
각각 따로 테스트하면 하나만 실행할 때는 괜찮지만, 실제 사람들은 그렇게 쓰지 않음
명확히 정의된 하드웨어에서 실행 시간을 제한하고, 명시적 결정 후에만 그 제한을 올리는 식의 인위적 한계가 해법일 수도 있음
다만 나는 성능 측면이 “데이터베이스에 멍청한 짓 하지 말고, 나머지는 고객이 그 걱정에 돈을 내지 않으니 신경 쓰지 말라”로 귀결되는 업무용 소프트웨어만 작성하므로 완전히 잘못 짚었을 수도 있음
- 대부분은 우선순위 문제임
-
marginalia.nu 업데이트가 올라올 때마다 늘 반가움. 이 사이트에서 소중한 사용자이고, 계속 올려주면 좋겠음
-
고마움. 검색 엔진 취미 프로젝트를 하고 있는데, 어쩐지 계속 “Magnolia” 변형만 검색하고 있었음. 내게는 Marginalia라는 이름이 잘 안 외워짐. 지금은 Searx를 파악하는 중임
Marginalia가 지난 하루, 지난주 같은 시간 필터 검색을 지원하는지 궁금함. 특수 키워드를 보면 검색 매개변수는 연도 기반만 받는 것처럼 보임
year>2005 (beta) 문서가 겉보기에는 2005년 이후에 게시됨
year=2005 (beta) 문서가 겉보기에는 2005년에 게시됨
year<2005 (beta) 문서가 겉보기에는 2005년 이전에 게시됨- 검색 색인이 한 달에 한 번 이상 갱신되지 않아서 그런 필터는 없음
연도 필터도 꽤 거칠고, 대부분의 웹페이지 날짜를 정확히 잡는 건 매우 어려움 - 틈새 콘텐츠용 검색 엔진임. 말하자면 주변부 콘텐츠용이라, Marginalia라는 이름이 이해됨
- 검색 색인이 한 달에 한 번 이상 갱신되지 않아서 그런 필터는 없음
-
“SSD가 디스크 어딘가의 단일 바이트를 업데이트할 때마다 전체 페이지를 지우고 다시 써야 한다”는 말이 실제로 SSD에 맞는지 궁금함
원시 플래시에서는 그렇지 않음. “빈” all-ones 값을 덮어쓰거나 1을 0으로만 바꾸는 경우라면 가능함. 쓰기는 읽기보다 몇 자릿수 느리지만, 지우기보다도 몇 자릿수 빠르고, 마모 예산에는 지우기만 반영됨
SSD 컨트롤러가 이 점을 활용하지 않는다면 자책골처럼 들림. 다만 내부가 실제로 로그 구조라면 못 할 수도 있겠다는 생각은 듦- 경우에 따라 다를 수 있음
예전에 인기 있는 플래시 칩 여러 개의 드라이버를 작성했는데, 당시 다룬 칩들은 모두 고정 크기 페이지 단위 입출력만 읽기와 쓰기에 사용했음
SSD는 칩들의 묶음이므로, SSD 안의 각 칩도 고정 크기 페이지 입출력만 지원할 것이라고 예상함 - 이 경우에는 사실상 전체 하드드라이브를 완전히 무작위 순서로 다시 쓰고 있었고, 이는 SSD에 최악의 경우임
보통 컨트롤러는 이런 최악 패턴을 피하려고 오버프로비저닝, 버퍼링, 쓰기 재정렬 같은 여러 기법을 쓰지만, 한계가 있음 - 내가 이해하기로는 이렇음
SSD에서 쓸 수 있는 가장 작은 단위는 페이지이고, 지울 수 있는 가장 작은 단위는 여러 페이지로 구성된 블록임
그래서 쓰기 작업이 페이지 안의 1바이트에만 영향을 줘도 SSD는 그 바이트만 지울 수 없음. 그렇다고 꼭 전체 블록을 지워야 하는 것도 아님
SSD는 “읽기-수정-쓰기” 유형의 작업을 수행할 수 있음. 바뀔 바이트가 들어 있는 전체 페이지를 SSD 캐시 버퍼로 읽고, 페이지 캐시에서 해당 바이트만 수정한 뒤, 비어 있는 새 블록을 지우고, 수정된 페이지를 캐시에서 새 블록에 쓰고, FTL 매핑 테이블을 새 블록의 갱신된 페이지를 가리키도록 업데이트함
따라서 1바이트만 바뀌어도 페이지는 다시 써야 함. 다만 그 안의 많은 페이지가 수정될 때까지 전체 블록 지우기는 피할 수 있음 - 정확히는 아님. 특정 플래시 주소에 페이지가 산다는 논리적 관점은 실제와 다름
쓰기가 일어나면서 페이지는 물리 장치 안에서 이동함. 드라이브 자체가 어떤 주소가 어떤 용도로 쓰이는지, 건강 상태가 어떤지 등을 담은 지도를 유지함. 일종의 희소 저장 방식임
TRIM 같은 명령이나 주기적 유지보수, 가비지 컬렉션도 있음
실제로 꽉 차지 않은 드라이브에 쓰기를 하면, 데이터가 들어갈 페이지를 찾고, 그곳에 데이터가 있는지 확인하고, 필요하면 읽기/수정/쓰기를 수행하고, 데이터를 어디에 쓸지 정한 다음 씀. 웨어 레벨링 때문에 원래 있던 곳으로 돌아가지 않을 가능성이 높음
컨트롤러가 성능을 위해 훨씬 복잡한 단계를 수행한다는 점은 맞음. 그래서 빈 새 드라이브가 한동안 더 잘 동작하고, 예비 페이지가 없는 오래된 꽉 찬 드라이브보다 실제로 느려지기도 함
출처를 덧붙이면, 캐시 일관성을 갖춘 메모리 매핑 플래시 가속기의 수석 엔지니어였음. Linux 사용자 공간에서 드라이브를 매우 효율적으로 매핑하게 했지만, 결국 한동안은 그냥 또 하나의 하드드라이브처럼 보이는 “쉬운” 프로그래밍 모델에 굴복했음 - 완전히 틀린 말임. 가장 원시적인 SSD 컨트롤러조차도 이런 상황을 완화하려는 시도는 함
- 경우에 따라 다를 수 있음
-
Mojeek의 내 상사도 아마 이와 매우 비슷한 길을 걸었을 것 같아 떠오름. 이 글이 과거 대화와 많이 맞닿아 있음
Mojeek은 2004년에 시작했고, 대부분 한 명의 개발자가 뼈대를 만들었으며 정보 검색과 인프라의 거의 전부를 구축했음
재정과 하드웨어 제약, 32비트 대 64비트 ID 결정, 샤딩, 업데이트 속도 같은 것들이 매우 익숙하게 들림
예전 Google의 “Google dance”도 생각남. 그때는 검색 결과가 한 달에 한 번 갱신됐고, 지금은 매일 출렁임. 모두 진화 과정이고, Marginalia가 빅테크 너머 웹을 보는 또 다른 관점을 제공하는 건 보기 좋음 -
읽기 좋았음
많은 사람이 최적화를 깊은 흑마법처럼 여기지만, 대부분은 일반적인 버그 수정보다 오히려 쉬움. 과도한 리소스 사용을 버그와 똑같이 취급하면 됨
쉽게 재현할 수 있는 버그 대부분은 고치기 위해 마법이 필요하지 않다고 봄. 버그를 찔러볼 수 있으면 보통 분류할 수 있고, 드문 설계 결함도 재현만 가능해지면 대체로 금방 드러남
아무도 성능을 비판적으로 들여다보지 않은 소프트웨어는, 쉽게 재현 가능한 버그 수백 개가 있는데 아무도 디버깅하지 않은 소프트웨어와 같음. 어려운 것에 부딪히기 전까지 꽤 오래 하나씩 깎아낼 수 있음
이런 태도는 예전에 사람들이 분기 대상 주소를 드럼 헤드가 CPU가 명령을 필요로 하는 순간에 맞춰 도착하게 조정하던 시절의 잔재에 가깝다고 봄. 자원이 극도로 제한되어 모든 것을 손으로 어셈블리로 작성하고, 전역 메모리 위치가 프로그램 단계에 따라 다른 의미를 갖던 때에는 이미 매우 똑똑한 사람들이 성능을 비판적으로 살폈기 때문에 그들이 못 찾은 것을 찾아야 했음. 현대 코드에서는 드문 일임- 대체로 동의하지만, 버그는 분할 정복 전략으로 훨씬 추적하기 쉬운 편임
요청 X를 서비스 Y에 보내면 버그가 재현된다면, 범인을 찾을 때까지 테스트 케이스를 점점 줄이면 됨
최적화는 흔히 아키텍처 문제임. 버퍼를 재사용할 수 있는데 복사하고 있는 경우도 있지만 그런 건 금방 고갈되고, 프로파일러가 필요한 정보를 알려줌
큰 성능 이득은 전체 데이터 흐름을 바꾸고, 상당한 부분을 제거해서 코드가 필요한 일을 가능한 한 적은 단계로 하게 만드는 데서 나오는 경우가 많음
- 대체로 동의하지만, 버그는 분할 정복 전략으로 훨씬 추적하기 쉬운 편임
-
왜 키-값 저장소가 아니라 SQLite를 골랐는지 궁금함. ID로 읽기만 원하고 다른 열은 필요 없어 보이니 관계형 데이터베이스는 불필요해 보임
- 그럴 수도 있었음. MariaDB에 임시 테이블을 만들어도 됐을 것임
SQLite는 단일 파일이라는 장점이 있음. 그래서 복사하거나 공유하는 등 멋진 일을 할 수 있음
- 그럴 수도 있었음. MariaDB에 임시 테이블을 만들어도 됐을 것임
-
제약이 혁신을 낳는 또 하나의 사례라서 좋음. 혁신은 풍요보다 한계에서 더 자주 발견됨