Redis array type: short story of a long development

Salvatore Sanfilippo

Redis 배열 타입: 긴 개발의 짧은 이야기

원문은 Salvatore Sanfilippo님이 에 게재했습니다. 이 블로그 구독하기

1월 초에 Redis의 새로운 Array 데이터 타입 작업을 시작했다. PR이 저장소에 머지된 건 이제서야니, 이 코드는 4개월 동안 숙성된 셈이다. 구현은 일종의 파트타임으로 진행했다(‘일종의’라고 한 건 사실 여러 주는 풀타임으로 일했기 때문이다. 가끔은 키보드에서 손을 떼는 게 참 어렵다). LLM이 없던 시절에도 아마 4개월이면 해낼 수 있었을 작업이다. 달라진 점이 있다면 같은 시간 동안 훨씬 더 많은 일을 해냈다는 것이다. 무슨 일이 있었는지 짧게 이야기해보려 한다.

첫 한 달은 그냥 명세 문서를 쓰는 데 썼다. 새로운 데이터 타입이 필요한 이유, C 구조체, 사용할 희소(sparse) 표현, 링 버퍼와 ARINSERT를 위한 배열 커서의 정확한 의미까지. 처음 며칠은 긴 명세서를 손으로 직접 썼고, 그 다음에는 Opus와 페어링하다가 GPT 5.3이 출시된 뒤로는 설계와 개발 전부를 Codex로 갈아탔다. 그 이후로 시스템 프로그래밍 작업에는 GPT 5.x만 쓰고 있다. AI 덕분에 명세는 피드백을 주고받으며 크게 발전했다. 무엇이 최선의 설계인지, 무엇이 적절한 타협인지, 무엇이 과한 엔지니어링이고 무엇이 아닌지에 대한 지적 도전을 거치면서였다.

두 번째 달부터는 자동 프로그래밍(원한다면 오토 코딩이라고 해도 좋다)을 이용해 구현을 시작했고, 만들어진 코드를 끊임없이 검토했다. 그러다 내가 선택한 간접 참조 수준이 잘못됐다는 걸 깨달았다. 나는 사람들이 ARSET myarray 293842948324 foo 같은 명령을 실행해도 거대한 메모리 할당 없이 모든 게 정상적으로 동작하길 정말로 원했다. 내가 갖고 있던 디렉터리와 슬라이스(sparse와 dense) 2단계 구조로는 충분하지 않았다. AI가 있었기에 타협하지 않고 한 걸음 더 나아가기로 했다. 특정 조건에 도달하면 자료구조가 내부적으로 형태를 바꾸어, 실제 배열 슬라이스(기본값은 슬라이스당 4096개 요소)를 가리키는, 잘게 쪼개진 dense 디렉터리들의 슈퍼 디렉터리가 된다. 이 설계는 내가 원하던 내부적인 “실은 배열이다”라는 표현과 내가 원하던 메모리 특성을 그대로 유지하면서도, ARSCAN이나 ARPOP 같은 경우 전체 범위가 아니라 존재하는 요소 수에 비례하는 시간 안에 기존 배열들을 스캔할 수 있게 해주었다.

그 다음에는 모든 코드를 한 줄 한 줄 읽어볼 차례였다. 모든 게 동작하고 있었고, 역시 AI 덕분에 이 타입은 방대한 테스트를 갖추고 있었지만, 겉보기에 동작한다고 해서 최적이라는 뜻은 아니다. 원치 않던 작은 비효율이나 설계 오류를 많이 발견했고, 그래서 여러 모듈을 수동으로, 그리고 AI의 도움을 받아 다시 쓰는 과정을 시작했다. 이 단계가 끝나고 세 번째 달에는 다양한 방식으로 구현을 스트레스 테스트하기 시작했다. 이제 정말 견고하고 유용하며 잘 설계되었다는 확신이 들기 시작했다.

그러다… 일이 생겼다. 자료구조가 사용하기 편한지 확인하려고 여러 유스케이스를 모델링하던 중, 마크다운 파일들을 Redis 배열에 넣기 시작했다. 파일이야말로 이 자료구조와 아주 잘 맞기 때문이다. 마침 다른 목표를 위해 에이전트들과 작업하던 중이던 나는, 필요로 하던 스킬 마크다운 파일들의 중앙 집중식 지식 베이스를 가질 수 있겠다는 걸 깨달았고, 그래서 내 필요에서 출발해 ARGREP을 구현하기로 했다. 그런데 정규 표현식도 필요했다. 어떤 라이브러리를 고를까?

결국 TRE를 골랐다(Ville Laurikari에게 감사한다!), Redis에서 정규 표현식을 쓸 때는 시간이나 공간 측면에서 병목이 되는 병리적 패턴이 없다는 걸 확신해야 하기 때문이다. 하지만 TRE는 foo|bar|zap을 매칭하는, 매우 유용하면서도 특정한 케이스에서 몹시 비효율적이었다. 그래서 GPT의 도움으로 이를 최적화하고, 잠재적인 보안 이슈 몇 가지를 수정했으며, 테스트도 확장했다. 모든 준비가 갖춰졌다.

그 모든 과정에서 가장 크게 깨달은 게 뭔지 아는가? 고품질 시스템 프로그래밍 작업에서는 여전히 온전히 몰입해야 하지만, 나는 AI가 없었다면 건너뛰었을 수준의 복잡도까지 도전했다는 것이다. AI는 두 가지에 대한 안전망이 되어주었다. 몹시 지치게 하는 특정 대규모 작업들(나중에 추가되고 테스트된 32비트 지원 같은)과, 동시에 복잡한 알고리즘에 명백한 버그가 없는지 확인하는 데 필요한 가상의 인력이다. 처음의 방대한 명세를 작성한 것이 이후 작업의 핵심이었듯, sparsearray.c와 t_array.c의 모든 줄을 한 줄씩 검토하고 맞지 않는 부분을 수정하는 것 역시 핵심이었다.

유스케이스에 대해서는 따로 말을 아꼈다. PR 자체에 상세한 설명을 담은 메시지로 문서화해 두었기 때문이다:

https://github.com/redis/redis/pull/15162

그래서 여기서 같은 말을 반복할 필요는 없을 것 같다. Redis에도 숫자 인덱스가 의미 체계의 일부가 되는 데이터 타입이 생길 때가 됐다고 나는 진심으로 믿는다는 말이면 충분하다.

Array PR이 곧 받아들여져서, 그것이 열어줄 새로운 유스케이스들의 혜택을 누릴 수 있기를 바란다. 물론 피드백은 언제나 환영한다. 감사하다.

이 글은 muse-spark-1.2-contributor 모델을 사용해 번역했습니다.

댓글