본문 바로가기

COMPUTER SCIENCE/PYTHON

[OSSCA] python-mysql-replication - RandEvent 구현하기

2023 오픈소스 컨트리뷰션 python-mysql-replication 첫번째 기여 활동으로 진행한 RandEvent 구현 과정에 대해 살펴보고자 한다.
(python-mysql-replication 설명은 이전 글 참고 : https://heehehe-ds.tistory.com/203)

 

[OSSCA] python-mysql-replication이란?

지난 7월부터 2023 오픈소스 컨트리뷰션 아카데미라는 활동을 통해 오픈소스에 직접 기여하는 활동을 진행하게 되었다. 2023 오픈소스 컨트리뷰션 아카데미 Open Source Contribution Academy www.contribution.a

heehehe-ds.tistory.com

 

Event란?

먼저 Event는 이전 글에서 언급했던 바와 같이 python-mysql-replication에서의 데이터 변경 최소 단위를 나타낸다.
대표적으로 아래와 같은 필드로 구성되어 있고, 새로운 이벤트를 구현할 때는 주로 Body(Payload) 부분에 대해 추가를 해주면 되었다.

  • Common-Header
    : 생성 시점(timestamp), 종류(event type), 생성 서버 id, 크기 등 Event에 대한 주요 정보
  • Post-Header
    : Event에 대한 부가 정보(metadata)
  • Body (Payload)
    : Event에 대한 실질적인 데이터가 담기는 곳

python-mysql-replication에서 event 목록은 pymysqlreplication/constants/BINLOG.py 에서 살펴볼 수 있었다.
https://github.com/julien-duponchelle/python-mysql-replication/blob/7420cfdcdce0c3fe963c6f5370228451fe04b653/pymysqlreplication/constants/BINLOG.py

이 중 가장 간단해보이면서도,
MySQL로부터 파생된 MariaDB에도 관련 문서가 있었던 RandEvent를 첫번째 구현 이벤트로 선정하게 되었다.

 

RandEvent란?

RandEvent란, 쿼리에서 RAND() 또는 password()라는 함수가 실행될 때 발생되는 이벤트이다.

MySQL 문서에서 살펴보자면 해당 이벤트가 발생할 때 Body(Payload) 부분에 random seed number가 들어오게 되는데,
이는 128 bit로 구성되어 있고 두개의 64-bit 숫자들로 나눠진다고 한다.

MariaDB 문서에서도 살펴본 결과,
해당 이벤트가 발생할 때 Body(Payload) 부분에 랜덤에 대한 seed number를 담은 2가지 필드가 uint8의 data type으로 들어오는 것을 확인할 수 있다.

다만 다른 event와 다른 특이한 부분이 있었다면, 바로 logging 방식이 statement-based logging이었다는 것이다.

 

Row-based / Statement-based Logging

데이터베이스 변경 사항을 어떻게 로깅시킬지 나타내는 것으로, 기본적으로 아래 2가지가 있다.

Row-based Logging(RBL)은 실제로 변경된 데이터 행의 내용을 로그에 기록하는 것으로, 기본적으로 해당 로깅 방식을 사용한다.
특정 연산에서 발생한 모든 변경을 정확하게 반영할 수 있다는 장점이 있지만,
많은 양의 데이터가 변경된 경우 로그 크기가 매우 커질 수 있다는 단점도 있다.

Statement-based Logging(SBL)은 변경이 발생되는 SQL 문장 자체를 로그에 기록하는 것이다.
NOW() 또는 RAND() 같이 동일한 결과를 보장하지 않을 수 있는 복제 오류가 발생할 가능성이 더 높지만,
수 많은 행이 변경되더라도 해당 SQL 문장은 하나로 기록된다.

chatgpt가 말하기를,
간단하게 비유하자면 statement-based는 명령/지시를 기록하는 것이고, row-based는 결과/상태를 기록하는 것이라고 한다.

  • Statement-based Logging: "물을 끓여라"와 같은 명령/지시를 기록하는 것.
  • Row-based Logging: "물이 100°C로 끓었다"와 같이 결과/상태를 기록하는 것.

때로는 2개 로깅 방식이 모두 필요할 수 있는데, 이때는 Mixed-mode를 통해 statement-based와 row-based를 함께 사용한다.

MySQL에서는 아래와 같이 binlog_format 변수를 통해 환경을 설정해줄 수 있다.

SET GLOBAL binlog_format='ROW';
SET GLOBAL binlog_format='STATEMENT';
SET GLOBAL binlog_format='MIXED';

 

이렇게 주어진 문서들과 chatgpt(!)로부터 힌트를 얻어 PyMyRepl에 직접 구현해보기 시작했다.

 

python-mysql-replication에 직접 구현해보기

python-mysql-replication에서는 pymysqlreplication/event.py 부분에 각 event가 정의되어 있고,
각 event는 공통 처리를 하는 BinLogEvent class를 상속받아 구현되어 있었다.

이에 따라 RandEvent도 BingLogEvent를 상속 받아 구현하고,
2개의 seed number를 받아오는 Body(Payload) 부분만 packet의 read_uint64 함수를 통해 추가해주면 되었다.

테스트 함수에서는 기존에는 모두 row-based logging 기반이었기 때문에
statement-based logging을 진행하는 테스트를 새로 추가해주어야 했다.

따라서 새로운 테스트 class를 생성하고 해당 class에서는 binlog_format='STATEMENT'가 되도록 설정해준 뒤 테스트를 진행했다.
해당 테스트가 완료된 뒤에는 다시 binlog_format='ROW'로 revert 시켜주는 작업을 거쳤다.

 

기여 결과

https://github.com/julien-duponchelle/python-mysql-replication/pull/414

 

Developed RandEvent and add statement-based logging test by heehehe · Pull Request #414 · julien-duponchelle/python-mysql-repl

1. Developed RandEvent Developed RandEvent class based on these following documents https://mariadb.com/kb/en/rand_event https://dev.mysql.com/doc/dev/mysql-server/latest/classbinary__log_1_1Rand...

github.com

이렇게 팀원들과 함께 구현한 내용을 정리해 PR을 올리고, 올린지 보름 만에 코멘트를 받고 merge시킬 수 있었다!!

막상 다시 보니 추가된 코드는 간단했지만,
초반에 어느 부분을 추가해야 하는지 헤매고 statement-based logging이라는 새로운 환경 세팅도 접하게 되었다.
무엇보다 팀원들로부터 어떻게 문서를 살펴보고 작업해야 하는지 많이 배울 수 있었고,
이 작업을 기반으로 다른 구현도 하나씩 진행해나갈 수 있었다 :)

다음에는 RandEvent에 이어서 구현했던 UserVarEvent에 대해 살펴보고자 한다.

반응형