로컬, 개발계 환경에서 잘 작동하던 API가 운영계에서 잘 작동하지 않는 문제가 발생했다.
모바일 프론트로 확인했을 때, 운영계 엔드포인트만 잡혀있는 버전의 경우 API
호출에서 하얀 화면이 노출되었다. 해당 API
는 mutation
으로, 서버에게 데이터 생성을 요청하는 기능을 가졌다.
splitAIntoB
라는 이름의 API
였는데, A 데이터를 분리해 B 데이터를 여러 개 생성하는 API
였다. 스키마
로 보았을 때에도 A와 B는 일대다 관계를 갖는다.
모바일에서 문제를 확인하고나서,운영계에서 테스트 계정으로 백엔드 API
를 호출해보았다. 처음에는 문제가 없어보였다.
그런데, 다음처럼 mutation
결과에 edge
까지 쿼리해보았더니 문제를 알 수 있었다.
mutation{
splitAIntoB(~~~){
aField1
aField2
B{
bField1
bField2
}
}
}
위 코드에서 aField1,aField2에는 정상적인 결과가 나왔다. 하지만, B까지 쿼리했을 때, B의 결과가 []
이었다. 빈 배열이 나온 것이다.
로컬과 개발계에선 B의 결과가 정상적으로 나왔는데 운영계에서만 발생했다.
그리고 splitAIntoB
에서 나온 B는 splitBIntoC
라는 메서드의 파라미터가 되는 형식이다. 따라서 B에서 빈 배열이 나올 경우, 그 이후의 메서드에서 에러가 발생할 수 밖에 없다.
결론부터 말하자면, 100% 정확한 원인은 찾지 못했다. 하지만 추정되는 원인은 다음과 같다.
DB
의 이원화DB
의 성능일단 3.의 경우, 로컬이나 운영계나 개발계나 상관없이 소요되는 시간이다. LLM
을 일정 부분 사용하기 때문이다.
1.과 2.는 로컬,개발계/운영계 사이의 환경 차이 중 제일 명백한 차이였다. 로컬, 개발계는 DB
가 일원화되어 있다. 하지만, 운영계는 DB
의 이원화
가 적용되어 있었다. 그리고 로컬, 개발계의 DB
는 성능이 좋지 않지만, 운영계 DB
는 상당히 고성능의 DB
다.
100% 장담할 수는 없지만, DB
의 성능이 너무 좋아서 하위 데이터가 미처 생성되기도 전에 mutation
의 결과가 리턴된 것으로 보인다.
func (s * XXservice) SplitAIntoB(
aId int,
) (A, error) {
// A 조회
A, err := s.aRepository.Find(
aId,
)
if err != nil {
return nil, err
}
//A에서 B로 분리 및 생성 작업 (생략)
// B bulk 저장 및 A와 외래키 맺기
_, err = s.bRepository.CreateBulk(
inputs,
)
if err != nil {
return nil, err
}
return A, nil
}
문제가 됬던 코드를 보면, A에서 분리해 B를 대량 생성하는데 그 결과로 B의 리스트를 반환하지 않고 A를 반환하고 있었다.
func (s *XXservice) SplitAIntoB(
aId int,
) ([]B, error) {
// A 조회
A, err := s.aRepository.Find(
aId,
)
if err != nil {
return nil, err
}
//A에서 B로 분리 및 생성 작업 (생략)
// B bulk 저장 및 A와 외래키 맺기
return s.bRepository.CreateBulk(
inputs,
)
}
위 코드로 수정하였더니, 로컬,개발계, 운영계 모두 정상적인 결과가 나옴을 확인할 수 있었다.
A에서 분리해 B를 대량 생성하는 API에서 B의 리스트를 반환한다.
Create
하는 API
의 response
는 반드시 그 API
의 호출로 생성된 데이터여야 한다.