융무의 기술블로그
article thumbnail

PR 내용

본 포스팅에서는 Python-mysql-replication 프로젝트에 기여한 경험을 공유하고 있습니다. 문서 업데이트, 버그 수정, 그리고 기능 개발 세 가지 분야에서의 기여 과정과 결과를 소개하고자 합니다.

docs: Update README to add Featured Books

Add Featured Section in README

Update README Featured Section with AWS Blog on RDS, XA Transactions

Remove duplicated Affected columns output in UpdateRowsEvent

Developed UserVarEvent and Added Statement-Based Logging Test

Enhance Testing with MySQL8 & Update GitHub Actions

Modify test structure

 

1편에 이어서 github action pytest 기여 부분에 대해서 포스팅하려고 합니다.

 

[OSSCA 2023] python-mysql-replication 프로젝트에 기여하기: 문서 업데이트, 버그 수정 및 기능 개발

PR 내용 본 포스팅에서는 Python-mysql-replication 프로젝트에 기여한 경험을 공유하고 있습니다. 문서 업데이트, 버그 수정, 그리고 기능 개발 세 가지 분야에서의 기여 과정과 결과를 소개하고자 합

mjs1995.tistory.com


기능개발

MySQL8 버전 테스트 추가 및 github action 버전 업그레이드

프로젝트 진행 당시 MySQL8에 대한 테스트를 할 수 없었고 MySQL5.7 버전과 MariaDB에 대해서만 진행을 할 수 있었으며 MySQL 8.0.14 버전 이후로 binlog_row_metadata=FULL이 나오면서 OptionalMetaData에 대해서 이벤트 개발을 하였습니다. 관련해서는 같은 조의 멘티님의 블로그를 읽어보시면 도움 되실 거 같습니다. 따라서 MySQL8 버전대에 대해서 도커파일을 추가하고 테스트 코드를 수정해야 했습니다.

 

python-replication-mysql Event Occured Time Column sync

python-replication-mysql는 Master에 최대한 부하를 주지 않고 데이터 분석, CDC를 가능하게 만드는 프로젝트이다. 국내기업에서는 카카오, 원티드 해외기업에서는 아마존에서 CDC로 사용되고있는 오픈소

seanical.tistory.com

version: '3.4'

x-mysql: &mysql
  environment:
    MYSQL_ALLOW_EMPTY_PASSWORD: true
  command: >
    mysqld
    --log-bin=mysql-bin.log
    --server-id 1
    --binlog-format=row
    --gtid_mode=on
    --enforce-gtid-consistency=on

x-mariadb: &mariadb
  environment:
    MARIADB_ALLOW_EMPTY_ROOT_PASSWORD: 1
  command: >
    --log-bin=master-bin
    --server-id=1
    --default-authentication-plugin=mysql_native_password
    --binlog-format=row

services:
  percona-5.7:
    <<: *mysql
    image: percona:5.7
    ports:
      - "3306:3306"

  percona-5.7-ctl:
    <<: *mysql
    image: percona:5.7
    ports:
      - "3307:3306"

  percona-8.0:
    <<: *mysql
    image: percona:8.0
    ports:
      - "3309:3306"

  mariadb-10.6:
    <<: *mariadb
    image: mariadb:10.6
    ports:
      - "3308:3306"
    volumes:
      - type: bind
        source: ./.mariadb
        target: /opt/key_file
      - type: bind
        source: ./.mariadb/my.cnf
        target: /etc/mysql/my.cnf
  • 도커 컴포스 파일은 여러 개의 컨테이너 구성 정보를 코드로 정의하고, 명령을 실행함으로써 애플리케이션의 실행환경을 구성하는 컨테이너들을 일원 관리하기 위한 툴로 docker-compose.yml 코드를 보면 percona:8.0 이미지를 추가하였고 percona 8은 gituhb에서 확인해 보면 최신 버전을 가져오고 있는 거 같습니다. (Percona Server 8.0.33 버전과 MySQL Shell 8.0.33 버전을 포함하고 있음)
  • 해당 코드는 YAML Anchors와 Aliases를 사용하였는데 이는 중복된 데이터 구조나 값을 재사용하기 위한 메커니즘이며, 이를 통해 파일의 크기를 줄이고 데이터의 중복을 최소화하며 파일의 가독성을 향상했습니다. 도커 앵커에서 사례를 확인할 수 있습니다.
    • Anchors (&): 특정 노드에 이름을 붙여 재사용할 수 있게 합니다.
    • Aliases (*): 이전에 정의한 앵커의 값을 재사용할 수 있습니다.
    • <<: 위에서 정의된 앵커를 참조합니다.
  • 테스트 케이스 추가
    • docker-compose 파일에서 설정한 환경 변수와 포트 값을 사용하여 데이터베이스에 연결할 수 있게끔 신규 클래스를 만들었고 복제 기능 테스트케이스를 추가하였습니다.

