Memory Control

ProSync의 데이터 흐름도를 상세히 이해하고, 이를 통해 Memory 사용량 조절방식을 안내한다.

ProSync Data Flow

그림 1. ProSync DataFlow

데이터가 처음 생성되는 위치는 Source DB의 Redo Log이다. Redo Log에서 발생된 데이터를 Extract 가 추출하여 Chunk 단위로 데이터를 적재한다.

적재된 데이터는 TCP Socket을 통해 전송이 된다. Non Blocking 통신이기 때문에 전체 데이터에 대한 전송이 실패하면, 이후의 데이터들은 Non Blocking Queue에 쌓이게 된다. 이 Queue에 쌓인 데이터는 커널로부터 반대쪽 TCP Recv Buffer가 비어있다는 신호가 올 때, 순서대로 다시 데이터를 보내게 된다.

Apply는 받은 데이터를 모두 객체화 (역직렬화) 하여 TX_HASH에 쌓이게된다. xid 는 트랜잭션 고유의 id 를 의미하며, ACID를 지켜주기 위해 트랜잭션의 커밋까진 이 위치에서 데이터를 계속 보관한다. 트랜잭션의 커밋이 들어오면 해당 트랜잭션은 Target Database에 반영이 가능한 상태로 판단하고 Replay Queue로 데이터를 넘겨준다.

Wait Queue에 쌓인 데이터는 트랜잭션들이 커밋된 순서에 맞게 Target Database로 반영이 된다. 이 때 반영 중인 트랜잭션과 반영 대기 중인 트랜잭션이 있을 텐데, 이들간의 의존성 검사를 통하여 시간 순서에 맞고, 의존성이 없는 트랜잭션들은 병렬 처리하여 성능을 높인다.

Case 1. TX_HASH 에 데이터가 너무 많이 쌓이는 경우

TX_HASH 에 데이터가 계속 쌓인다면 Out of Memory 를 피할 수가 없다. 따라서 특정 임계치를 넘어갈 경우 Part File이라는 이름으로 된 파일을 생성하여 이 위치로 데이터를 옮겨준다.

데이터를 옮기는 기준은 현재 TX_HASH에 있는 데이터들 중 가장 용량이 큰 트랜잭션 기준으로 파일로 내려가게 되며, 내려간 파일은 실제 반영이 되는 시점까진 계속 Disk에 남게 된다

이에 대한 기준이 되는 파라미터가 TX_HASH_SIZE 파라미터이다. TX_HASH_SIZE라는 임계치보다 더 많이 데이터가 쌓이게 될 경우 아래와 같은 동작이 수행된다고 생각하면 된다.

그림 2. if TX_HASH's Size is Greater than TX_HASH_SIZE Parameter

Part File은 반영이 완료되면 제거가 되며, 반영이 되기 전까진 Disk에 남는다. 실제 트랜잭션 반영 시점에도 Part File로 내려간 DML 데이터들이 시간 상 더 앞서기 때문에, 파일에 있는 데이터를 먼저 반영한 후 메모리에 남은 트랜잭션 데이터를 반영한다.

Case 2. Wait Queue에 데이터가 너무 많이 쌓이는 경우

ProSync는 ACID를 지켜주기 위해 데이터를 순서에 맞게 반영해야 한다. 바꿔 말하면, 앞선 트랜잭션이 반영되지 않는 경우 데이터는 반영될 수 없다는 의미이다. 따라서 Target Database에 장애가 발생하여 트랜잭션 처리가 지연된다면 그만큼 Queue에는 데이터가 쌓이게 된다.

Queue에 영구적으로 데이터가 쌓이는 걸 방지하기 위해 ProSync는 Queue 내에서 대기 혹은 반영 중인 트랜잭션들의 크기를 모두 확인한다. 이 크기가 너무 크다면 Memory 할당을 멈춰야 한다.

Memory 할당을 멈추기 위해 ProSync는 데이터가 추가적으로 생산되는 것을 막는다. 이 말은 Extract 로부터 들어오는 데이터를 잠시 막음으로써 현재 들고 있는 데이터들이 일정량 이상 처리되기를 강제로 대기 시킨다고 생각하면 된다.

