티스토리 뷰

터미널에 비친 로그에 좌절해서 얼굴을 찌푸렸다. 이 몹쓸 캐시는 얌전히 데이터를 가지고 있을 생각을 하지 않았다. 원래 캐시는 데이터를 유지할 의무가 없었지만, 몹쓸 개발자였던 나는 일관성 없이 사라지는 데이터들이 주는 시련에 짜증이 날 수밖에 없었다. 문득 어느 주말, 회의실에 모여서 보고 자료를 리뷰할 때의 기억이 떠올랐다. 기술적인 용어가 가득한 문서를 리뷰하다가 갑자기 상석에 앉아있던 임원 한 분이 '그런데 너네 캐시와 인메모리 데이터 그리드의 차이는 알고 쓴 거야?'라고 물어봤고, 속으로 아니 여기 앉아있는 사람들의 아키텍트 경력만 합쳐도 200년은 될 텐데 누가 그걸 몰라?라고 생각했지만, 고작 7년 차 막내 아키텍트였기에 차마 나서서 대답할 수 없었던 나는 토카막의 플라스마처럼 위태롭게 유지되던 몇 초간의 침묵이 의아할 수밖에 없었다.

사실 캐시와 인메모리 데이터 그리드는 혼동을 유발할 만큼 유사점이 많은 용어는 아니다. IO속도는 레지스터 - CPU 캐시 - 메모리 - 디스크 - 네트워크 순서로 느려지고, 각 단계 사이에는 넘볼 수 없는 격차가 존재하기에, 이 중에서 컨트롤이 가능한 가장 빠른 저장소인 메모리에 데이터를 저장한 것이 인메모리 데이터베이스이고, 빠르기는 했지만 휘발성인 메모리에 데이터를 저장하기는 것이 유류고 위에 고압산소 보관하는 것 마냥 불안해서 등장하기 시작한 것이 여러 메모리에 데이터를 분산해서 저장하는 인메모리 데이터 그리드의 시작이었다.

캐시는 이와 상관 없이, 그냥 액세스가 빈번한 데이터를 조금이라도 빠른 저장소에 중복 저장해서 성능을 높이기 위한 기법이고, 네트워크에서 받아온 데이터를 다시 받지 않기 위해 디스크에 저장한다거나, 디스크에서 읽어온 내용을 메모리에 올려둔다거나, 메모리에서 자주 읽는 데이터를 L3 캐시에 저장한다거나, 뭐 그런 식으로 낮은 속도의 저장소에 있는 데이터를 한 티어 높은 저장소에 복사해오는 식으로 많이 활용한다. 하지만 높은 티어의 저장소는 가격대 용량비가 매우 나빠지는 특성을 가지고 있었고, 그래서 많은 데이터를 저장하기 어려웠던 사람들은 고민 끝에 많이 쓰일 것 같은 데이터만 선별해서 캐시에 집어넣고 예상보다 많이 안 쓰인 데이터는 지워버리고 다른 데이터로 채우는 여러 알고리즘을 만들었다.

이렇게 전혀 다른 분야의 용어가 혼동될 수 있는 이유는, 데이터베이스에 전적으로 의존하던 옛날 개발과 달리 요즘에는 데이터베이스의 부담을 줄이고 전체 시스템의 성능을 높이기 위해 인메모리 데이터 그리드를 데이터베이스 캐시로 사용하는 구조가 너무 일반적이기 때문이었다. 그래서 많은 문서에서는 인메모리 데이터 그리드와 캐시가 뚜렷한 맥락 구분 없이 사용되는 경향이 있었고, 때문에 어디선가 인메모리 데이터 그리드 혹은 캐시라는 용어를 봤을 때 둘의 차이를 물어보는 것은 상대의 전투력을 측정하기에 좋은 질문이 될 수 있었다.

