Skip to content
isdnetworks
Go back

표본에 섞인 삭제 표시

(수정됨)

모바일 앱 푸시 토큰 상태를 보려고 device_tokens 에서 ORDER BY RAND() LIMIT 100 으로 백 건을 뽑아 유효 여부를 셌다. 절반 넘게 무효라고 나왔는데 실제 푸시 발송은 멀쩡했다.

Table of contents

Open Table of contents

증상 — 무작위 표본이 낸 값

표본의 값과 실제 관측이 크게 어긋나면 표본 쪽을 먼저 의심하는 것이 순서다. 모집단에 무엇이 들어 있는지를 deleted_at 유무별로 세어 봤다.

삭제 표시가 붙은 행이 살아 있는 행의 두 배였다. 지운 토큰은 당연히 무효이므로 무작위로 뽑으면 그쪽이 대부분 잡힌다.

검증 — 지운 것을 뺀 뒤의 값

deleted_at IS NULL 을 붙인 모집단에서 다시 백 건을 뽑으니 대부분 유효로 나왔다. 실제 발송 상태와 맞는 값이었다.

같은 쿼리에 조건 하나가 있고 없고가 결론을 뒤집었다. 표본의 문제는 뽑는 방법이 아니라 무엇에서 뽑느냐에 있었다.

원인 — 화면을 통하면 안 보이는 조건

DeviceTokenRepository 를 보니 기본 조회에 deleted_at IS NULL 이 들어가 있었다. 화면을 통해 보면 그 조건이 항상 붙으므로 지운 행이 있다는 사실 자체가 드러나지 않는다.

내가 손으로 친 쿼리에만 그 조건이 없었다. 직접 조회할 때 빠지는 조건은 화면에서 늘 붙어 있던 것이라 빠뜨렸다는 자각도 없다.

표본이 이상해서 본 것이 다른 것을 알려 줬다

지운 행이 두 배가 넘는 것이 이상해서 deleted_at 날짜로 묶어 봤다. 특정 하루에 사천 건이 한꺼번에 지워져 있었고 리브랜딩 앱으로 갈아타면서 옛 앱 토큰을 정리한 정상 작업이었다.

그 사천 건 중 대부분이 구십 일을 넘긴 상태라 device_tokens_archive 로 옮기고 본 테이블에서 뺐다. 표본이 이상해서 들여다본 것이 데이터 정리 대상까지 알려 준 셈이다.

조치 — 조건이 빠질 자리를 없애기

두 가지 방법이 있었다. 손 조회 절차서에 deleted_at IS NULL 을 붙이라고 적는 것과, 살아 있는 행만 보는 뷰 v_device_tokens_active 를 만들어 손 조회는 그것을 보게 하는 것이다. 뒤쪽을 골랐다. 이유는 절차서는 읽는 사람만 지키고 뷰는 이름을 고르는 순간 조건이 따라오기 때문이다. 대신 지운 것까지 봐야 할 때는 본 테이블을 직접 봐야 하는 것을 감수했고 그 경우 의도가 드러난다.

코드에서도 findAll() 에 조건이 붙게 하고 전체를 보는 것은 findAllIncludingDeleted() 로 이름으로 구분되게 했다. 표본을 뽑기 전에 전체 건수와 섞인 것과 대표성을 확인하는 목록도 적었는데 시험용 기기가 test_ 이름 규칙으로만 구분되고 있어서 그것도 is_test 값으로 바꿔 조회에서 걸러지게 했다.

정리


Share this post on:

Previous Post
한쪽만 막으면 다른 쪽으로 샌다
Next Post
스스로 정할 범위의 경계