Slide 1

Slide 1 text

GopherCon Korea 2023 Scenario Test 김재원 @ AB180 & Airbridge 인수 테스트 자동화로 자신감, 생산성 높이기 GopherCon Korea 2023 Data Pipeline Team Lead [email protected] https://abit.ly/ab180-scenario-test

Slide 2

Slide 2 text

GopherCon Korea 2023 Index ● 배경 소개 ● Scenario Test ● Lessons Learned ● What’s Next ● Q&A

Slide 3

Slide 3 text

GopherCon Korea 2023 배경 소개

Slide 4

Slide 4 text

GopherCon Korea 2023 Airbridge Daily 20억건 이상의 사용자 행동 데이터 수집 및 분석 People-Based Attribution

Slide 5

Slide 5 text

GopherCon Korea 2023 Airbridge Data Pipeline https://engineering.ab180.co/stories/kafka-consumer-proxy

Slide 6

Slide 6 text

GopherCon Korea 2023 Airbridge Data Pipeline 1) WAS 마이그레이션: Python -> Go 2) Worker 마이그레이션: Python -> Rust 3) DB 마이그레이션: DynamoDB -> ScyllaDB 4) 네트워크 비용 최적화 비용 최적화 액션 아이템

Slide 7

Slide 7 text

GopherCon Korea 2023 Airbridge Data Pipeline 1) WAS 마이그레이션: Python -> Go https://abit.ly/ab180-go-migration

Slide 8

Slide 8 text

GopherCon Korea 2023 Airbridge Data Pipeline 2) Worker 마이그레이션: Python -> Rust AS-IS: TO-BE: - 성능이 매우 중요한 부분에 Rust 도입 및 분리를 결정함 - Preprocessing 로직이 매우 복잡한데 - 이 복잡한 걸 Python, Rust로 둘 다 유지보수 해야하는 상황

Slide 9

Slide 9 text

GopherCon Korea 2023 Airbridge Data Pipeline 2) Worker 마이그레이션: Python -> Rust TO-BE: - 성능이 매우 중요한 부분에 Rust 도입 및 분리를 결정함 - Preprocessing 로직이 매우 복잡한데 - 이 복잡한 걸 Python, Rust로 둘 다 유지보수 해야하는 상황

Slide 10

Slide 10 text

GopherCon Korea 2023 Airbridge Data Pipeline 2) Worker 마이그레이션: Python -> Rust TO-BE1: TO-BE2: Preprocessor를 Golang으로 만들어서 전처리를 한 데이터를 따로 만들기로 결정!

Slide 11

Slide 11 text

GopherCon Korea 2023 1. Touchpoint Worker는 성능이 매우x100 중요해서 Rust로 결정 2. Preprocessor는 성능도 중요하긴 하지만 유지보수 가능 여부가 훨씬x100 중요해서 Golang으로 결정 Airbridge Data Pipeline 2) Worker 마이그레이션: Python -> Rust

Slide 12

Slide 12 text

GopherCon Korea 2023 마이그레이션 검증은 어떻게? 1. 단위 테스트는 최대한 옮긴다 2. 필요한 통합 테스트 또한 옮긴다 3. 인수 테스트는 전부+ 더 만들어서 옮긴다 - 기존 Python 코드로 일주일치 테스트 케이스를 만듦 - 확신을 매우 높여줌

Slide 13

Slide 13 text

GopherCon Korea 2023 인수 테스트 (Acceptance Test)

Slide 14

Slide 14 text

GopherCon Korea 2023 인수 테스트 (Acceptance Test) - Given: 어떤 환경이 주어졌을 때 - When: 어떤 요청사항이 들어온다면 - Then: 이런 결과가 나오길 기대한다

Slide 15

Slide 15 text

GopherCon Korea 2023 기존 인수 테스트의 문제 1. 매번 짜야하는 코드가 많음 - 그래서 잘 안짜게 되거나 생산성이 떨어짐 - Framework이 없다면 각자 다른 스타일로 짜게 됨 - 규칙을 정할 순 있겠으나, 강제하기 어려움 2. 어떤 인수테스트가 있는지 한 눈에 보기 어려움 - 이 서비스에는 어떤 기능적인 요구사항을 만족하고 있을까? 3. 언어 변경을 하게 된다면 사람이 손으로 옮겨줘야 함 - 언어를 변경할 일은 잘 없으니 참을만 함

