Rails 8 이전에는 includes, preload, eager_load와 같은 기법을 사용하더라도 연관 관계가 순차적으로 로딩되어 성능 병목 현상이 발생했습니다. 예를 들어, User.includes(:posts, :comments, :profile)는 사용자 쿼리 후 게시물, 댓글, 프로필 쿼리를 순서대로 실행하여 동시성을 제한했습니다.
load_async는 Ruby의 동시성 기능을 활용하여 이 문제를 해결합니다. users.load_async(:posts), users.load_async(:comments), users.load_async(:profile)와 같이 호출하면 세 가지 연관 관계 쿼리가 병렬로 실행되어 데이터 로딩 시간을 크게 단축합니다.
load_async 설정
load_async를 설정하려면 다음 단계를 따릅니다.
-
Rails 8로 업그레이드:
Gemfile에gem 'rails', '~> 8.0'추가. -
데이터베이스 연결 구성:
config/database.yml에서pool크기를 늘려 동시 쿼리 처리 용량 확보. -
애플리케이션 동시성 구성:
config/application.rb에config.active_record.async_query_executor = :global_thread_pool및config.active_record.async_query_executor_concurrency = 5설정.
실용적인 예시
실용적인 예시를 통해 load_async의 활용법을 살펴볼 수 있습니다.
-
기본 연관 관계 로딩: 컨트롤러에서
@users.load_async(:posts, :comments)를 호출하여 뷰에서 게시물과 댓글 데이터를 비동기적으로 로드합니다. -
복잡한 다단계 로딩: 대시보드 컨트롤러에서
current_user.load_async(:posts, :comments, :followers, :following)와 같이 여러 중첩된 연관 관계를 병렬로 로드하고, 통계 계산도 스레드를 활용하여 동시에 처리할 수 있습니다. -
배치 처리:
ReportGenerator클래스에서users.load_async(:posts, :orders, :preferences, :notifications)를 사용하여 여러 사용자의 연관 데이터를 일괄적으로 비동기 로드하여 보고서 생성 성능을 향상시킵니다.
load_async 적용 시 성능은 벤치마크 결과에서 명확히 드러납니다. 50개 사용자의 연관 관계를 로딩할 때, 순차적 방식은 약 450ms가 소요되었지만, load_async를 사용한 병렬 방식은 약 180ms로 약 60% 더 빠른 성능을 보였습니다.
중요 고려사항
그러나 load_async 사용 시 몇 가지 중요한 고려사항이 있습니다.
-
데이터베이스 연결 풀:
config/environments/production.rb에서async_query_executor의pool크기를 데이터베이스 용량에 맞게 조정해야 합니다. -
오류 처리:
ActiveRecord::QueryCanceled와 같은 비동기 쿼리 실패에 대비하여rescue블록을 통해 폴백 전략을 구현해야 합니다. -
메모리 사용량: 동시 로딩은 메모리 압력을 증가시킬 수 있으므로, 프로세스의 메모리 사용량을 지속적으로 모니터링하는 것이 중요합니다.
모범 사례
load_async의 모범 사례는 다음과 같습니다.
-
신중한 사용: 실제로 액세스할 연관 관계에만
load_async를 적용하여 불필요한 리소스 낭비를 피합니다. -
다른 최적화와 결합:
includes와 같은 기존 방식을 자주 사용되는 연관 관계에 적용하고,load_async를 덜 자주 사용되는 연관 관계에 결합하여 효율성을 높입니다. -
모니터링 및 측정: 비동기 로딩 시간과 활성 쿼리 수를 모니터링하여 성능을 지속적으로 측정하고 최적화합니다.
load_async는 RSpec을 통해 쉽게 테스트할 수 있으며, 동시 로딩의 성능과 오류 처리 시나리오를 검증하는 데 활용됩니다.