본문 바로가기
IT

파이썬을 활용한 NoSQL 몽고디비(MongoDB) 및 Redis TPCC 테스트 소개 및 python 코드 분석 과정

by eddy's warehouse 2024. 3. 20.

이번 글에서는 파이썬을 이용하여 NoSQL인 몽고디비의 TPCC(Transaction Processing Performance Council)를 테스트하는 오픈소스 프로젝트인 py-tpcc에 대해 소개합니다. 특히, 이 소스코드는 MongoDB와 Redis를 대상으로 TPCC 테스트를 수행하는 기능을 포함하고 있습니다.

우선, 우리는 Redis TPCC 테스트 코드가 포함된 이 소스코드가 어디 있는지 알려드릴 것입니다.
해당 코드는 몽고DB의 GitHub에 존재하는 MongoDB의 TPCC 테스트 코드로, MongoDB에서는 이 코드를 활용하여 TPCC가 잘 동작함을 보여줍니다. 이를 통해 MongoDB와 Redis의 TPCC 성능을 측정하면 다른 일반 DBMS인 MariaDB나 Altibase, Cubrid와 같은 DBMS들과 성능 비교가 가능할 것입니다.

또한, 이 코드는 MongoDB Adaptation of PyTPCC으로 GitHub에 공개되어 있습니다.

이어서 py-tpcc에서 Redis TPCC 테스트 시 발생한 오류를 확인하는 과정을 살펴보겠습니다. 이 코드는 Redis TPCC를 구현한 소스로부터 발생한 오류로, 이를 해결하기 위해 파이썬의 예외 처리 구문을 이용하여 콜스택을 출력하는 방법을 사용했습니다.

특히, 발생한 Redis 오류를 해결하기 위해 디버깅 도구로서 Visual Studio Code(VSCode)를 활용하는 과정을 간략하게 설명합니다.

파이썬을 활용한 NoSQL 몽고디비(MongoDB) 및 Redis TPCC 테스트 소개 및 python 코드 분석 과정 썸네일

py-tpcc 소개

python을 이용해서 NoSQL인 몽고디비의 tpcc를 테스트한 오픈소스가 있습니다.

이 소스에는 redis tpcc 테스트 코드가 존재합니다. 저는 유사한 코드로 NoSQL 그리고 일반 mysql이나 mariadb와 같은 유명한 오픈소스 DB와 altibase나 cubrid와 같은 국산 DBMS의 tpcc 성능을 테스트하려고 합니다.

 

이 코드는 mongolab의 github에 존재하는 mongodb의 tpcc 테스트 코드입니다. mongodb에서는 이 코드를 이용해서 tpcc가 잘 동작할 수 있다는 점을 설명합니다.

이 코드를 이용해서 mongodb와 redis의 tpcc 성능을 측정한다면, 다른 일반 DBMS인 mariadb나 altibase, cubrid와 같은 DBMS들과 성능 비교가 될 수 있습니다.

해당 코드는 아래의 github에 존재합니다.

mongodb-labs/py-tpcc: MongoDB Adaptation of PyTPCC (github.com)

 

GitHub - mongodb-labs/py-tpcc: MongoDB Adaptation of PyTPCC

MongoDB Adaptation of PyTPCC. Contribute to mongodb-labs/py-tpcc development by creating an account on GitHub.

github.com

 

redis tpcc 테스트 오류 확인하기 위한 코드 삽입

해당 코드에서, redis tpcc를 구현한 소스가 오래되어서 에러가 발생합니다. 에러 메시지를 확인하기 위해서 먼저 py-tpcc 코드를 분석해 본 결과 execute 함수의 executeTransaction 함수에서 에러가 발생합니다.

에러가 발생하는 상황에서 에러의 원인을 파악하기 위해서는 파이썬의 try ~ except 구문을 사용해서 예외 처리시 콜스택을 출력하도록 하였습니다.

 

해당 에러 코드는 다음과 같습니다.

    def execute(self, duration):
        global_result = results.Results()
        assert global_result, "Failed to return a Results object"
        logging.debug("Executing benchmark for %d seconds" % duration)
        start = global_result.startBenchmark()
        debug = logging.getLogger().isEnabledFor(logging.DEBUG)
        # Batch Results
        batch_result = results.Results()
        start_batch = batch_result.startBenchmark()
        while (time.time() - start) <= duration:
            txn, params = self.doOne()
            global_txn_id = global_result.startTransaction(txn)
            batch_txn_id = batch_result.startTransaction(txn)
            if debug: logging.debug("Executing '%s' transaction" % txn)
            try:
                (val, retries) = self.driver.executeTransaction(txn, params)
            except KeyboardInterrupt:
                return -1
            except (Exception, AssertionError) as ex:
                logging.warn("Failed to execute Transaction '%s': %s" % (txn, ex))
                traceback.print_exc(file=sys.stdout)
                print("Aborting some transaction with some error %s %s" % (txn, ex))
                global_result.abortTransaction(global_txn_id)
                batch_result.abortTransaction(batch_txn_id)
                if self.stop_on_error: raise
                continue

 

traceback.print_exc(file=sys.stdout) 을 통해서 콜스택을 출력할 수 있습니다.

 

Redis 에러 내용 확인하기

레디스에서 발생한 에러 내용을 확인해 보니 아래의 인덱스를 찾지 못하는 KeyError가 발생합니다.

