트랜잭션 엔진 일람

사람들이 말하는 프로그래밍은 대부분 원주율 계산이 아니라 소프트웨어 시스템 구축이다. 소프트웨어 시스템 구축에 있어서 애플리케이션 상태 관리가 핵심이다. 따라서 백엔드 프로그래밍에서 트랜잭션 엔진이야말로 핵심 요소다. 그 중요성이 너무 덜 강조된 것과 대조되게 말이다.

튜링 머신

당연히 모든 트랜잭션 처리 수단은 이 범주에서 벗어나지 못한다. 문제 연산을 위해 발명되었지만, 소프트웨어 시스템 구축에 더 많이 쓰이는 것 같다.

메모리(Random Access Memory)

메모리에서 당연히 트랜잭션을 처리할 수 있다. 문제는 트랜잭션 중간 상태에서 정전이나 고장이 발생할 경우, 트랜잭션의 원자적 적용에만 실패하는 것이 아니라, 원 상태마저 누락하는 재난이 발생해버린다는 점이다.

우리가 사용하는 메모리 장치가 용량이 무한대이고 절대 정전이나 고장이 발생하지 않는 추상적인 “완벽한” 메모리라면 데이터베이스를 발명할 필요도 없었을 것이다.

관계형 데이터베이스(RDBMS)

흔히 백엔드 프로그래밍에 node.js를 써야 하나 python을 써야 하나 하는 토론이 발생하지만, 정작 중요한 부분은 왕왕 DBMS 쪽이라는 것이 간과되곤 한다. RDBMS는 오랜 역사 기간동안 트랜잭션 엔진 영역의 왕좌를 지켜왔다. 그러다 최근에 왕좌에서 끌려 내려왔는데, 스케일 문제 때문이었다. RDBMS는 수평확장성이 좋지 않았다.

여러 문서간 트랜잭션을 지원하지 않는 NoSQL

애플리케이션이 트랜잭션 처리 기능을 포함해야 한다. 이것을 잘하기 아주 어려움. 잘 해도 더러워짐. 확장성의 대가.

액터 모델(Actor Model)

애플리케이션이 트랜잭션 처리 기능을 포함해야 한다. 쉬운 확장성의 대가. 액터 모델은 상태 변경 명령을 전달하는 방식으로 작동한다. 다중 객체 상태의 동시 settlement에 약하다.

LMAX 아키텍처 (싱글 스레드, 이벤트 소싱을 통한 레플리케이션)

이 방식은 LMAX 외환 거래소, 한국거래소, 됴쿄증권거래소, 상하이증권거래소 같은 곳에 모두 적용되어 있다. 한마디로 높은 빈도의 트랜잭션 처리 영역을 장악하고 있다.

싱글스레드 모델. 수평확장 불가. 아이러니하게 TPS가 가장 높음 (6백만 TPS) . 저장공간 수평확장 불가한 것이 최대 단점.

참고: https://martinfowler.com/articles/lmax.html

고신뢰성 분산 키값 저장소 (a.k.a 분산 코디네이터)

Apache Zookeeper, etcd, Google Chubby 등 구현이 있다.

분산 저장소지만 기본적으로 중앙화된 저장소라고 봐야 한다. 다만 클라이언트에 데이터 누락의 위험 없이 최신 상태를 지속적으로 (최종 일관성) 동기화 해준다는 보장을 한다. (클라이언트로의) 분산 동기화에 특화되어 있는 저장 백엔드라고 봐야 한다.

장점:

  • 거의 완벽한 안전성.

단점:

  • 여러 문서간 트랜잭션 기능 미제공
  • TPS
  • 수평확장 불가

트랜잭션 기능 있는 클라우드 스케일 NoSQL DBMS

단점이 거의 없음. ACID + 확장성. 레이턴시를 대가로 CAP 모두 만족시킬 수 있음. 범용적임.

가상 액터 + 트랜잭션 패치(즉 Microsoft Orleans)

레플리케이션 불가. 하지만 상태 랜딩 (영구화) 백엔드가 레플리케이션 기능 있는 DBMS일 경우 레플리케이션 기능이 생긴다. 단 레플리케이트 되려면 컨시스턴트한 쓰기를 해야 함. 이는 레이턴시를 대가로 함. 객체 데이터베이스(Object Database)라 할 정도의 높은 표현력이 킬링 포인트. 다른 장점: 투명한 분산 / 쉬운 수평확장. Stateless 서비스에 준하는 배포 민첩성.

