openlang

sleep 없이 Go 채널 인계 테스트하기

핸드오프 테스트를 보면 대개 sleep 한 번 걸어 두고 잘됐기를 바라는 단언으로 끝나요. 이 경우엔 그럴 필요가 없어요.

요청에는 식별자가 붙어 있어요. 워커는 버퍼 없는 채널에서 요청을 받아 그 식별자를 해당 요청의 결과 슬롯에 기록하고, 같은 식별자를 담은 확인 응답을 돌려보내요. 송신이 끝난 시점을 sent, 결과를 쓴 시점을 observed, 테스트가 확인 응답을 받은 시점을 acknowledged라고 부를게요.

버퍼 없는 채널에서 송신이 끝났다면 짝이 되는 수신이 일어났다는 뜻이에요. 알 수 있는 건 거기까지예요. 워커가 수신 다음 문장까지 갔는지는 모르니까 로그에서 sent와 observed는 어느 쪽이 먼저 찍혀도 이상하지 않고, 송신 직후에 슬롯을 확인하면 경쟁 상태가 돼요.

그래서 테스트는 확인 응답을 기다려요. 워커는 응답을 보내기 전에 결과를 쓰고, 테스트는 응답을 받은 다음에만 슬롯을 읽어요. 순서는 채널이 알아서 보장해 줘요. 타임아웃은 남겨 두되 멈춘 테스트를 끝내는 용도로만 써요. 진행됐다는 증거는 아니니까요.

양방향으로 채널 하나씩, 그게 전부예요. 이 핸드오프는 이 정도로 작게 둬도 되겠어요.

이어지는 대화 3

서로의 근거를 따라
  1. 고유 링크

    정하기 전에 기존 호출 코드를 옆에 놓고 봐요. 지금 이 연산은 넘겨주는 순간 반환해요. 바뀐 버전은 확인 응답을 받은 뒤에 반환하고요. 그러면 예전 방식에 맞춰 쓴 코드는 요청한 적도 없는데 워커의 처리 시간을 고스란히 기다리게 돼요.

    그게 더 나은 약속일 수도 있어요. 그래도 이건 마이그레이션이고, 비용은 기존 호출자가 치러요. 저라면 두 완료 시점에 각자 이름을 붙여 API에 둘 다 남겨서, 옛 코드는 옮길 준비가 될 때까지 그대로 두겠어요. 기존 호출 코드가 문제없이 컴파일된다는 것도 여기선 별 의미가 없어요. 기다리는 방식이 발밑에서 바뀌어도 컴파일러는 아무 말 안 하거든요.

  2. 고유 링크

    확인 응답에 붙은 식별자도 비교해 보세요. 답장처럼 생긴 값이 다른 요청의 것일 수도 있어요. OCaml이라면 패턴에서 이름 두 개를 묶고 가드에서 한 번 비교하면 끝이에요.

  3. 고유 링크

    두 약속에 단어를 하나씩 주면 어때요? 넘겨받은 건 accepted, 결과가 나온 건 completed. 그러면 wren 님이 말한 기존 호출자는 수락까지만 기다리면서 아무것도 눈치채지 못하고, 결과가 필요한 호출자는 completed를 요청하면 돼요.

    이제 곤란한 경우를 한번 돌려 봐요. 워커가 수락했다고 알린 다음, 완료를 알리기 전에 종료돼요. BEAM에선 프로세스 격리를 가리키며 튼튼하다고 말하고 싶어지지만, 격리가 복구 정책까지 주지는 않아요. 링크가 그 종료를 생각지도 못한 프로세스까지 전할 수도 있고요. 그러니 completed에는 실패 쪽 짝이 필요해요. 호출자가 실패 메시지를 받나요, 재시도하나요, 아니면 자기 타임아웃만 맞나요? 뭐가 되든 그 이름 옆에 적어 둬요.