당시의 나는 아키텍트 경력이 몇 년 되기는 했지만, 그 기간 중 대부분을 아키텍트라기 보다는 개발자로 일해서 아직 해감이 끝나지 않은 바지락처럼 개발자 물이 빠지지 않은 상태였기에 그 질문에 대해서 아키텍트다운 간결함으로 조리 있게 대답할 자신은 없었지만, 그 일이 있기 얼마 전에 중간고사를 보면서 캐시 테이블을 수십 장 그렸던 기억이 있었기 때문에 개발자다운 복잡함으로 장황하게 대답할 자신은 있었다. 하지만 회의실의 무거운 침묵은 맹물에 담가 둔 조개처럼 내 입을 다물게 만들었고, 그래서 데이터가 삭제되니까 캐시라고 쓰는 게 맞다는 답변은 속으로 뻐끔거릴 수밖에 없었다.

 

하지만 캐시라고 아무 데이터나 삭제하는 것은 아니다. 상황에 따라 필요한 데이터가 캐시에 많을 수록, 그러니까 적중률이 높을수록 시스템의 성능은 비약적으로 증가했고 이 적중률은 근본적으로 파고들면 필요 없는 데이터를 골라내는 기술에 달려있다고 볼 수도 있었다. 그래서 많은 캐시들이 '가장 적게 쓰인 데이터를 삭제'하거나 '가장 많이 쓰인 데이터를 삭제'하거나 '가장 최근에 사용된 데이터를 삭제'하거나 '가장 옛날에 사용된 데이터를 삭제'하는 등의 알고리즘을 사용하고 있는데, 어떤 알고리즘을 사용하는 것이 정답인가는 데이터의 성격에 따라 달라지지만 어떤 알고리즘이 가장 인기가 있냐고 물어보면 보통 LRU(Least Recently Used), 그러니까 가장 가장 오랫동안 안 쓰인 데이터를 삭제하는 규칙이 인기가 있는 편이다.

레디스(Redis)는 '데이터'를 저장하기 위해 쓰이는 프로그램이고, 데이터를 메모리에 저장하깅에 '인메모리'이며, 이를 분산해서 저장하거나 복제해서 유지할 수 있기에 '그리드'이기도 하다. 레디스는 인메모리 데이터 그리드 중 가장 인기 있고 압도적으로 많이 쓰이는 프로그램인데, 이런 류의 프로그램에서는 특이하게도 '인메모리 데이터 그리드'의 맥락에서 사용되는 경우와 '캐시'의 맥락에서 사용되는 경우가 모두 어색하지 않은 프로그램이기도 하다. 그러니까 데이터를 저장하는 메인 데이터 저장소의 역할을 수행하는 유즈 케이스를 찾아보기 어렵지 않으면서도, 많은 시스템이 캐시 용도로 사용하기도 하는 것이었다. 하지만 많은 시스템이 레디스를 기본 설정으로 사용하기에 캐시 용도로 사용하기에는 조금 애매한 구석이 있었다.

레디스는 최대 메모리 사용량을 지정하거나, 지정하지 않을 수 있는데 만약 최대 메모리 사용량을 지정하지 않을 경우 무한한 데이터를 저장할 수 있다. 물론 무한하다는 것은 비유적인 표현이지만, 실제로는 레디스가 설치된 시스템의 메모리를 전부 사용하고 모자라면 디스크 스왑(Swap)을 통해 더 쓸 수 있으니까 거의 무한한 용량을 가지고 있는 것과 다름이 없다. 문제는 메모리에 저장될 데이터가 스왑을 통해 디스크에 저장되기 시작하면 시스템의 성능이 극심하게 떨어질 수 있다는 것이고, 뭐가 되었든 레디스가 메모리를 다 잡아먹으면 다른 프로세스들에게 큰 불편을 줄 수 있으므로 보통은 최대 메모리 사용량을 지정하는 것이 맞다. 문제는 최대 메모리 사용량을 지정할 경우 '메모리가 가득 찼는데 데이터가 더 들어오면 어쩔 건데?'라는 의문에 답을 해야 하고, 기본 설정에서 레디스는 이 경우 에러를 반환하고 데이터를 넣지 않는다.

이는 레디스를 캐시 용도로 사용할 경우 용납할 수 없는 동작이다. 때문에 보통 캐시 용도로 사용하는 레디스는 시스템의 용도에 따라 적당한 최대 메모리 사용량을 지정해주고, 보통 LRU 등의 알고리즘을 지정해두어서 잘 안 쓰이는 데이터를 자동으로 삭제하게 만들어주는 것이 올바른 설정이고, 올바른 설정을 했을 때 소박하지만 확실한 행복감을 느낄 수 있다. 그리고 올바르지 못한 설정을 가지고 있는 시스템에 이런 내용을 지적하는 것은 대박 확실한 행복감을 느낄 수 있는 일이었다.

