Uber에서 만든 Long-Running business logic을 처리하기 위한 asynchronous workflow frame work이다.
AWS에 Simple Workflow에서 감을 얻었다고 한다. (실제 동작하는 방식과 쓰이는 용어도 거의 유사하다)
Casandra로 동작하는 polling기반의 asyncjob이라고 표현할 수 있다.
2.1 WorkFlow
Workflow라는 개념이 익숙치 않을 수 있는데, 커다랗고 긴 작업, Transactional 하게 처리되어지길 원하는 일들을 처리하는 것을 이야기한다.
Uber의 예로는 UberEats에 대한 이야기를 하였는데, 예를 들어 Uber Eats에서 order가 들어오게되면 여러 과정을 거치게된다.
주문, 픽업시간 계산, 배달시간계산, 배달알림, 결제 등..
이런 과정들은 하나의 Workflow의 예제가 될 수 있고, 중요한것은 이러한 여러 작업 사이사이에 취소 혹은 여러 이유로 다양한 분기가 될 수 있다는 점이다.
이러한 환경에서 하나의 order를 종료하기까지의 다양한 로직을 어떻게 gracefull 하게 개발자의 손에서 아름답게(?) 마무리시킬 수 있을까?를 고민해 나온 적합 솔루션이 아닐까 싶다.
uber eats.. 하나의 workflow로 동작하고 싶은 경우다.
2.2 Activity
workflow 내에 여러 activity가 존재한다.
activity는 실제 실행되어야할 동작들에 대한 함수 형태로 존재한다.
activity는 기본적으로 asynchronous하게 동작한다.
activity는 workflow의 몇 가지 기능을 통해 group단위로 sync하게 동작 될 수 있으며, cancel, retry등이 다양한 작업들이 가능하도록 기능적 함수를 꽤나 제공해준다.
workflow는 결국 이런 activity들을 어떻게 관리하고 clean up해줄지 결정하는 가장 부모의 control-plane 이다.
물론 이런 workflow역시 cancel, retry등의 기능적 작업들을 지원해준다.
2.3 Feature
Cadence는 몇 가지 멋찐 컨셉들을 제공해 준다.
Fault-Oblivious Stateful Workflow Code
Cadence는 상태저장 워크플로우이며 프로세스나 케이던스 서비스 오류에 영향을 전혀(?)받지 않고 독립적으로 동작할 수 있다. 재해(?)에 강하다(문서에강조해서 자랑한다..)
ChildWorkflow
workflow하위에 여러 workflow를 실행시킬 수 있다.
Retries
workflow를 retry policy에 의해 자동으로 retry시켜준다.
그외 다양한 기능들..
특정 작업에 wrokflow id로 cancel혹은 signal을 명시적으로 보내준다던가.
workflow로 동작함으로 gc등의 역할을 할수 있는 clean code를 등록할 수 있다던가.
CronJob을 등록할 수 있다던가.
workflow가 관리되기에 query를 통해 특정 workflow의 state를 조회한다던가의 기능들이 가능하게 된다.
2.4 Async job..?
Asynchronous job을 잘 처리하기에는 여러 고민거리들이 항상 존재한다.
job을 처리하는 환경은 어떻게 잘 구성될것인가. (충분한scalable,distributed, durability 등의 문제를 해결해 줄 수 있는가)
job을 처리할때 필연적으로 발생되는 clean up과정들이 잘 처리될수 있는가. (cancel 요청이 들어온다던가. 중간에 error가 발생했을때의 rollback처리 등을 아름답게 처리해 줄 수 있는것인가)
job의 다양한 형태들을 제공해주는가? (request-response/fire-forget)
job을 어떻게 tracking할 것인가? status는 어디에 어떠한 방식으로 저장되는가.
…
위와 같은 여러 고민들이 녹아져 Cadence가 만들어진 것 같고, 모두 만족할 수 는 없지만 몇 가지 항목들을 잘 처리해 놓은것으로 보인다. 특히 stateful workflow란 개념이 있는 이상 clean-up할 수 있는 여러 요소들을 잘 컨트롤할 수 있도록 해준다는 이야기가 될 수 있다.
docker 폴더로 이동하여 docker-compose up 을 통해 cadence를 띄운다.
띄울때 주의깊게 보아야할 설정은 /config/development.yaml 파일이다.
cadence environment로 들어가게되는데 해당 위치에 cadence와 cadence-client가 통신할 rpc-name이 들어가 있다. client에서 cadence와 통신할때 해당 name으로 통신하지 않을경우 동작하지 않는다.
기본적으로 cadence-frontend로 설정되어있다.
cadence,cassandra,cadence-web도커가 뜬다.
localhost:8088을 통해 cadence web을 볼 수 있다. workflow마다 어떠한 과정으로 job이 끝나게되는 지 tracking할 수 있게된다.
Run example
Example Code를 실행시켜 보며 어떻게 동작하는지 확인해보자.
Download & Run
cadence sample 을 다운받는다.
README를 잘 읽어본다.
친절하게 설명되어있다. 최초에 make file로 각각의 example에 대한 binary를 만들어준다.
bin/binary를 가지고 worker를 띄우고 trigger로 직접 요청을 쏴보면 된다.
Details
workflow가 먼저 worker와 실행과 등록된다. 이때 worker는 tasklistname인 GC 를 가진 상태서 실행된다. 그리고 trigger에서 같은 GC 로 job을 쏘게 하면 해당 workflow가 실행된다.
workflow의 동작 소스 자체는 결국 worker에서 등록과 workflow실행(trigger)에 참조 되기때문에 두 곳에서 모두 필요함으로 후에 어느 곳에 존재해야되는지에 대한 설계가 중요하다.
위의 예제처럼 동작하는 상세 과정은 아래와 같다.
workflow,activity register
workflow를 작성한다. 물론 activity도 같이 기술될것이다.
example에는 다양한 workflow, activity를 가지고 있어서 위에 언급한 retry, cancel, signal등의 다양한 예제가 존재한다.
해당 worfklow를 cadence에 Register한다.
activity를 작성할 때는 여러 옵션들에 의해 제어 될 수 있다.
ScheduleToStartTimeout, HeartbeatTimeout,RetryPolicy 등이 그것이다.
run worker (worker)
위에 등록한 workflow를 실행시켜주는 worker를 띄어둔다.
worker가 등록될때 TaskList라는 값을 가지고 등록된다.
생성된 worker는 해당 TaskList만을 바라본다. 즉 workflow가 어디에선가 실행되어질때 실행되는 주최에서 TaskList를 가지고 실행되는데 해당 TaskList를 보고 이것을 바라보는 worker가 일을 하게 된다는 이야기다.
start worflow (trigger)
worker가 준비됬으니 이제 workflow를 시작 해주면된다.(job을 밀어넣어준다.)
위에서 언급한대로 workflow가 start될때 TaskList를 가지게 된다. 그러므로 해당 TaskList를 가지는 worker에서 해당 workflow를 시행할것이다.
웹화면은 아래와 같다.(cadence-web에서 가져옴..)
workflow 트래킹이 가능하다, 아 물론 activity도..
4.마치며
cadence는 asyncjob의 필수불가결한것을 해결해주는 마법사이다.
예전에 서비스를 만들때 상점 결제 로직이 들어가며 다양한 분기에 따라 다양한 clean up처리를 위해 지저분하게 코드가 들어간적이 있었는데 그때의 고통(안문잘문)이 불현듯 스쳐 지나갔다.
그때는 이런것이 있을 줄도 몰랐고, 그냥 내코드가 동작하는 것에만 신경썼다. 그래서 위의 과정들이 비정상(?)적으로 들어가있었다(즉 버그가 많았다.)
무언가를 찾아보고 비교하는 습관은 개발자에게 필수적인 자세인듯 같다.
모든 일이 그렇듯 많은 일들을 제공해주지만 과연 온전히 이 친구를 믿어서 쓸 수 있을지는 모르겠다. 어떤 규모에서 어떠한 형태로 얼마나 많은 데이터를 처리해 줄 수 있는지에 대한 성능 측정과 고민이 필요해보인다.