Batch System With Node.js, Lambda, and PostgreSQL
- Part 2. 데이터 업로더 (feat. Law SQL)

목차

Part 1. 배치 시스템 설계 (링크)
Part 2. 데이터 업로더 (feat. Law SQL)
Part 3. 데이터베이스 테스트 (feat. Jest) (링크)
Part 4. 결과 알람 (feat. Slack) (링크)
Part 5. 스케쥴링 (feat. AWS EventBridge rules) (링크)


데이터 업로더

데이터 업로더의 주 목적은 DB에서 DB로 대규모의 데이터를 이동시키 것이다.
데이터는 빠르고 정확하게 옮겨져 와야하고 다른 데이터에 손상을 줘서는 안된다.

에러는 없는게 최선이지만 언제라도 발생할 수 있다.
에러가 발생하면 즉시 발견이 되야하고 대응이 가능해야 한다. -> 테스트, 알림으로 대응
더 중요한건 에러가 발생하더라도 서비스에 영향을 줘서는 안된다. -> 배치와 서비스는 분리. 서비스 적용을 위한 별도 시스템 구성

사용한 언어는 Node.js, Typescript, Law Sql를 사용했고 AWS Lambda로 실행한다.

배치 작업에 Law SQL을 사용할 때 장단점

  • 절차형 프로그래밍에서 적합
    배치 시스템의 경우 객체지향 보다는 절차형 프로그래밍 방식에 가깝게 설계됐다.
    각 업로더들은 독립적으로 기능을 하고 서로 공유하는 모듈은 없다.
    따라서 객체지향의 객체간의 협력보다는 하나의 업로드를 강력하게 만드는게 중요했고 Law SQL을 쓰는게 적합하다고 생각했다.

  • Database 구조 직관적
    SQL은 query에서 Database의 구조를 직관적으로 읽을 수 있어 배치 시스템 로직 작성에 도움을 준다.

  • 속도
    Database에서 인식하는 Query 언어로 가장 속도가 빠르다.

  • 로직 구현
    테이블 관계 또는 비지니스 로직이 복잡할 때 Law SQL을 사용하면 비교적 쉽게 구현 가능하다.

  • Database CLI 편리성
    Database에서 바로 데이터를 확인할 수 있다.

배치 작업에서 Law SQL의 단점

  • 데이터 디버깅 어려움
    벌크 업로드시 에러가 생겼을 때 어떤 데이터에서 오류가 발생했는지 찾기가 어렵다.

  • 까다로운 쿼리 입력
    쿼리를 입력할 때 문자 하나라도 잘못되면 에러가 발생한다. 처음 작성하는 복잡한 로직이라면 시간이 좀 걸릴 수 있다.

대규모 데이터 업로드 방법

데이터 양이 많으면 메모리 초과 등으로 에러가 발생할 수 있다.

단순한 방식으로 해결했다. 반복문으로 대규모 데이터를 나눠서 벌크 작업을 해줬다.
예외처리, 알림 등도 나눠진 벌크 기준으로 처리해준다.
특정 벌크에서 에러가 생긴다면 그 부분만 다시 작업하면 되도록 했다.

아래는 반복문 예시 소스코드이다.
전체 업로드 범위는 DB의 1~85000번째 rows이고 10000개씩 나눠서 업로드되는 예시이다.

// 10000개씩 배치 반복 실행

const initialOffset = 0;
const initialLimit = 85000;
const quantity = 10000;

let offset = initialOffset;
for (
    let leftLimit: number = initialLimit;
    leftLimit > 0;
    leftLimit -= quantity
) {
    let limit: number;
    if (leftLimit < quantity) {
        limit = leftLimit;
    } else {
        limit = quantity;
    }
    await executeBatch(batchDate, offset, limit); // executeBatch 업로더 함수
    offset += quantity;
}

여기서 주의할건 반복문에 foreach 또는 map은 사용할 수 없다. Promise를 리턴해주는 함수를 기다려주지 않기 때문이다.

insert or update

업로드 로직에는 insert 또는 update를 해주는 경우가 상당히 많다.
그럴 때 아래와 같은 쿼리문을 사용할 수 있다.

insert into mytables
    (a, b, c, d, e)
values
    ('a1','b1','c1','d1','e1),
    ('a2','b2','c2','d2','e2),
    ...
    ('a10000','b10000','c10000','d10000','e10000')
on conflict (a,b)
do update set
    a = excluded.a,
    b = excluded.b,
    c = excluded.c,
    d = excluded.d,
    e = excluded.e

이때 conflict로 설정한 a,b 칼럼은 미리 DB Constrains에 Unique Key로 등록해둬야 한다.

배치용 람다 사용 유의점

  1. 요금
    람다는 사용 시간에 따라 요금이 부과된다. 배치 실행 시간이 길어질수록 요금도 비례해서 올라간다.
    만약 시간이 오래 걸리는 작업이라면 람다에는 맞지 않을 수 있다.
    내가 작성한 배치 시스템은 한달 주기로 실행되고 가장 오래 걸리는 배치라고해도 3분을 넘기지는 않기에 람다에 사용해도 무방했다.

  2. 메모리
    람다는 메모리를 설정해줄 수 있는데 메모리 값에 따라 람다를 부팅하는 컴퓨팅 사양이 결정된다.
    데이터 작업 규모에 따라 메모리를 적절히 상향해주는게 좋다.
    람다에 최대로 설정할 수 있는 메모리는 10GB이다. 나는 4GB로 설정했다.


Part3에서 계속..

댓글남기기