Extract는 앞에서 설명한 내용대로 Non Blocking Queue에 데이터를 쌓다가 실제 추출을 멈춤으로써 추가적인 데이터 생산을 멈추게 된다.

임계치에 해당하는 파라미터는 아래와 같다.

  • MEMORY_CTRL_STOP_SIZE Wait Queue에 이 크기 이상 만큼의 데이터가 쌓이면 Socket 으로 부터 추가적으로 들어오는 데이터를 막는다.

  • MEMORY_CTRL_RESUME_SIZE Wait Queue에 이 크기 이하만큼 데이터가 줄어들면, Socket 으로 부터 데이터를 다시 받는다.

그림으로 나타내면 아래와 같다.

그림 3. Socket Block in ProSync

Source DB가 Cluster인 경우 고려사항

Active-Active Cluster인 RDBMS들은 Physical Disk를 공유하고 있다. 따라서 같은 공간에 서로 순서에 맞게 끔 데이터를 입력한다. Redo Log는 서로 다른 곳에 작성하더라도 Change Data들의 시간은 순서에 맞게 끔 작성이 된다. ProSync 입장에서 보면 서로 다른 위치에 남은 Redo Log들을 모아 직렬화를 하면 결국 DB Time 순서대로 데이터 정렬이 가능하다는 의미이다.

따라서 ProSync의 Apply 입장에선 추출이 Cluster인지 아닌지는 관심사가 아니다. 어차피 DB Time기준으로 정렬이 가능한 데이터라면 단순히 한 줄로 세워 놓고 하나씩 쿼리로 변경해주면 된다. 다만 문제는 장애 상황이나 지연 상황에서 발생한다.

만약에 한쪽 노드에서 아무런 부하가 없거나 장애로 멈춰버리게 되면 Redo Log 작성이 멈추게 된다. 읽을 것이 없는 Extract 프로세스는 아무런 데이터를 보낼 수가 없다. 이렇게 되면 Apply 프로세스 입장에선 현재 Cluster 중 하나가 장애가 나서 못 보내고 있는 것인지, 부하가 없어서 데이터를 못 보내는 것인 지 확인할 길이 없다.

따라서 Extract 에선 장애가 났을 경우는 장애가 났다는 메세지를 TCP Socket으로 전달해주고, 부하가 없는 상황을 대비하여 ProSync용 Dummy Table에 강제로 부하를 만들어 데이터를 생산해서 전달해준다.

이 과정을 통해 Apply프로세스는 서로 다른 Cluster Node의 데이터들을 언제나 정렬할 수 있게 된다. 하지만 정렬을 하기 위해선 정렬을 위한 공간이 필요하며, 이 위치는 아래 그림과 같다.

그림 4. Merge Queue

위와 같이 Extract가 여러 개인 상황 (Active Cluster) 에선 각 Extract 별로 데이터를 먼저 Merge Queue에 쌓아 놓는다. 쌓인 데이터가 직렬화가 되려면 모든 Extract 별 데이터가 1개라도 있어야 하기 때문에 이때까진 Merge Queue에 데이터가 쌓이게 된다.

다만 네트워크 장애나 모종의 이유로 한쪽에서 데이터가 들어오는 행위가 매우 지연될 수 있기 때문에, 마찬가지로 여기서도 추가적인 데이터가 들어오지 못하게 TCP Socket으로 부터 데이터를 막는 동작이 사용된다.

여기에 사용되는 파라미터는 아래와 같다.

  • MERGE_BLOCK_CNT 특정 노드로부터 데이터가 이 갯수 이상만큼 쌓이면 해당 TCP Socket을 막는다.

  • MERGE_RESUME_CNT 특정 노드로부터 데이터가 이 갯수 보다 작다면 해당 TCP Socket을 다시 열어준다.

그림으로 보면 아래와 같다.

그림 5. Socket Block in ProSync 2

Extract 별로 연결이 따로 존재하므로 이와 같이 별도로 관리하게 된다.

Last updated