토스뱅크는 거래내역 조회를 할 때마다 계정계의 코어뱅킹 서버에 요청을 보내는 것이 아니라 채널계에 있는 송금 서버에만 요청을 보내고 있습니다. 이렇게 동작하기 위해서는 송금 서버가 항상 코어뱅킹 서버와 거래내역을 정확하게 동기화해야하는 문제를 해결해야 합니다. 조금만 어긋나면 거래내역이 누락되는 문제가 생기면서 은행의 신뢰성에 큰 타격을 입을 수 있습니다. 하지만 또 코어뱅킹 서버와 너무 자주 동기화를 시도하면 코어뱅킹 서버의 부하가 커져서 장애가 발생할 수도 있습니다.
카카오톡, 인스타그램 등의 SNS 서비스의 경우 비즈니스 특성 상 일부 동기화가 진행되지 않아도 신뢰성에 큰 타격이 가거나 하지는 않습니다.
- 토스뱅크에서 이체가 일어나는 경우 이를 송금 서버 DB에도 저장합니다.
- 타행 서비스로 이체를 한 경우 코어뱅킹 서버에서 송금 서버로 이체 정보를 알려줘야 합니다.
- 토스뱅크에서는 Kafaka를 이용하고 있습니다.
- 코어뱅킹 서버가 Kafaka 토픽에 메시지를 프로듀싱하고 송금 서버가 컨슘하여 송금 DB에 저장합니다.
송금을 실행하는 도중 코어뱅킹 서버의 응답이 늦어져서 타임아웃으로 송금 서버가 응답을 받지 못한다면 송금 서버는 거래내역을 DB에 저장하지 못할 것이고, 사용자는 송금에 성공했더라도(코어뱅킹 서버에서는 송금 성공!) 해당 거래내역을 확인할 수 없게 됩니다.
Kafaka를 이용해 이 문제를 해결할 수 있습니다. 송금 서버에서 송금 API 실행 중 타임아웃이 발생했더라도 코어뱅킹 서버에서 송금이 완료되었을 때 Kafaka 토픽을 통해 송금 서버에게 이체 실행 결과를 알려준다면 송금 서버는 해당 데이터를 DB에 저장할 수 있습니다.
(더 있음..)
계속해서 DB 저장에 실패하는 경우 컨슈머 데드 레터(Consumer DeadLetter)라는 Kafka 토픽에 실패한 메시지를 저장하고 개발자가 실패하는 원인을 확인하여 문제를 해소한 뒤 해당 토픽을 다시 컨슘해서 송금 이력을 저장하게 할 수 있습니다.
송금 서버의 여러 스레드를 활용해 동시에 여러개의 동기화 메시지를 처리할 수 있습니다. Kafka는 메시지들을 여러 파티션으로 나눠서 여러 개의 컨슈머가 처리할 수 있게 해줍니다. 또한 이 때 개발자가 지정한 키를 기준으로 파티션을 나눠줍니다. 예를 들어 거래내역을 동기화하는 경우, 계좌번호를 키로 삼으면 같은 계좌의 거래내역은 반드시 같은 파티션에 들어가게 되고 같은 파티션은 같은 컨슈머가 처리하기 때문에 같은 계좌를 여러 컨슈머가 동기화하면서 발생할 수 있는 동시성 문제를 방지할 수 있습니다. 하지만 파티션을 계속 늘릴 수는 없습니다. 파티션을 늘리는데에는 시간이 걸리며 그 사이에 동기화 문제가 발생할 수도 있고 파티션을 한번 늘리면 다시 줄일 수 없기 때문에 많은 자원을 잡아먹을 수 있습니다. 이는 컨슈머당 여러개의 워커 스레드를 할당해 해결할 수 있습니다. 만약 파티션을 10개로 하고 컨슈머당 워커 스레드를 100개로 한다면 총 1천개의 스레드로 건당 100ms 속도로 동기화를 하게 되므로, 초당 1만건의 동기화가 가능해지게 됩니다. 이때 워커 스레드 또한 계좌번호 기준으로 선택하게 해야합니다.
이렇게 여러 방어 로직을 만들어 두어도 문제가 발생할 수 있습니다. 이러한 상황을 대비해 사용자가 직접 동기화를 실행할 수 있도록 '동기화 버튼'을 만들어 둘 수 있습니다.
진짜 돈이랑 관련된 서비스는 모든 예외 상황을 다 고려할 수 있어야 한다는게.. 참.. 어려운 것 같다..