깃허브 액션 버전 업그레이드

    steps:
      - name: Check out code
        uses: actions/checkout@v2

      - name: Setup Python
        uses: actions/setup-python@v2
        with:
          python-version: ${{ matrix.python }}

현재 pytest github action에서 사용되고 있는 workflow 액션은 actions/checkout@v2과 actions/setup-python@v2 입니다. 여기서 Node 16의 기본 런타임은 2023년 9월 11일에 지원이 종료될 예정으로 액션을 실행할 때마다 경고 문구가 나왔습니다. 따라서 GitHub Actions 버전의 기능과 최적화를 활용하기 위해 최신버전으로 업데이트를 진행하였습니다.

  • actions/checkout@v4 : GitHub 저장소의 코드를 체크아웃하여 GitHub Actions 실행 환경의 작업 디렉토리에 배치합니다.
  • actions/setup-python@v4 : 작업 환경에 특정 버전의 Python을 설치하고 설정합니다.

github action 버전 업그레이드에 대한 PR은 링크에서 확인할 수 있습니다.

 

Enhance Testing with MySQL8 & Update GitHub Actions by mjs1995 · Pull Request #484 · julien-duponchelle/python-mysql-replicati

Overview Add MySQL-8 based test (with percona-8.0) Since below two features need to test based on MySQL version 8, this PR added percona-8.0 docker and test based on MySQL version 8. Optional Meta...

github.com


github action pytest 개선

단위 테스트

단위 테스트 프레임워크와 라이브러리

  • unittest는 파이썬의 표준 라이브러리이고 두 번째 pytest는 pip를 통해 설치해야 하는 라이브러리
  • 테스트 시나리오를 다루는 것은 unittest만으로도 충분할 것입니다. 왜냐하면 다양한 헬퍼 기능을 제공하기 때문입니다. 그러나 외부 시스템에 연결하는 등의 의존성이 많은 경우 테스트 케이스를 파라미터화할 수 있는 픽스처(fixture)라는 패치 객체가 필요하며 이렇게 보다 복잡한 옵션이 필요한 경우는 pytest가 적합합니다.
    • unittest
      • unittest 모듈은 자바의 JUnit을 기반으로 하며 JUnit은 Smaltalk의 아이디어를 기반으로 만들어졌으므로 객체 지향적입니다. 이러한 이유로 테스트는 객체를 사용해 작성되며 클래스의 시나리오별로 테스트를 그룹화하는 것이 일반적입니다.
      • 가장 일반적인 메서드는 실제 실행 값과 예상 값을 비교하는 assertEquals(, [, message])
      • 예외적인 상황이 발생하면 잘못된 가정 아래 실행을 계속하는 것보다는 예외를 발생시키고 호출자에게 바로 알려주는 것이 좋습니다. 이것이 assertRaises 메서드가 확인하려는 것입니다.
    • pytest
      • 차이점은 unittest 처럼 테스트 시나리오를 클래스로 만들고 객체 지향 모델을 생성하는 것이 가능하지만 필수 사항이 아니며, 단순히 assert 비교만으로 단위 테스트를 식별하고 결과를 보고하는 것이 가능합니다.
      • 픽스처(Fixture)
        • pytest의 가장 큰 장점 중 하나는 재사용 가능한 기능을 쉽게 만들 수 있다는 점입니다.
        • 테스트에서 사용될 때는 테스트 사전/사후에 사용 가능한 리소스 또는 모듈을 뜻합니다.
  • 더욱더 자세한 설명을 원하면 파이썬 클린코드 2nd Edition 책을 읽어보거나 서평을 읽어보시는 것을 추천드립니다.

