Lambda에서 Sequalize.js 삽질기

고통과 번뇌의 삽질

Harry The Great
해리의 유목코딩
6 min readMay 9, 2019

--

왜 람다에서 RDBMS인가?

요즘들어 점점 더 많은분들이 Serverless와 Container 기반으로 개발을 하시는것같습니다. 일반적인 온프로미스 환경과달리 람다는 아주 짧은 시간동안만 컴퓨팅 파워를 쓰기때문에 MongoDB나 RDB같은 풀기반의 데이터베이스 환경보다 API 기반의 데이터베이스가 더 궁합이 맞습니다. 하지만… 궁합이 맞는것과 실제로 개발의 편의성은 다른 이야기입니다.

Datagrip못 잃어! Workbench못 잃어! SQL 못 잃어!

Lambda에서는 DynamoDB를 쓰면 정말 편하지만 데이터를 체크하고 관리하기가 무척이나 힘듭니다. SQL을 사용할때는 우리에게 Workbench(혹은 Datagrip)이라는 궁극의 툴이 있습니다. 또한 데이터를 좀 더 유연하게 쿼리할 수 있기때문에 Lambda에서 RDBMS를 사용하기로 결정했습니다.

ORM vs Raw SQL

RDBMS를 사용하기로해도 문제가 있습니다. ORM을 통한 class 맵핑을 자동으로 할당해줄지 아니면 로우한 SQL 쿼리를 직접 질의할지입니다. 후자의경우는 사용이 크게 어렵지 않습니다. 핸들러 블록 내에서 connection을 설정하고 질의를 한 후 close 메서드를 이용해 연결만 끊어주게된다면 문제가 없습니다.

callbackWaitsForEmptyEventLoop

혹시라도 사용하는 라이브러리에서 Timeout에러가 난다면 callbackWaitsForEmptyEventLoop옵션을 설정해주어야합니다. Lambda 함수에서는 callback(async일경우 return)이 호출되어도 람다가 끝나지않고 이벤트루프에있는 작업들이 끝나기를 기다립니다.

데이터베이스 풀문제

데이터베이스에서 풀이란 보통 연결상태를 끊지않고 계속 연결하는것을 말합니다. 일반적인 온프로미스환경에서는 서버가 계속해서 HTTP Request를 기다리는 [대기]상태이기때문에 항상 풀이 연결된 상태를 유지합니다.

하지만 문제는 람다에서도 풀이 계속 연결된 상태를 유지하기때문에 람다에서는 아직 이벤트루프에 작업들이 진행중이기때문에 람다함수의 종료를 보류합니다. 이로인해 요청한 클라이언트에서는 수초이상 응답을 받지 못했기때문에 Timeout에러가 발생하게됩니다. 이때 context.callbackWaitsForEmptyEventLoop에 true값을 주어 callaback이 실행됨과 동시에 람다함수가 종료되도록 설정해주어야합니다.

Sequalize.js

단순한 형태의 테이블이라면 로우한 SQL를 질의해도 되지만 모델클래스가 필요하고 데이터의 후가공과 테이블의 관계가 복잡해진다면 ORM은 사용하지 않을수 없습니다. Node의 RDBMS진영에서 가장 유명한 라이브러리는 Sequalize.js 입니다. Sequalzie v3버전을 사용하기까지는 큰 문제가 없었지만 v4 버전부터는 connection 에러가 간간히 발생하게되었습니다.

generic-pool 라이브러리 문제

v3버전까지 node-pool 라이브러리에서는 문제가 발생하지 않았으나 sequalize.js의 v4버전부터 사용하는 generic-pool의 경우 람다함수가 끝났음에도 pool을 계속해서 가지고있는 문제가 발생했습니다. 이로인해 최근 출시된 v5버전부터는 connection 문제가 해소되었습니다. 관련 Github Issue글

커넥션 idle 타임

sequalize.js에서 풀을 사용하지 않는다면 pool옵션을 따로 설정해주어야합니다. v3까지는 pool옵션에 false를 주어 사용하지 않을 수 있었지만 v4버전부터는 풀을 강제하게됩니다. 이로인해 아래와같은 코드로 pool을 설정하지 않을 수 있습니다.

체크해볼 문제점들

딜레이시간

람다에서 pool을 사용하지않고 데이터를 가져올경우 약 500ms의 딜레이시간이 발생합니다. 람다가 콜드스타팅될경우 더 많은 시간이 딜레이 발생하게됩니다. 람다가 콜드스타팅되지 않을경우 딜레이시간이 크게 느껴지지는 않지만 만약 Lambda함수가 RDS와함께 VPC내에서 구축되어있다면 Lambda의 Network EMI를 붙이는 딜레이시간이 약 500ms~ 800ms 또 발생하게됩니다.

Max Connection

Lambda에서 커넥션을 아주 짧은 시간만 유지하지만 유저가 몰린다면 단시간내에 엄청나게 많은 커넥션풀이 발생하게됩니다. 이를 방지하기위해 데이터베이스의 Max Connection값을 넉넉하게 잡아두어야하며 맥스값 관리에 주의가 필요합니다.

Typescript에서 구축하기

sequalzie와 typescript를 검색해보면 sequalize-typescript라는 라이브러리가 있습니다. 아직 프로젝트가 성숙되지 못해서인지 람다환경에서 구현하는데 자잘한 문제가 상당히 많았습니다. 다행히 v5버전부터는 typescript를 구현하는 샘플코드를 제공하고있습니다.

Define 형태로 ORM을 정의했다면?

이전까지 사용해오던 define 형태의 모델정의가 아닌 class 형식의 모델 정의를 사용하기때문에 이전의 define 형태를 타입스크립트로 마이그레이션한다면 이 포스트에서 예제를 확인할 수 있습니다.

마치며

다른경우라면 Sequalize.js를 Lambda에서 사용하는데 크게 문제가 될것같진 않지만 API Server에서 풀을 이용한 데이터베이스를 사용할때 대용량 처리에 관련해서는 다소 걱정이 되기는합니다.

--

--

Harry The Great
해리의 유목코딩

Android & IOS Developer 😀 미디움 이외에 스니펫이나 디버그노트로 활용하는 https://www.harrymikoshi.com/ 블로그도 운영하고있습니다.