[throttled-restart.tcl] throttling 적용하여 파일변경시 자동으로 프로세스 재시작하기와 Tcl 취향고백
코딩을 하다보면, 파일변경시에 자동으로 빌드, 서버재시작, 혹은 테스트케이스를 실행 되도록 하면 꽤 편리할 수 있다.
커먼리습 같은 경우엔 바로 실행중인 코드가 변경되는 방식이어서 신경 쓸게 별로 없고, Rails 같은 웹프레임웍은 자동으로 재시작하거나 변경된 코드를 다시 로딩해주기도 하고, PHP 계열도 웹브라우저를 refresh하면 자동적으로 새 코드으로 실행하게 된다.
다른 컴파일이 필요한 언어나, 파이썬, 펄 같은 언어들, 혹은 자동으로 코드에 반영되는 언어/프레임웍을 사용하더라도, 원하는 태스크(예: 테스트케이스 실행) 같은걸 걸어 놓으려면 커맨드라인에서 지정해두는게 낫다.
문제 #1: 파일변경을 모니터링하기
-
대부분의 언어/프레임웍들은 이런 "파일변경 모니터링" 기능이 없다.
- ==> 다른 외부도구를 커맨드라인에서 연결해서 사용하면 될 것 같다.
시도 #1: fswatch으로 파일변경을 모니터링하고 지정한 커맨드 실행하기
- fswatch 커맨드라인 도구를 사용하기로 했다. (freebsd, linux 모두 지원)
일단 간단하게 시작: 모든 변경을 STDIN으로 전달 받음
|
|
시도 #2: throttling, Perl One-liner
-
(문제) 한 변경에 너무 많은 triggering 갯수
$HOME/w
디렉토리에 한 번에 여러 파일이 생성/삭제/수정되면 그게 한 라인씩 찍힘.- 한 뭉치 변경은 하나의 라인으로 뭉치면 좋겠다.
- 한 단위시간 동안 이미 트리거 되었다면, 단위시간내에 다시 발생한 이벤트는 트리거하지 않도록 무시하자.
- ==> throttling하자
- 조금 문제: 시작하자마,
$SECS
-이내에 발생한 이벤트는 무시된다. - 커맨드 실행은, 위의
xargs
부분 파이프연결을 더하면 된다.
문제 #2: pid-file, kill-switch이 필요해졌다
트리거링 이벤트/입력에 대해 스로틀링하고, 원하는 커맨드를 실행하는 것도 했지만, "재시작"을 위해서는 재시작 대상 프로세스가 좀 더 지원해줄 필요가 있었음.
- pid-file : 재시작을 위해 기존 프로세스 id (pid)을 파일에 기록해 두서 그걸 kill해야 하거나,
- kill-switch : (or) 재시작 대상이 되는 프로세스가 별도의 unix named pipe / socket file을 열고, 그 입력에 따라 스스로 gracefully shutdown 하는 지원이 필요.
기존 프로세스를 종료하려면 어떤 프로세스인지 알아야 할테니까.
(단순하게 pkill
등으로 프로세스 이름을 적당히 찍어서 그렇게 해도
되긴 하겠지만)
시도 #3: 그래서 짜버렸다 throttled-restart.tcl
https://github.com/ageldama/throttling-restart.tcl
tcl 8.6, tcllib 이용해서, 원하는 커맨드를 시작/재시작하고 자동으로 STDIN에 따라 종료/재시작하는 작은 유틸리티를 작성해봤다.
자동적으로 프로세스를 시작하고, pid을 관리/종료도 자동으로 해주니 재시작 대상 프로그램은 변경/확장될 필요가 전혀 없다.
후기: tcl
지금 생각해보니, subprocess을 꼭 분리된 thread 안에서 관리할 필요는 없었을거 같기도 하다. 처음 설계할 때엔 blocked 상태에 빠질까봐 분리를 했었지만.
tcl을 사용해서 뭔가를 진지하게 짜기 시작하니까 재밌었다:
-
[ GOOD ] event-loop, evented i/o이 기본이어서 좋았다.
- node.js, python의 asyncio 등처럼 콜백지옥도 별로 없고, 또 async/await 같은 function coloring 문제도 없었다.
- 적당히 golang의 i/o system처럼 비동기이지만 적당히 동기형처럼 짤 수 있어서 좋았다.
- 분명히 callback을 지정해서 이벤트를 처리하는데도 그렇게 콜백지옥이 되지 않아서 신기할 정도.
- 특히 tcl 입출력 모델은 정말 깔끔하고 편리한 것 같다.
-
[ GOOD ] 스레딩도 신선했다.
- Python, Ruby 등이 모두 GIL문제을1 우회하려 애써야 하거나 했던데 비해서,
- 단순하게 그냥 Tcl/C API 수준에서 스레드를 만들고 분리된 Tcl Interpreter을 만들어 서로 연결되도록 하는 방식이어서, 문제가 생길 여지가 훨씬 적었다.
-
Perl으로 짰었다면,
- perl / thread으로 좀 고민 했을거 같고, (어차피 결국 안썼겠지만)
- AnyEvent 모듈을 적절히 썼을거 같다.
-
[ BAD ] 여전히 Tcl 문법, expansion등에 대해서는 가끔 생소해진다.
- 현대에 익숙한 언어들(펄, C/C++, Go, Java, Python) 등와는 많이 다르기도 하고.
- 의외로 Tcl의 간단한 규칙2에서 파생되는 복잡해지는 상황이나,
- 내장된 String interpolation / Evaluation 규칙 때문에, Escaping이 필요한데, 그럴 방법이 잘 떠오르지 않을 때도 있었던거 같다.
-
[ BAD ] Closure이 바로 지원되지 않아서 거 좀…
- [ BEST ] 그런데 위 (4), (5)은 Tcl이라 생기는 이슈이고, 사실 그런 이슈 때문에 String을 기반으로 한 리습언어 같다, 거기에 바로 현재 쓸 수 있는, 그리고 리눅스/유닉스 시스템에 잘 녹아있는 것이라 좋은거 같다.
Footnotes
파이썬은 3.14 이후로 GIL을 끈 방식으로 실행할 수 있도록 했지만, 사용해보진 않아서.
https://learnxinyminutes.com/tcl/ 간단한 기본 문법규칙. 정말 다른 언어에 비해서 단순하긴 하고.