하지만 이런 지적(知的) 지적(指摘)을 위해서는 많은 경험이 필요했는데, 안타깝게도 보통 이런 내밀한 구조를 파악하기 위한 경험이란 대체로 불행한 사고의 경험이었다. 어떤 새로운 기술을 도입하기에 적당한 시기를 가늠하는 내 기준은 보통 국내에 그 기술과 관련된 책이 몇 개나 출판되었는가 인데, 내가 레디스를 처음 만난 것은 1권 무렵이었다. 보통 어떤 기술을 내 프로젝트에 적용하기에 적당한 시기는 도전적인 개발자에게는 2~3권, 안정지향적인 개발자에게는 5~6권 무렵이기에 아무래도 그때는 레디스에 대한 경계심이 상당히 컸었다. 사실 당시에는 '랜덤 액세스 메모리'에 데이터를 저장한다는 사실이 생소하고 불안했다.

데이터는 시스템의 생명이었다. 시스템의 다른 모든 부분은 터져도 복구할 수 있지만, 데이터가 날아가면 많은 사람들이 다친다. 그래서 경험많은 오래된 아키텍트들은 느려 터져도 데이터는 안 터질 시스템을 설계하는 것이 훨씬 중요했고, 이런 관점에서 설계된 대부분의 시스템이 데이터를 디스크에 저장하고, 물리적으로 떨어진 또 다른 디스크에 저장하고, 그 저장이 완료되었음을 확인하고 나서야 사용자에게 오케이 사인을 보낸다. 그러니 이 관점에 도전하는 것은 우리 업을 관통하는 금기를 어기는 일이었다.

하지만 원래 하지 말라는 것이 더 하고 싶은 법이다. 사실 레디스는 메모리에 데이터를 저장하면서도 주기적으로 그 데이터를 디스크에 백업하기 때문에 생각처럼 데이터가 쉽게 날아가는 프로그램은 아니었지만, 상황에 따라 데이터의 일부가 유실될 가능성은 충분히 존재했다. 그래서 초기 레디스의 사용은 계속 말했던 캐시나 공유 메모리의 용도로 한정되는 경향이 있었고, 이는 당연히 메인 저장소로 별도의 데이터베이스를 두는 것을 상정한 설계였다. 하지만 나는 2권 무렵에 당면한 문제를 고민하다가 '그냥 레디스에 데이터를 다 저장하면 어떨까'라는 생각을 했고, 곧 그런 생각을 한 나 자신을 준엄하게 꾸짖었다. 어디서 감히 메모리를 메인 데이터 저장소로 쓸 생각을 하려는가, 어리석은 개발자여. 그런데...뭐...빠르기는 엄청 빠르겠네.

기본 설정에서 레디스는 1분 내에 10,000개의 명령이 들어오면 데이터를 백업한다. 혹은 300초 내에 10개, 900초 내에 1개의 데이터 변경 명령이 들어오면 데이터를 백업한다. 명령이 들어와야만 데이터의 변경이 생기므로, 중간에 서버가 꺼지거나 레디스가 꺼지는 불행한 사태가 발생할 경우 레디시는 최소한 900초 전의 데이터까지는 백업이 되었음을 보장해주고, 데이터의 저장 시간을 고려하지 않으면 최대 9,999개의 데이터까지 유실될 수 있다. 물론 설정을 변경하여 이 주기를 훨씬 자주, 예를 들어서 1분에 1개의 데이터만 변경되어도 저장하게 하면 대부분의 데이터를 살릴 수 있지만 이는 성능에 영향을 준다. 차라리 AOF(Append On File) 모드를 활성화하여 백업 이후 커밋 로그를 별도로 유지하는 것이 효율적이다.