Slide 16

Slide 16 text

GopherCon Korea 2023 Scenario Test

Slide 17

Slide 17 text

GopherCon Korea 2023 Scenario Test 자동화된 인수 테스트 Scenario: “json 형태”의 인수 테스트 케이스 하나 - (Given) Context: 이 데이터를 처리하기 위해 필요한 외부 데이터 (RDB 데이터, OS 환경변수 등) - (When) Input: 실제 Component의 Input - (Then) Output: 기대하는 Output (Kafka로 보내야하는 데이터, Cache에 써야하는 데이터 등) my_scenario.json

Slide 18

Slide 18 text

GopherCon Korea 2023 Scenario Test 자동화된 인수 테스트

Slide 19

Slide 19 text

GopherCon Korea 2023 my_scenario.json Scenario Test Scenario Generator

Slide 20

Slide 20 text

GopherCon Korea 2023 my_scenario.json Scenario Test Scenario Tester

Slide 21

Slide 21 text

GopherCon Korea 2023 Scenario Test Scenario Tester - 결과

Slide 22

Slide 22 text

GopherCon Korea 2023 Scenario Test 1. 기존 Python 코드로 일주일치 Scenario를 만듦 - Kafka -> Generator(on ECS) -> Kafka 구조로 생성 - 100억 개가 넘는 케이스를 생성 - 장애 방지를 위해 Scenario Generator 전용 Infra를 다 새로 띄워 격리시킴 2. 만들어진 Scenario로 Golang 코드 검증 - Kafka -> Tester(on ECS) -> Kafka 구조로 테스트 - 실패한 케이스만 모아두고 로컬에서 검증 - 코드 다 고쳤으면 다시 검증 마이그레이션 과정에서의 활용

Slide 23

Slide 23 text

GopherCon Korea 2023 Scenario Test 1. encoding/json 패키지에서 생긴 이슈 - “<”와 “>”를 unicode escaping 해버림 단위 테스트로는 못잡았던 케이스 “>” 를 “\u003e”로 바꿔버리는 모습.. 🤦

Slide 24

Slide 24 text

GopherCon Korea 2023 Scenario Test 1. encoding/json 패키지에서 생긴 이슈 - “<”와 “>”를 unicode escaping 해버림 단위 테스트로는 못잡았던 케이스 Marshal을 이런 식으로 해서 해결 (별로 행복하진 않음)

Slide 25

Slide 25 text

GopherCon Korea 2023 Scenario Test 1. encoding/json 패키지에서 생긴 이슈 - “<”와 “>”를 unicode escaping 해버림 - json.Unmarshal을 할 때 case-insensitive하게 받음 단위 테스트로는 못잡았던 케이스 key를 “lowercase_key”로 정의했으나 “LOWERCASE_KEY”도 받아주는 모습.. 🤦

Slide 26

Slide 26 text

GopherCon Korea 2023 Scenario Test 1. encoding/json 패키지에서 생긴 이슈 - “<”와 “>”를 unicode escaping 해버림 - json.Unmarshal을 할 때 case-insensitive하게 받음 단위 테스트로는 못잡았던 케이스 <해결> A. Case Sensitivity가 중요하지 않은 struct(정해진 값만 들어오는 것이 확정)는 Known-issue로 처리 B. Case Sensitivity가 중요한 struct는 정해진 Key만 받도록 Unmarshal을 직접 구현 (별로 행복하지 않음... 😭 더 좋은 방법/라이브러리가 있다면 알려주세요)

Slide 27

Slide 27 text

GopherCon Korea 2023 Scenario Test 1. encoding/json 패키지에서 생긴 이슈 - “<”와 “>”를 unicode escaping 해버림 - json.Unmarshal을 할 때 case insensitive하게 받음 2. 그외 미처 생각도 못했던 곳에서 발생했던 실수 - Timestamp를 13자리(ms)로 기록해야하는데 10자리(sec)로 기록함 - 잘못된 hash 알고리즘으로 hashing을 함 단위 테스트로는 못잡았던 케이스 (기억은 다 안나지만, 이거 보다 훨씬 많은 케이스가 있었습니다)

Slide 28

Slide 28 text

