Skip to content
isdnetworks
Go back

센티널은 코드로 강제한다

감시 기능에 재알림 억제를 추가한 변경을 독립적으로 재검토했다. 코드를 손수 재현하며 따라가다가 하나가 걸렸다.

Table of contents

Open Table of contents

같은 키스페이스의 두 종류

상태 저장 테이블에 행이 두 종류였는데 변화 감지용은 채널과 식별자를 이어 키로 쓰고 억제용은 식별자 자리에 예약어를 넣는다. 같은 키스페이스를 공유하는 구조다.

식별자 목록에 그 예약어가 들어가면 두 행의 키가 완전히 같아져 충돌한다. 코드에는 실제 목록의 식별자에 이 이름을 쓰지 않음이 보장돼 있어 충돌하지 않는다는 주석이 있었고 강제하는 코드는 없었다.

충돌했을 때의 경로

실제로 충돌하면 어떻게 되는지를 읽기와 쓰기 양쪽에서 따라갔다. 읽을 때는 키가 예약어로 끝나는지로 분류하므로 변화 감지용 행이 억제 행으로 오분류된다.

억제 행 형식에는 마지막 정상 시각 필드가 없으므로 뒤의 손상 행 가드에 걸려 조용히 버려지고 변화 감지 이력이 로그도 없이 소실된다. 쓸 때는 그 멤버의 변화 감지 데이터를 억제 정보로 덮어쓴다.

예외가 아닌 침묵

이 결함의 성질이 나쁜 지점이 여기다. 읽기 실패는 조용한 스킵이 되고 쓰기 충돌은 덮어쓰기 성공이 되므로 어느 쪽에서도 예외가 나지 않는다.

손상 행을 조용히 스킵하는 가드가 함께 있으면 충돌이 예외가 아니라 침묵으로 나타난다. 그 가드 자체는 형식이 깨진 행 때문에 전체가 죽지 않게 하는 좋은 장치인데 이 경우에는 문제를 가린다.

단언 한 줄의 차단

해결은 간단했다. 목록을 로드하거나 콜드 스타트할 때 식별자가 예약어와 같은지를 단언으로 확인하면 된다.

RESERVED = '__realert__'
for member in registry:
    assert member.identifier != RESERVED, \
        f'identifier 에 예약어 {RESERVED} 를 쓸 수 없다'

그런 식별자가 추가되는 순간 바로 터지고 배포도 안 되므로 결함 클래스가 원천 차단된다. 여기서 규칙이 나왔는데 실데이터 키스페이스에 센티널 키를 섞을 때는 주석이 아니라 코드로 예약어를 강제한다는 것이다. 주석은 읽는 사람에게만 말하고 목록에 값을 추가하는 사람이 그 주석을 읽을 이유가 없다.

잠재와 현재의 구분

발견하고 나서 현재 목록의 식별자를 전부 확인했더니 어느 것도 예약어와 겹치지 않았다. 지금은 안전하고 잠재적 결함이라는 뜻이다.

이것을 밝히는 것이 중요했는데 지금 터지고 있다는 것과 터질 수 있다는 것은 대응 우선순위가 다르기 때문이다. 발견 경위도 남겨 뒀다. 코드를 읽기만 한 것이 아니라 입력을 가정하고 실행하듯 따라간 것이 조건이었고, 읽기만 하면 주석에 보장돼 있다는 데서 넘어가지만 따라가면 그 보장이 코드에 없다는 것을 알게 된다.

정리


Share this post on:

Previous Post
네 번 따로 적힌 같은 결함
Next Post
매핑표를 먼저 완성한다