하지만 데이터를 지키려고 하는 모든 설정은 성능 손실을 감수해야 하는 것이고, 이는 '빠름'이 매력이던 레디스에게는 별로 달가운 설정은 아니었다. 그래서 나는 가만히 앉아서 백업 주기를 고민하기보다는 불의의 사고로 데이터가 유실되었을 때 내가 회사에서 잘릴까 잘리지 않을까, 몇 개까지 유실되었을 때 용서받을 수 있고 몇 개부터 혼날까에 대해 고민하기 시작했다. 하지만 보통 1개의 데이터 유실도 용납하지 않는 오래된 문화에서는 '운이 없으면 데이터가 날아갈 수도 있는 아키텍처' 같은 것은 정감록 만큼이나 불온한 문서 취급받을 가능성이 높았다.

문득 전역을 몇 달 남긴 말년에 부대에서 '도서관리'를 하라는 명령을 받았을때의 기억이 떠올랐다. 작은 영외 중대였던 우리 부대에는 컴퓨터도, 케이블 TV도 없었기에 취미거리가 부족했고 이를 불쌍히 봐줬는지 시흥도서관에서 '순환 문고'형태로 책을 빌려주겠다는 제안이 들어왔다는 것이었다. 그래서 부대 간부들은 곧 들어올 책들을 관리하기에 적합한 사람으로 맨날 누워서 책만 보던 나를 떠올렸고, 그럴 거면 정식 직함을 받아서 연등실에서 사서 역할이나 하는 게 보람 있지 않겠냐는 제안을 했던 것이다.

한창 무료한 시간을 보내고 있던 나는 소일거리고 괜찮겠다 싶어서 제안을 수락했고, 곧 '도서관리'의 임무가 500권짜리 책장을 만드는 것부터 시작된다는 사실에 후회했지만, 그래도 도서관에 가서 500권을 책을 수령해서 부대로 돌아올때는 마음의 부식을 추진하는 듯한 기분이 들어서 뿌듯했다. 하지만 그 뿌듯함을 오래가지 않았는데, 도서관에서 받은 도서 목록과 실제 책을 비교하면서 하나씩 책장에 꽂다가 '체 게바라 평전'을 발견했던 것이었다. 도대체 시흥도서관 분들이 왜 군대에 체 게바라 평전을 보내주셨는지는 모르겠지만, 이 장소에는 절대로 있어서는 안 될 책이기에 이걸 어떻게 처리해야 하나 한참을 고민했다.

처음에는 문제의 싹을 없애기 위해, 최선의 방어는 공격이라는 생각으로 태우거나 묻어버리는 방안을 생각했다. 하지만 나중에 도서관에 책을 반납할 때 '어? 체 게바라 평전이 없는데 어디 갔죠?'라는 말이 나오고 간부들이 부대에 체 게바라 평전을 숨긴 사람을 찾아다니는 일이 벌어지는 상상을 하자 이내 포기할 수밖에 없었다. 그대로 책을 들고 내려가서 간부들에게 이상한 책이 섞여 들어왔음을 보고하고 도서관에 책을 반납하는 방안이 모범적인 해결책이라는 생각이 들었지만 보통 군대에서는 문제를 보고한 사람이 문제라는 이상한 인식이 있었기에 그 뒤에 도서관에 전화를 하여 사건의 전말을 파악하고 책을 반납하고 경위서를 쓰는 등의 귀찮은 일들이 따라올 것이 분명했다. 특히나 완전한 선의로 책을 빌려준 도서관에 어떤 방식으로든 싫은 소리를 해야 한다는 점이 마음에 들지 않았다.

그래서 내가 생각한 방안은, 애드거 앨런 포의 소설 '도둑맞은 편지'에 나오는 트릭을 쓰는 것이었다. 그러니까 그냥 책장에 꽂아놨다는 말이다. 놀랍게도 체 게바라 평전은 그 뒤로 내가 전역할 때까지 3개월간 아무도 빌려가지 않은 상태로 그 자리를 지키고 있었다. 도서관은 꽤나 인기가 있었기에 많은 부대원들과 간부들이 책을 하나하나 뒤져보고 훑어봤지만, 내가 보기에 그중 누구도 체 게바라 평전에서 불온서적이라는 사실을 몰랐던 것 같았다.