pytest Fixtures

  • pytest의 fixture는 테스트 코드의 재사용성을 높이기 위해 사용되며, 테스트 코드 실행 전 특정 설정이나 값을 설정할 수 있게 합니다.
  • fixture는 여러 테스트에서 공용으로 사용될 수 있으며, 특정 scope 내에서만 사용가능합니다.
  • pytest가 테스트를 실행하려고 할 때 3가지 요소의 픽스처 실행 순서를 고려합니다.
    • scope
    • dependencies
    • autouse
      • autouse fixture는 정의된 scope 안에 있는 테스트에만 영향을 주며 scope 내에서 먼저 실행됩니다.
class PyMySQLReplicationTestCase(base):
  def ignoredEvents(self):
      return []

  @pytest.fixture(autouse=True)
  def setUpDatabase(self, get_db):
      databases = get_databases()
      # For local testing, set the get_dbms parameter to one of the following values: 'mysql-5', 'mysql-8', mariadb-10'.
      # This value should correspond to the desired database configuration specified in the 'config.json' file.
      self.database = databases[get_db]

conftest.py

  • conftest.py 파일은 한 디렉토리에 있는 모든 테스트에 fixture를 제공하는 수단입니다.
  • conftest.py에 정의된 fixture는 해당 패키지의 어떤 테스트에서도 import 없이 사용될 수 있습니다.(pytest가 자동으로 검색합니다.)
  • 여러 conftest.py가 중첩된 디렉토리 구조를 가질 수 있으며, 각 디렉토리의 conftest.py는 상위 디렉토리의 conftest.py에서 제공하는 fixture에 추가하여 자체 fixture를 제공할 수 있습니다.
import pytest


def pytest_addoption(parser):
    parser.addoption("--db", action="store", default="mysql-5")


@pytest.fixture
def get_db(request):
    return request.config.getoption("--db")
  • pytest_addoption
    • pytest_addoption은 pytest 초기화 단계에서 커맨드라인 옵션 및 설정 값을 등록하는 데 사용됩니다
    • parser.addoption(...): 커맨드라인 옵션을 등록하며 argparse의 add_argument() 함수가 받는 속성과 동일합니다
    • 플러그인 또는 테스트의 루트 디렉토리에 위치한 conftest.py 파일에서만 구현되어야 합니다.

pytest keyword expressions

  • pytest -k
  • 이 표현식은 대소문자를 구분하지 않으며, Python 연산자를 포함할 수 있습니다.
  • EXPRESSION 부분에는 파일명, 클래스명, 함수명 등의 문자열을 포함하여 조건을 지정할 수 있습니다.
env:
  PYTEST_SKIP_OPTION: "not test_no_trailing_rotate_event and not test_end_log_pos and not test_query_event_latin1"

pytest -k "$PYTEST_SKIP_OPTION"
  • test_no_trailing_rotate_event, test_end_log_pos, test_query_event_latin1라는 문자열이 포함되지 않은 테스트를 선택하여 실행합니다.

pytest markers

  • pytest -m
  • pytest -m 옵션은 마커 표현식을 사용하여 테스트를 선택합니다.
  • MARKER 부분에는 미리 정의된 마커의 이름을 지정합니다.
pytest -k "$PYTEST_SKIP_OPTION" -m mariadb
  • @pytest.mark.mariadb 데코레이터가 지정된 테스트만 선택하여 실행합니다.
@pytest.mark.mariadb
class TestMariadbBinlogStreamReader(base.PyMySQLReplicationTestCase):
    def setUp(self):
        super().setUp()
        if not self.isMariaDB():
            self.skipTest("Skipping the entire class for MariaDB")

