새 권한을 가진 파트너가 Open API 를 호출했는데 그 자리에서 500 이 났다. 요청 형식을 바꿔 주는 LegacyIdMiddleware 가 있는데 그것이 동작하지 않고 있었다.
Table of contents
Open Table of contents
원인 — 세 갈래 중 마지막 분기
미들웨어 코드를 열어 보니 요청을 가르는 분기가 셋 있었다. 이미 신규 product_id 면 그대로 넘기고 신규 형식의 자식이면 parent_id 로 바꿔서 넘기고 있었다.
그리고 두 조건에 다 안 걸리면 아무 변환도 없이 그대로 넘기고 있었다. 문제가 된 상품은 마이그레이션 이전 원본이라 두 조건 어디에도 해당하지 않았다.
주의 — 사양이 아니라 빠뜨림
변환 없이 넘어간 요청이 구형 LegacyProductController 까지 도달했고 결국 거기서 오류가 났다. 미들웨어의 마지막 줄은 통과시키겠다는 결정처럼 보이지만 실제로는 그 입력을 생각하지 않은 결과였다.
조건 분기의 마지막 갈래는 명시적 결정과 고려하지 않음이 같은 코드로 보이므로 둘을 구분할 방법이 코드 안에 없다. 어떤 입력이 그 갈래로 오는지를 세어 봐야 갈린다.
검증 — 로그 이름의 유일한 매핑처
이전 원본을 신규 마스터로 바꾸려면 매핑이 필요한데 products 에는 없었다. product_migration_logs 에 그 연결이 들어 있었다.
테이블 이름이 로그라서 기록용으로 보고 처음에는 그냥 지나쳤다. 실제로는 그 연결을 담고 있는 유일한 자리였고 이름이 성질을 알려 주지 않는 경우였다.
조치 — 미들웨어에 조회를 넣기 전에
두 가지 방법이 있었는데 매핑을 products 에 컬럼으로 복사해 두는 것과 미들웨어에서 매 요청마다 product_migration_logs 를 조회하는 것이다. 뒤쪽을 골랐다. 이유는 컬럼을 추가하면 마이그레이션이 한 번 더 필요하고 이 매핑은 읽기만 해서다. 대신 모든 요청에 조회가 하나 붙는 것을 감수했고 그래서 인덱스부터 확인했다. legacy_id 에 인덱스가 걸려 있었고 Cardinality 도 충분히 넓은 편이었다.
모든 요청이 지나가는 자리에 조회를 넣을 때는 그 조회의 비용이 전체 처리량을 정한다. 인덱스를 확인하고 나서 설계를 확정했다.
배경 — 들어올 때 바꾸고 나갈 때 되돌리기
식별자를 바꿔 넣기만 하면 응답이 파트너가 보낸 값과 달라져서 상대가 못 알아본다. 그래서 원본 값을 $request->attributes 에 숨겨 두고 응답 단계에서 되돌렸다.
그러면 안쪽 처리는 신규 형식만 알면 되고 변환은 경계에서만 일어나게 된다. 옵션은 식별자 매핑이 없어서 값 조합으로 맞췄는데 완벽하지 않은 방법이므로 왜 이것으로 충분한지를 함께 적었다.
정리
- 미들웨어의 마지막 갈래가 사양이 아니라 빠뜨림일 수 있다
- 명시적 결정과 고려하지 않음이 같은 코드로 보인다
- 어떤 입력이 그 갈래로 오는지 세어 봐야 갈린다
- 조건에 안 걸린 입력은 그냥 통과해 뒤에서 터진다
- 매핑이 로그라는 이름의 테이블에만 있을 수 있다
- 모든 요청이 지나는 자리에 조회를 넣을 때는 인덱스부터 본다
- 들어올 때 바꾸고 나갈 때 되돌려 안쪽을 단순하게 둔다
- 완벽하지 않은 매칭은 왜 충분한지를 함께 적는다