그런 경험이 있었던 나는 데이터 유실 가능성이 있는 불온한 아키텍처를 프로젝트에 도입할 방안을 쉽게 떠올릴 수 있었다. 몰래 적용하자. 금기를 어기는 것이 나에게 새로운 지평선을 열어줄 것이다. 설계도에는 '레디스'라는 최신 기술의 'NOSQL 데이터베이스'를 사용한다라고 적어두자. 이것을 리뷰하는 사람들이 지식이 있다면 레디스에서 데이터 유실의 가능성을 떠올릴 수 있겠지만, 아마 아무도 모를 가능성이 높다. 애초에 문서 같은 거 아무도 안 읽어보니까. 그리고 고작해야 데이터 유실되면 발생하는 문제는 로그인이 끊기는 정도이다. 있어서는 안 되는 일이지만, 그래도 우리 시스템의 신뢰도에 씻을 수 없는 상처를 주는 사고 같은 것은 일어나지 않는다. 마션을 읽어봐도 테스트 과정에서 문제가 발생할 확률이 2퍼센트 정도라니까 테스트를 생략해버려서 로켓 발사 시간을 획기적으로 단축하지 않았는가. 잘 돌아가던 레디스가 갑자기 내려갈 확률도 그와 비슷하지 않을까. 그런 생각으로 코드를 작성했다. 물론 마션에서 그 로켓이 결국 터져버렸다는 사실은 외면하고.

금기를 어긴 대가는 달콤했다. 시스템의 모든 동작이 100배는 빨라진 느낌이었다. 이래서 운동선수들이 도핑을 하는구나라는 생각이 들었다. 다시는 평범한 데이터베이스를 쓰지 못할 것 같은 기분이 들었다. 스키마의 정규화와 반정규화를 동시에 신경 쓰면서도 복잡다단한 쿼리와 실행계획, 인덱스와 커넥션을 모두 고려해야 하는 기존의 데이터베이스들은 순식간에 고리타분하고 지루하게 느껴졌다. 순서대로 한 번에 하나의 명령만 처리하는, 그런데 그게 어마어마하게 빨라서 초당 20만 개의 명령 정도는 코어 하나 만으로도 처리하는 구조는 심플하면서도 강력했다. 테이블과 정해진 스키마가 있는 것이 아닌 단순한 키 - 밸류의 심플한 사전형 데이터 구조도 훨씬 직관적이었다. 무엇보다도 '검색'이라는 것을 하기에 적합하지 않은 구조였기에, 애플리케이션이 데이터를 명확한 규칙을 가지고 저장하고 활용할 것을 강제하는 점도 매력적이었다. 지금까지는 데이터베이스가 애플리케이션을 감당하는 구조로 시스템을 설계했지만, 이제부터는 애플리케이션이 데이터 그리드를 받아들여야 하는, 공수가 바뀐 구조가 되었던 것이다. 하지만 그게 그렇게 생소하고 특이한 일은 아니었다. 심지어 레디스도 복제 모드로 2개의 프로세스를 마스터 - 슬레이브 구조로 띄워두면 가끔 마스터에 사고가 생겼을 때 슬레이브와 마스터 관계가 뒤집히곤 한다.

그 뒤로 몇 년의 시간이 흘렀고, 결국 나는 레디스에서 헤어나오지 못했다. 처음 금기를 어겼을 때 레디스에 저장하는 데이터는 고작해야 액세스 토큰이나 암호화 키 정도였지만, 몇 년 뒤에는 사용자의 심전도 그래프를 통째로 저장할 정도로 과감해졌다. 나는 커리어 동안 관계형 데이터베이스보다 레디스를 10배는 더 많이 쓴 개발자가 되어버렸고, 경력에 비해 SQL 작성에 미숙하다는 단점을 가지게 되었지만, 5~6권 무렵에 레디스를 보조적인 용도로 조금씩 쓰기 시작한 다른 개발자들에 비해서 훨씬 레디스를 익숙하게 사용한다는 장점도 가지게 되었다. 가끔은 내가 레디스의 심연을 조금이나마 들여다본 것 같다는 생각이 들 때도 있었다.

