동기(Synchronous)와 비동기(Asynchronous)[feat: Meilisearch]

게으른 개발자·2025년 1월 18일
post-thumbnail

현재 재직중인 회사에서 Meilisearch라는 오픈 소스를 사용하여 프로그램을 개발하고 서비스를 하고 있는데, 이번에 동기와 비동기의 차이로 인한 버그가 발견되어 이번 글을 작성하게 되었다.

Meilisearch란?

  • 최근들어 ES(Elasticsearch)를 무서운 기세로 쫓아오고 있는 RESTful 검색 API이다.
  • 최종 사용자에게 빠르고 관련성 있는 검색 경험을 원하는 모든 사람을 위한 바로 사용할 수 있는 솔루션을 목표로 한다.

특징

  • 매우 빠르다
    • 50밀리초 이내에 답변
  • 다양한 형태로 기능 제공이 가능하다
    • Cloud, Server 등
    • 다양한 프레임워크 또는 언어와 쉽게 연결을 할 수 있도록 별도의 라이브러리를 제공한다.
      • 라이브러리에서 제공하는 API는 대부분이 비동기로 진행된다.
  • 다중 검색
    • 단일 HTTP 요청으로 여러 인덱스에 대한 여러 검색 쿼리 수행이 가능
  • 문서 조회시, 다양한 옵션 설정
    • 인덱스별 중지어, 동의어, 오타 허용 범위 등의 설정이 가능

구조

  • Index
  • Document
  • Primary Key

동기(Synchronous)

  • 작업이 순차적으로 진행되는 것을 의미
  • 하나의 스레드가 작업중이면, 해당 작업이 완료될 때까지 다른 작업은 기다려야 된다.
  • 데이터베이스 트랜잭션, 파일 입출력 등의 기능을 처리하는데 유용

예제

package sync;

public class SyncSampleMain {

    public static void main(String[] args) {
        System.out.println("작업 1 시작");
        performTask();
        System.out.println("작업 1 종료");
    }

    private static void performTask() {
        try {
            Thread.sleep(3000); // 3초 대기 (블로킹)
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("작업 2 완료");
    }

}

결과
작업 1 시작
(3초 슬립)
작업 2 완료
작업 1 종료

비동기(Asynchronous)

  • 작업이 독립적으로 실행
  • 기존에 실행중이던 작업의 완료 여부와 상관없이 다른 작업도 실행 될 수 있다.
  • 네트워크 요청이나 UI 이벤트 처리 등의 기능을 처리하는데 유용

예제

package async;

import java.util.concurrent.CompletableFuture;

public class AsyncSampleMain {
    public static void main(String[] args) {
        System.out.println("작업 1 시작");

        CompletableFuture<Void> future = CompletableFuture.runAsync(() -> {
            performTask();
        });

        System.out.println("작업 1 종료");

        // 비동기 작업이 완료되기를 대기
        future.join();
    }

    private static void performTask() {
        try {
            Thread.sleep(3000); // 3초 대기
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("작업 2 완료");
    }
}

결과
작업 1 시작
(3초 슬립)
작업 1 종료
작업 2 완료

이슈

  • 현재 Meilisearch 공식 사이트에서 제공하는 라이브러리의 API들을 이용하면 대부분이 비동기로 진행되어 작업의 완료 여부와 상관없이 다음 작업을 진행하게 된다.
  • 위와 같은 동작으로 인해 Meilisearch를 사용하는 기능 중 순차적으로 진행되어야 할 작업들이 비동기로 수행되어 고객사의 네트워크 속도 차이에 따른 버그가 발생
  • 이해를 위해 아래 그림을 참고 (단순 예시)

1. 회원가입 요청
2. 회원가입의 완료 여부를 체크하지 않고 바로 로그인을 요청
3. 회원가입이 완료되지 않은 상태에서 로그인을 시도하여 오류가 발생

해결방법

1. 클라이언트에서 고정적인 시간을 대기 후 다음 작업 수행

  • 가장 단순한 방법이면서 제일 하지 말아야 될 방법
  • 고객의 네트워크 속도를 예측할수가 없어 공통적으로 사용할 대기 시간을 측정할수가 없다.

2. Meilisearch와 직접 통신하는 서버에서 비동기 프로세스의 완료 여부를 체크(현재 적용)

  • API의 요청 결과로 TaskInfo라는 객체를 응답받는데 해당 객체의 항목중 status의 값으로 구분하여 작업의 완료 여부를 체크
  • status를 최대 10초 체크하는데 최근 10초를 넘어서 응답받은 고객사가 있어 수정이 필요
	public static void validMeiliTask(Client client, int taskUid) {
        try {
            long sleepTime = 10L;
            long totalSleepTime = 0L;
            long maxSleepTime = 10000L; // 10 seconds
            Task task = client.getTask(taskUid);
            while (!task.getStatus().equals("succeeded")) {
                Thread.sleep(sleepTime);
                task = client.getTask(taskUid);
                totalSleepTime += sleepTime;

                if (totalSleepTime >= maxSleepTime) {
                    log.warn("timeout. taskUid:{}, taskStatus: {}", task.getIndexUid(), task.getStatus());
                    break;
                }
            }
        } catch (MeilisearchException e) {
            log.warn("meiliSearch Error: {}", e.getMessage());
        } catch (InterruptedException e) {
            log.warn("meiliSearch Task Sleep Error: {}", e.getMessage());
        }
    }

정리

이번 개발이 나에게있어 가장 큰 고비였다.... 기존에 이미 개발을 다 해둔 상태라 모든 프로세스가 다 비동기로만 돌아가도록 되어있었고 전혀 다른 역할을 하는 서비스 여러개가 하나의 서비스로 운영되고 있어서.... 파악하는데도 엄청 오랜 시간이 걸렸다ㅜㅜ 그래서 대략적인 파악 후, 팀장님께 서비스 분리 요청을 드리고 서비스를 분리하는 작업을 진행하고 있다.

분리하는 작업을 해오면서 대략적으로만 파악했던 기능들에 대해서도 자세하게 알게되었고 메인 기능중 내가 모르던 코드들에 대해서도 알게되어서 시간가는줄 모르고 하고는 있다!! 하지만, 양이 너무 많다보니 이걸 어느 세월에 다 끝낼지 모르겠다ㅜㅜ(그때까지 내가 이 회사에 있을까....??)

아직 끝나진 않았지만 이번 기회에 오픈 소스를 사용하는 방법과 연동을 위해서는 어떤식으로 접근하는지에 대해서도 배워서 이 기회를 놓치지 않고 꾸준하게 공부하면서 새로운 지식 또는 기술을 습득하는 과정이 되었으면 한다.

출처

profile
6년차 백엔드 엔지니어로 일하고 있습니다~!

0개의 댓글