레플리케이션 활성화된 Redis

장점: 높은 TPS (백만 TPS), 안전성.

단점:

  • 레플리카 랙 기간동안의 아주 미약한 데이터 불안전성 (크로스 리전 20ms 대. 베어 메탈 로컬 네트워크 1ms 대)
  • Redis의 자료형 표현력의 한계 (메모리 주소 쓸 수 없음)
  • 그로 인한 프로그래밍 표현력 부족. 애플리케이션 상태 의존적인 트랜젹션은 제한된 lua로 짜야 함 (즉 client-controlled 트랜잭션의 부재).
  • 수평확장 불가 (수평확장 시 트랜잭션 불가)
  • 수직 확장의 공간 한계가 낮음(메모리 한계). 콜드 데이터를 디스크로 랜딩 시키는 기제(와 온디맨드 로딩 기제)가 필요할 경우 애플리케이션 단에서 제공해야 함.

블록체인?!

크게 퍼블릭 체인과 프라이빗 체인으로 나뉨. 퍼블릭의 경우 크게 POW와 POS로 나뉨. 각각 아주 많은 종류가 있다. 종류에 따라 TPS, 데이터 redundancy 수준, 분산 공격 가능성 모두 다름. 단점도 각각 다름. 장점이 뭐냐 라고 한다면, 퍼블릭 프라이빗을 막론하고 한가지로 요약할 수 있음. 가트너의 평가를빌자면:

멀티 파티 신뢰 문제를 해결함.

(It allows untrusted parties to exchange commercial transactions.)

Gartner Top 10 Strategic Technology Trends for 2018

즉 트랜잭션 엔진의 멀티 파티 소유가 가능해짐. 그중 “파티”의 뜻은? RFC 7519(JSON Web Token)의 서언에서 말하는 “two parties”의 “파티”와 같은 뜻. 한발 나아가 그 어떤 파티에도 소유되지 않은 트랜젹션 엔진이 탄생했음을 의미. 역사상 처음으로 애플리케이션의 상태에 대한 글로벌 컨센서스를 독점 외의 수단으로 달성할 수 있게 됨.

단점: 블록체인 트릴레마(불가능 삼각형). 각자 단점이 모두 다르지만 공통된 단점: 레이턴시. 아직까지 효과적인 수평확장이나 계층화된 확장 솔루션이 구현 및 실천에서 성공한 기록이 없음(이론과 소규모실천에는 많음). 애플리케이션 상태의 비밀성 부재. 결정론성 요구 때문에 상단 애플리케이션에서 외부 상태 참조 불가.

결론

CAP 이론이 소프트웨어 개발, 그중에서도 트랜잭션 엔진에 무엇을 의미하는지 지금까지도 업계가 오해해왔던 만큼, 업계가 이 영역에 대해 모르는 것도 많을 것이다. 많은 토론이 필요한 이유다.

싱글스레드 모델이 액터 모델(Actor-Model)보다 나은 X가지 이유

1. 액션(동작) 순서 컨트롤이 된다. 액션의 페이스를 컨트롤할 수 있다. 예를 들어 30fps 로 모든 몹의 x좌표 더하기 1. 액터 모델에서는 비동기라서 그 액션이 어느 프레임에 들어갈찌 모른다.

2. 디버깅이 쉽다.
2.1 액터 모델에서는 글로벌 상태에 브레이크 포인트를 찍은 후에 매개인의 메일박스를 검사해야 하는 번거로움이..
서버의 경우 클라와 달리 브포보다 로그가 더 유용할 경우가 많다. 예를 들어서 Joe Armstrong 과 Richard Stallman 등 분들이 모두 printf 디버깅 방식을 더 선호한다는 소문이… 하지만 서버가 클라만큼 상태가 복잡할 경우, 브포(==시간정지) 를 통한 글로벌 상태 파악이 극히 중요해질 때가 있다.
2.2 그리고 액터 모델은 비결정론적이라서 어떤 버그들이 어떤때는 재현되고 어떤 때는 재현되지 않을 수 있다. (그러고보니 2.2 항목은 LMAX 의 장점만 말하면 다 나오네.)