"괴물과 싸우는 사람은 스스로 괴물이 되지 않도록 조심해야 한다. 당신이 심연을 들여다본다면, 그 심연 또한 당신을 들여다보게 될 것이니"라는 말처럼 레디스라는 괴물의 깊은 곳을 파해칠때마다 내가 혹시 성능충이라는 괴물이 되어버린 것은 아닐까 섬뜩할 때가 있었다. 나는 이미 디스크 IO의 느린 속도를 견딜 수 없는 몸이 되어버린 것은 아닐까. 하지만 누가 관계형 DB로 만들었다가 '느려서 도저히 못 쓰겠다'라고 판정난 시스템을 살짝 건드려서 레디스를 쓰도록 바꾼 뒤 100배 빠른 시스템으로 만들어낼 때의 쾌락은 끊을 수가 없었다.

그 절정은 몇 년전에 어느 부서에 새로 전입하고 이틀째 되는 날에 있었는데, 그 부서에서 초당 2,000개의 사용자 요청을 감당해야 하는 요구사항을 가진 서비스를 이미 완성했는데, 테스트를 해보니 초당 50개를 처리하는 게 한계였다고 이걸 좀 어떻게 해보라는 업무를 받았을 때였다. 그래서 생각이 뇌를 거치지도 않고 '레디스를 쓰면 될 것 같은데요'라고 대답했다가 '이미 레디스를 써서 만들었는데 느리다'라는 답을 받고 잠시 당황했다. 하지만 우리 레디스가 그렇게 느릴 리가 없다는 확신이 있었던 나는 침착하게 코드를 살펴봤고, 레디스의 금기를 어긴 코드들을 다수 적발해낼 수 있었다. 예를 들어서 와일드카드를 써서 키 스페이스를 검색한다거나, 트랜잭션이라는 것이 없는 레디스에 트랜잭션이 있는 것처럼 사용한다거나, 멀티 커맨드로 날려야 할 부분이 하나씩 날아가고 있다거나.

그래서 나는 레디스를 더럽히고 있던 코드들을 싹 지우고, 하나의 사용자 요청이 기존 데이터 규모와 상관없이 상수시간에 완료될 수 있도록 메소드를 수정했고, 바뀐 시스템에 대한 성능 테스트를 했고, 초당 15,000개의 요청을 처리하는 레디스틱(Redis-tic)한 시스템으로 길들일 수 있었다. 끝을 모르고 치솟는 TPS 그래프를 보면서 이 성능 테스트 결과는 내 장례식장에 걸어두어도 괜찮겠다는 생각을 잠시 했다. 이 정도면 나도 레디스 마스터라고 할 수 있지 않을까.

하지만 간단한 개선 뒤에는 어렵고 고통스러운 후처리 작업이 필요했는데, 결국엔 물리적인 메모리의 한계를 고려해야 했기에 메모리가 가득찼을 때나, 시간이 지났을 때나, 기타 등등의 상황을 대비하여 필요가 없어진 키들을 폐기하는 로직을 추가해야 했던 것이었다. 물론 레디스는 앞에서 말했던 것처럼 메모리가 가득 차면 필요 없어진 키를 선택해서 지워주는 다양한 알고리즘 설정을 지원하고 있었고, 각각의 개별 키에 대해서는 일정 시간이 지나면 자동으로 삭제되도록 하는 EXPIRE 명령도 가지고 있었지만, 이는 키 자체에 대한 삭제였지 내가 상수 시간에 동작을 완료하기 위해서 사용한 집합(SET) 구조의 내부 원소에 대해서는 적용되지 않는 것이었다.

많은 고뇌와 번민 끝에 나는 정렬된 집합(Sorted Set)을 사용하여 타임스탬프를 기록하고 주기적으로 일정 타임스탬프보다 작은 원소들을 개별로 삭제하는 스케줄된 메소드를 돌리는 것이 최선이라는 결정을 했다. 레디스는 싱글 스레드의 민감한 요청 처리 구조를 가지고 있었기에, 원소의 탐색과 삭제가 한 번에 너무 많이 일어나지 않도록 조심하는 것도 어려운 일이었지만, 가장 어려웠던 것은 애플리케이션이 스케일 아웃되어 여러 개 존재할 때 서로의 스케줄이 중복되지 않도록 관리하는 일이었다.