2024-03-20 10:21:33,339 [execute:072] WARNING: Failed to execute Transaction 'PAYMENT': 'C_FIRST'
Traceback (most recent call last):
  File "/home/altibase/py-tpcc/pytpcc/runtime/executor.py", line 68, in execute
    (val, retries) = self.driver.executeTransaction(txn, params)
  File "/home/altibase/py-tpcc/pytpcc/drivers/abstractdriver.py", line 109, in executeTransaction
    result = self.doPayment(params)
  File "/home/altibase/py-tpcc/pytpcc/drivers/redisdriver.py", line 1055, in doPayment
    if cust['C_FIRST'] < customers[index]['C_FIRST'] :
KeyError: 'C_FIRST'

 

이 에러의 원인을 파악하기 위해서는 cust 딕셔너리에 'C_FIRST'가 있는지 혹은 customers[index]에 'C_FIRST'의 키를 갖는 데이터가 있는지 확인해 봐야 합니다.

vscode에서 디버깅 하기

vscode에서 디버거를 통해 'C_FIRST'의 딕셔너리 키가 정말 없는지 확인해 보기 위해서 launch.json을 다음과 같이 수정하였습니다.

아래에서 이 프로그램을 돌리기 위해서는 아규먼트(args)가 필요한데 아규먼트는 args 뒤에 이미지와 같이 넣어 주시면 됩니다.

그리고, 브레이크 포인트를 걸고 디버거를 실행하여 문제가 되는 딕셔너리를 확인합니다.

vscode 디버깅 하기

 

vscode 디버거에서 아래와 같은 딕셔너리가 나옵니다.

아래 딕셔너리를 자세히 확인해 보면 문자열 앞에 b가 붙은 것이 바이너리 문자열이라는 의미입니다.

customers[index]:{b'C_ID': b'1933', b'C_D_ID': b'8', b'C_W_ID': b'1', b'C_FIRST': b'tiwuxqd', b'C_MIDDLE': b'OE', b'C_LAST': b'BARABLEESE', b'C_STREET_1': b'uujztztitghavbwi', b'C_STREET_2': b'mgtxbyocrbnftfrwgshe', b'C_CITY': b'kfcaekzyrot', b'C_ZIP': b'zi', b'C_PHONE': b'138511111', b'C_SINCE': b'7170425601641183', b'C_CREDIT': b'2024-03-20T10:12:54.609256', b'C_CREDIT_LIM': b'GC', b'C_DISCOUNT': b'50000.0', b'C_BALANCE': b'0.1892', b'C_YTD_PAYMENT': b'-10.0', b'C_PAYMENT_CNT': b'10.0', b'C_DELIVERY_CNT': b'1', b'C_DATA': b'0'}

 

바이트 문자열은 말 그대로 바이트로 표현된 문자열을 말하며 b가 앞에 붙습니다. 실제로도 바이너리 바이트 단위로 되어있습니다. 그러나, 일반적으로 사용하는 파이썬의 문자열은 유니코드 문자를 사용하며, UTF-8 인코딩을 사용합니다. 그러므로 최대 3byte를 사용할 수 있습니다.

그러므로, customers[index]['C_FIRST']와 customers[index][b'C_FIRST']는 서로 다른 키 값이 되어 에러가 발생하였습니다.

바이너리 바이트 문자열은 isoformat()이라는 함수로 변환하여 유니코드 문자로 바꿀 수 있습니다.

그러나, 이 문제가 발생한 원인을 살펴보면, 레디스의 변화로 인한 것으로 추정되었습니다.

레디스의 현재 버전은 바이너리 문자열이 기본이라고 합니다.

그러나, 해당 코드는 유니코드 문자열이라고 가정하고 작성되어 있습니다.

 

레디스 라이브러리의 문자열 기본값 설정

레디스는 파이썬 라이브러리에서 최초 연결을 설정할 때 기본 값으로 문자열을 바이너리로 받을지 아니면 문자열로 받을지 선택할 수 있습니다.

아래와 같이 redis.Redis 함수에서 decode_responses=True로 설정함으로써 문자열을 유니코드로 자동으로 디코드해서 받을 수 있도록 할 수 있습니다.

import redis

# Redis 클라이언트 연결 설정
r = redis.Redis(
    host='localhost',  # Redis 서버 주소
    port=6379,         # Redis 포트 번호
    decode_responses=True  # 응답을 문자열로 자동 디코드
)

# 파이프라인 생성
pipe = r.pipeline()

# 파이프라인에 명령 추가
pipe.set('test_key', 'value')
pipe.get('test_key')

# 파이프라인 실행 및 결과 출력
results = pipe.execute()
print(results)  # ['OK', 'value']

# 결과 확인
print(type(results[1]))  # <class 'str'>

 

위와 같이 레디스의 연결 시 유니코드 문자열 decode를 기본으로 하여 접속하도록 수정 후 문제가 해결되었습니다.

 

오늘은 레디스에서 파이썬 라이브러리를 이용할 때 기본적으로 바이너리 문자열로 반환되는 것을 확인해 보았습니다.

그리고, 이때 유니코드 문자열을 이용하기 위해서는 어떻게 해야 하는지도 알아보았습니다.

고생하셨습니다.

facebook twitter kakaoTalk kakaostory naver band shareLink