@pytest.mark.mariadb
class TestOptionalMetaData(base.PyMySQLReplicationTestCase):
    def setUp(self):
        super(TestOptionalMetaData, self).setUp()
        self.stream.close()
        self.stream = BinLogStreamReader(
            self.database,
            server_id=1024,
            only_events=(TableMapEvent,),
        )
        if not self.isMySQL8014AndMore():
            self.skipTest("Mysql version is under 8.0.14 - pass TestOptionalMetaData")
        self.execute("SET GLOBAL binlog_row_metadata='FULL';")
  • 위의 코드에서는 TestMariadbBinlogStreamReader와 TestOptionalMetaData 클래스에 대해서만 pytest가 실행됩니다.

mysql5.7, mysql5.7-ctl, mysql8, mariadb에 대해서 테스트 코드 구조 개선 및 github action에 대해서 pr을 올렸습니다.

 

Modify test structure by heehehe · Pull Request #502 · julien-duponchelle/python-mysql-replication

Resolve #499 Overview As we add test for MySQL 8 in #484, we need to create an additional class to test everything against MySQL 8. So instead of creating a class based on the database, we'd like t...

github.com


후기

https://www.oss.kr/notice/show/53ba6af3-867d-45ad-a31c-ba576c4f1855

2023 오픈소스 컨트리뷰션 아카데미를 통해서 저희 팀은 우수상을 수상하였습니다.

python-mysql-replication 프로젝트에 기여하면서 다양한 개발 경험을 얻었습니다. 이 과정에서는 기술적 지식을 획득하는 것뿐만 아니라, 오픈소스 커뮤니티와의 협업에 대한 인사이트 또한 얻을 수 있었습니다. 오픈소스에 기여하는 것은 코드 작성뿐만 아니라 지식 공유, 협업, 함께 성장하는 커뮤니티의 일원이 되어가는 것 같습니다. OSSCA 프로그램을 통해 멘토 및 동료 멘티들과의 교류 과정에서 많은 것을 배우고, 개인적으로 큰 성장을 이룰 수 있었던 것이 무척 값진 경험으로 남았습니다.

마지막으로 저희 팀 프로젝트 기여에 대해 궁금하시면 아래 포스팅을 추천드립니다.

 

python-replication-mysql Event Occured Time Column sync

python-replication-mysql는 Master에 최대한 부하를 주지 않고 데이터 분석, CDC를 가능하게 만드는 프로젝트이다. 국내기업에서는 카카오, 원티드 해외기업에서는 아마존에서 CDC로 사용되고있는 오픈소

seanical.tistory.com

 

[OSSCA] python-mysql-replication - 개념 이해하기

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

heehehe-ds.tistory.com

 

[pymyrep] python pep8, docstring

7월부터 9월까지 오픈소스 컨트리뷰션 아카데미(OSSCA)에 참가했다. 참가한 프로젝트는 python-mysql-replication으로, mysql에서 replication(복제)을 할 때 replica(slave) 대신 Python을 통해 변동 사항 등을 조회

starcat37.tistory.com

 

오픈소스 성능 문제 개선하기

근 2개월 정도 되는 시간동안 OSSCA(오픈소스 컨트리뷰션 아카데미)의 활동에 참여했었습니다! 그 중에 가장 기억에 남았던 오픈소스(python-mysql-replication)에서 발생하는 성능 문제 개선에 대해 공

velog.io

 

Previous_gtids_log_event in MySQL Replication

1. 소개 Previous_gtids_log_event 는 MySQL DB 에서 발생하는 binlog event 중 하나로 직전 binlog 파일에 기록된 gtid_executed 를 담고 있는 이벤트이다. gtid_executed 는 특정 DB 에 지금까지 발생한 모든 GTID 들을 간

suhwanc.tistory.com

 

profile

융무의 기술블로그

@융무

포스팅이 좋았다면 "좋아요❤️" 또는 "구독👍🏻" 해주세요!