다행히도 레디스는 그 자체를 이벤트 큐로 사용하여 메시지를 발행(Publish)하고 구독(Subscribe) 할 수 있는 기능이 있었고, 이를 잘 활용하면 리더 선택(Leader Election)이나 쿼럼(Quorum) 설정 및 헬스 체크에 활용되는 다양한 알고리즘들을 구현할 수 있었다. 무엇보다도 좋은 것은 레드락(Redlock)이라는 분산처리 알고리즘이었는데, 이 레드락은 레디스의 Expire 기능을 기가막히게 활용하여 안정성이 보장된 분산 락 프로토콜(Distributed Locking Protocol)을 구현할 수 있도록 해줬다. 이를 이용하면 여러 서버가 동시에 같은 스케줄링이 돌지 않도록 할 수도 있었던 것이다.

하지만 이론과 실제는 달랐고, 나는 결국 일관성 없이 사라지는 데이터들에 짜증을 낼 수밖에 없었다. 그토록 오랫동안 레디스에 헌신하는 인생을 살았지만, 조금 복잡한 요구를 하자 레디스는 이내 의도하지 않은 데이터를 지워버리는 것으로 응답하곤 했다. 내가 감히 레디스의 마스터라고 자칭해서 벌을 받은 것일까? 사실 레디스의 동작에 맞추어서 로직을 짜지 못한 내가 잘못이었겠지만, 집합의 원소에 대한 Expire를 죽어도 지원하지 않아서 나에게 어려운 숙제를 준 레디스가 원망스러워졌다. 급기야 레디스와 결별하고 다른 인메모리 데이터 그리드를 알아볼까라는 생각이 들었다. 헤이즐캐스트가 요새 인기라던데. 그건 심지어 JVM에서도 돌릴 수 있다며? 아, 이그나이트를 쓰면 기술지원도 받을 수 있고, 관계형 데이터도 넣어서 SQL까지 돌릴 수 있다던데. 사실 알아보면 세상 데이터베이스의 절반은 인메모리일 텐데. 내가 굳이 레디스에 집착할 필요가 있을까. 이제 레디스도 살짝 지루해진 것 같은데.

하지만 문득, 세상이 많이 변했다는 생각이 들었다. 더 이상 레디스를 쓰는 것이 특별한 레시피가 되지도 않는 세상이었다. 요새는 신입사원들도 레디스를 쓸 줄 안다. 자그마한 데이터 유실 가능성에도 벌벌 떨던 시절도 아니다. 이 바닥의 오랜 관습은 점점 깨지고 우리가 추구해야 할 것은 무결함(Zero defect)이 아닌 내결함(Fault Tolorence)으로 바뀌었다. 인공지능이 우리 업계를 습격한 이후로 언제나 뭐든지 100% 정확해야 한다는 말은 점점 사라지고 이제는 컴퓨터가 '잘 모르겠어요'라는 말을 부끄럼없이 하는 시대다.

누가 알아볼까 두려워하며 몰래 프로젝트에 레디스를 적용하고 그 대가로 얻은 놀라운 성능에 설레던 시절은 이미 몇 년전의 일이었다. 이제는 레디스에서 어떤 새로움을 찾으려고 하기보다는 깊은 이해를 바탕으로 내가 할 수 있는 것들을 고려하는 것이 맞지 않을까라는 생각이 들었다. 그러면 분명 익숙함 속에서 오는 편안함이 내가 원하는 것들을 이룰 수 있도록 도와줄 테니. 처음에는 모든 것이 좋아 보이기만 했던 레디스의 밝은 면 뒤에 설령 그림자가 50개쯤 존재한다고 해도 함께 다양한 문제를 해결한, 아직 Evict되지 않은 기억들이 그 음영 속에서 길을 찾을 수 있게 해줄 것이라 믿으며, 정렬된 집합에서 일정 시간 전에 기록된 원소를 상수 시간에 일괄 삭제하는 알고리즘에 내가 놓친 것이 무엇이 있는지에 대해 고민하기 시작했다.

'에세이' 카테고리의 다른 글

너무 느리거나, 너무 빠르거나  (0) 2021.05.20
echo  (0) 2021.04.05
레디스(Redis)의 50가지 그림자  (1) 2021.03.14
보이지 않는 별빛에 닿아  (0) 2021.03.07
우리들의 분리수거는 뭔가 크게 잘못되었다  (0) 2021.02.14
나는 개발왕이 될 거야  (0) 2021.02.12
댓글
댓글쓰기 폼