운영계에 올리니 API가 먹통이 되었다(DB가 고성능이어도 문제가 발생할 수 있다)

dasd412·2024년 12월 27일
0

실무 문제 해결

목록 보기
14/17

상황

로컬, 개발계 환경에서 잘 작동하던 API가 운영계에서 잘 작동하지 않는 문제가 발생했다.

모바일 프론트로 확인했을 때, 운영계 엔드포인트만 잡혀있는 버전의 경우 API 호출에서 하얀 화면이 노출되었다. 해당 APImutation으로, 서버에게 데이터 생성을 요청하는 기능을 가졌다.

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% 정확한 원인은 찾지 못했다. 하지만 추정되는 원인은 다음과 같다.

  1. DB의 이원화
  2. DB의 성능
  3. A에서 여러 개의 B로 데이터를 생성할 때 소요되는 시간이 1초 이상

일단 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의 리스트를 반환한다.


교훈

  1. Create하는 APIresponse는 반드시 그 API의 호출로 생성된 데이터여야 한다.
  2. 로컬, 개발계 된다고 운영계에도 잘 될거란 보장이 없다. 배포할 때마다 항상 확인이 필요하다.

profile
시스템 아키텍쳐 설계에 관심이 많은 백엔드 개발자입니다. (Go/Python/MSA/graphql/Spring)

0개의 댓글