3. 반경 10메터안 모든 몹들에게 범위공격. 액터모델의 경우 남의 좌표를 알 수 없어서 일일이 물어야 한다. “님 좌표 얼마임?” (그렇지 않으면 좌표를 좌표테이블에 등록해야 하는데, 그러면 액터모델이 아니게 됨) (IO 오버헤드, 코딩 코스트)

4. 로드밸런싱따위 필요없다. 액터모델의 경우 (스케쥴러에 따라 다르지만) (어차피 스케쥴러 연구하는 사람은 없으니) 액터 로드밸런싱이 필요한 경우가 있다(Skynet 역사 참조). 반면 싱글스레드 모델에서 프로그래머는 신이고, 로드밸런싱따위 필요없다.

5. 트랜잭션?

6. 클라도 싱글스레드 모델이잖아.

7. CPU 성능에 좋다 (동일언어) (사실 이건 가장 중요하지 않은 원인이다) (늘여쓰면 글한편감이라 자세한 내용은 생략한다).

결론: 두 모델이 서로 다른 문제 도메인 (Problem Domain) 에 적합할 것 같다는거다:

  • 액터모델은 격리성, 분산성과 faul-tolerance 가 골(goal)에서 제일 중요한 경우 또는 message-passing 자체가 문제를 잘 모델링할 경우 (IM, Instant Messenger: e.g. Facebook Messenger, Whatsapp, etc.)
  • 싱글스레드 모델은 글로벌 상태 컨트롤이 중요한 경우. 예를 들어 게임. (시뮬레이팅 문제?)

적재적소(適材適所).

길게 쓰면 아무도 안읽으니 이번에도 후딱 끝내는 미덕을 발양하겠다.

(끝)

Skynet 의 UDP 지원

작자: cloudwu

번역: coolspeed