GopherCon Korea 2023 Scenario Test 1. Dependency Injection이 쉬운 구조로 코드를 짜놔야 편하다 - Mock Infra로 쉽게 교체할 수 있어야 Generator/Tester를 만들기 편함 2. 활용한 도구 - https://github.com/yudai/gojsondiff - Diff가 생겼을 때 예쁘게 볼 수 있음 - https://pkg.go.dev/gorm.io/gorm/callbacks#AfterQuery - gorm AfterQuery Callback을 통해 Context를 capture할 수 있음 3. stdin, stdout(+ Makefile)을 잘 활용하면 쉽게 생성/테스트 할 수 있다 만들때 팁

Slide 29

Slide 29 text

GopherCon Korea 2023 Scenario Test 만들때 팁 https://github.com/yudai/gojsondiff 어떤 테스트 케이스에서 어떤 Diff가 생겼는지 시각화 해주어서 디버깅 하기 아주 좋음 👍 (빨간색은 원래 기대한 결과, 초록색은 현재 결과)

Slide 30

Slide 30 text

GopherCon Korea 2023 Scenario Test 만들때 팁 stdin, stdout으로 Scenario 만들기 (자유도가 높고 쉬움)

Slide 31

Slide 31 text

GopherCon Korea 2023 Scenario Test 1. 개발 공수가 많이 든다 - 고도화를 하다보니, 서비스 하나를 만드는 수준의 공수가 들었음 - 언어마다 다 각자 개발해줘야 함 (Python, Go, Rust) 2. Scenario Generator/Tester를 잘못 만들면 말짱 도루묵 - 일반적으로 짜는 테스트 코드 보다 복잡하기 때문에 실수하기 더 쉬움 단점

Slide 32

Slide 32 text

GopherCon Korea 2023 Scenario Test - 기존 Python Scenario Generator에서 Scenario를 잘못 만듦 - Input Data를 코드에서 다루다가 변경해버림 - A -> A’ 로 바뀐 상태로 Scenario가 저장됨 - A’를 Input으로 받는 코드를 작성해서 Golang으로 신나게 검증을 함 🥲 겪었던 장애 - Scenario를 잘못 만든 케이스 Input Data는 불변성을 보장할 수 있도록 장치 추가

Slide 33

Slide 33 text

GopherCon Korea 2023 1. 한 번의 큰 장애 🥲 (및 성장) 2. 15배 효율적인 이벤트 처리 (월 천 만원 이상의 비용 절감 효과 🤑) 3. Python -> Go - 강타입 언어라 유지보수 하기 훨씬 용이함 👍 - 훨씬 좋은 성능 👍 - 더 확장성 있는 구조로 변경됨 👍 마이그레이션 결과

Slide 34

Slide 34 text

GopherCon Korea 2023 Lessons Learned

Slide 35

Slide 35 text

GopherCon Korea 2023 Lessons Learned ● 마이그레이션을 하면서 겸사겸사 리팩토링을 하면 오히려 안좋다 ○ 매몰작업이 될 수 있더라도 미리 리팩토링을 하고, 같은 구조로 옮겨야 검증이 쉬움 ● 일정은 더 넉넉히 잡아야 한다 (자기 자신을 과대평가 하지 말자) ○ 처음에 2개월 정도 걸릴줄 알았으나 4개월 걸림 ● 리팩토링을 이제 정말 자신있게 할 수 있게 되었다 ○ 레거시를 다시 짜면서 성능 뿐만 아니라 오히려 생산성도 올라감 ○ 하지만 이때 제일 조심해야됨

Slide 36

Slide 36 text

GopherCon Korea 2023 What’s Next

Slide 37

Slide 37 text

GopherCon Korea 2023 What’s Next 1. 여러 컴포넌트에 걸친 테스트를 할 수 있게 하기 - A Service의 Output을 B Service의 Input으로 씀 - 한 서비스의 시나리오 파일은 Downstream 서비스들에 다 포함돼야 함 2. 언어에 구애받지 않는 단일화된 Generator/Tester 만들기 - 현재는 언어마다 각자의 Generator/Tester가 있음 - 관리도 힘들고 사용법도 각자 조금씩 다름 - 잘 만들어서 Open Source화를 할 수 있으면 좋지 않을까 3. 비기능적 요구사항도 테스트

Slide 38

Slide 38 text

GopherCon Korea 2023 Q & A

Slide 39

Slide 39 text

GopherCon Korea 2023 Thank you https://recruit.ab180.co/