오랜 고민 끝에 결국 skynet (https://github.com/cloudwu/skynet) 에 기본적인 UDP 지원기능을 넣기호 했다. 대부분 경우, 특히 네트워크 게임 영역에 있어서 나는 UDP 프로토콜 사용을 반대하지만 skynet 은 이미 게임영역에만 사용되는게 아니라는 점을 고려하여 UDP 지원을 추가하는게 의미있는 일이라고 생각했다.

그나저나 최근의 피드백상으로 보면 어떤 사람은 skynet 을 이더넷 스위치 (Ethernet Switch) 구현에 사용했고 (사용한 CPU 가 powerpc 의것이여서 Big / Small Endian 관련된 버그들을 해결하는데 도움이 됐다고 한다), 증권분야에 사용한다는 사람도 있었다. 그리고 비디오 방송 분야에서 사용한다는 사람도 있었다. 그리고 아마 skynet 을 web 개발에 사용하는 사람도 있을 것이다. 간단한 테스트 결과에 의하면 성능면에서 lua 를 nginx 에 접합시켜 사용하는 것보다 못지 않았다.

현재 UDP 부분의 코드는 이미 완성되였는데 github 에서 “udp”라는 브랜치에 넣었다. 머지 않아 마스터 브랜치에 머지할 생각이다. 나는 이쪽의 니즈가 없어서 이런 니즈가 있는 친구들이 코드를 읽고 실제로 사용해주기를 바란다. 그래야 잠재 보그들이 발견될거니까.

이 부분의 설계와 일부 api 문서들은 wiki 에 보충해넣었다. ( https://github.com/cloudwu/skynet/wiki/Socket ) (중문)

Written in November 13, 2014
Translated in December 24, 2015

원문링크: http://blog.codingnow.com/2014/11/skynet_ae_udp_oeoe.html

Skynet 설계 설명

작자: cloudwu

번역: coolspeed

한달이란 시간에 걸쳐 드디어 skynet (https://github.com/cloudwu/skynet) 의 C 버전을 완성했다. 중간에 여러 모듈을 반복하여 리팩토링 한 결과 최종 남은 코드는 얼마 되지 않았다: 겨우 C 코드 6천여줄, 그리고 Lua 코드 천여줄이었다. 비록 일부 코드는 좀 급하게 짰지만 나 자신의 퀄리티 요구에 부합된다. 버그는 피면하기 어렵겠지만 이정도 작은 규모의 코드베이스는 충분히 명료하여 수정하기가 어렵지 않을 것이다.

Github 에 올린 이 프로젝트를 개발하는데 사용된 시간은 사실 한달도 훨씬 안된다. 나의 대부분 시간은 지난 반년 넘게 짜놓은 Erlang 버전 프레임워크와의 하위호환성과 호환 안되는 Erlang 모듈들을 포팅하는데 허비했다. 이 부분 코드들은 저희의 실 게임프로젝트와 관련 있는 것이어서 오픈하지 않았다. 또한 양이 이보다 몇배나 되는 관련코드들을 왕창 올려봤자 이 오픈소스프로젝트의 의미에 도움이 되지 않을 것이다. 프로젝트에 관심을 가지는 친구들은 그런 별로 중요하지도 않고, 많은 인터페이스들이 레거시 부담으로 추하게 설계된 구조 때문에 오히려 미궁에 빠질 것이다.

저희의 구 프로젝트와 연동시켜 보고 포팅도 다 잘 됐음이 확인된 후에 나는 또 skynet 의 일부 밑층 설계를 수정했다. 안전한 포팅을 보장하는 전제하에서 최대한 개선을 함으로써 히스토리컬 레거시를 최소한으로 걺어지도록 힘썼다. 이런 수정들은 쉽지 않았지만 의미가 있다고 생각한다. 최근 내가 자세하게 생각을 굴린 성과들이다. 오늘의 이 블로그글에서 나는 이번 픽스된 버전을 토대로 설계 설명을 좀 기록하려고 한다. 추후 찾아보기에도 좋게.


Skynet 코어는 어떤 문제를 해결하는가

나는 우리의 게임 서버가 ( 하지만 skynet 이 게임서버에만 사용될 수 있는게 아니다) 충분히 멀티코어의 파워를 이용할 수 있기를 바라며 서로 다른 비즈니스 로직들을 서로 독립적인 실행환경속에서 협동적으로 작동시키기를 원한다. 최초에 나는 이런 실행 환경이 OS 의 프로세스이기를 바랬다. 하지만 후에 발견한건데 만약 우리가 임베디드 언어를 필연적으로 사용하게 된다면, 예를 들어서 Lua, 독립적인 OS 프로세스의 의미가 크지 않을 것이다. Lua State 는 이미 충분히 양호한 샌드박스 환경을 마련해주어 서로 다른 실행환경을 격리해줄 수 있었다. 뿐만아니라 멀티스레딩 방식은 상태 공유를 통해 데이터 교환을 더욱 효율적으로 할 수 있게 해준다. 그러면서 멀티스레딩이 흔히 비평받는 단점들, 이를테면 복잡한 스레드 락, 스레드 스케쥴링 등은 밑단의 규모를 가능껏 제한하고 설계를 최대한 간소화함으로써 최소한의 범위안으로 가두어둘 수 있다. 이런 입장에서 skynet 은 최종적으로 3000 줄 안되는 C 코드로 코어층을 구현했는데 이것은 코딩 좀만 할줄 아는 C 프로그래머라면 빠른 기간내에 이해하고 유지보수 할 수 있는 규모이다.

핵심기능차원에서 skynet 은 오직 한가지 문제만 해결한다:

하나의 규약에 부합되는 C 모듈을 다이나믹 라이브러리 ( so 파일) 로부터 가동시켜 영원히 겹치지 않는 (모듈이 꺼지더라도) 하나의 숫자 id 에 바인딩하여 handle 로 사용하는 것이다. 모듈은 서비스 (Service) 라 불리고 서비스들은 서로 자유롭게 메세지를 보낼 수 있다. 모듈은 skynet 프레임워크에 하나의 callback함수를 등록하여 그것으로 자기에게 보내온 메세지를 받는다. 모듈들은 모두 하나하나의 메세지에 의해 드리븐 되고 메세지가 오지 않을 때에는 펜딩되여있으며 CPU 자원 소모가 0이도록 보장된다. 자발 실행적인 로직이 필요하다면 skynet 시스템에서 제공하는 timeout 메세지를 이용하여 일정 시간마다 트리그 시킬 수 있다.

Continue reading