ReactiveCaching
- 이 문서는
reactive_caching.rb
을 참조합니다.
ReactiveCaching
관심사는 백그라운드에서 일부 데이터를 가져와 Rails 캐시에 저장하여 요청되는 동안 최신 상태로 유지합니다. 만약 데이터가 reactive_cache_lifetime
동안 요청되지 않는다면 새로 고쳐지지 않고 제거됩니다.
예제
class Foo < ApplicationRecord
include ReactiveCaching
after_save :clear_reactive_cache!
def calculate_reactive_cache(param1, param2)
# 여기서 비용이 많이든 작업이 수행됩니다. 이 메서드의 반환 값이 캐시됩니다.
end
def result
# `with_reactive_cache`에 임의의 인수를 전달할 수 있습니다. `calculate_reactive_cache`는 동일한 인수로 호출됩니다.
with_reactive_cache(param1, param2) do |data|
# ...
end
end
end
이 예제에서 #result
가 호출되는 첫 번째 시간에는 nil
을 반환합니다. 그러나 #calculate_reactive_cache
를 호출하고 초기 캐시 수명을 10분으로 설정하는 백그라운드 작업을 예약합니다.
작동 방식
처음 #with_reactive_cache
가 호출될 때 백그라운드 작업이 예약되고 with_reactive_cache
가 nil
을 반환합니다. 백그라운드 작업은 #calculate_reactive_cache
를 호출하고 그 반환 값을 저장합니다. 또한 reactive_cache_refresh_interval
후에 다시 백그라운드 작업을 예약합니다. 따라서 저장된 값을 최신 상태로 유지합니다. 계산은 절대로 동시에 실행되지 않습니다.
값이 캐시될 때 #with_reactive_cache
를 호출하면 제공된 블록을 호출하여 캐시된 값을 얻습니다. 또한 reactive_cache_lifetime
값으로 캐시의 수명을 연장합니다.
수명이 만료되면 더 이상 백그라운드 작업이 예약되지 않고 #with_reactive_cache
를 다시 호출하면 nil
을 반환하여 프로세스가 처음부터 다시 시작됩니다.
ReactiveCaching에 하드 제한 설정
성능을 유지하기 위해 ReactiveCaching
을 포함하는 클래스에 강한 캐싱 한계를 설정해야 합니다. 설정 방법 예를 참조하세요.
자세한 내용은 내부 이슈 Redis (또는 ReactiveCache) 소프트 및 하드 제한을 참조하세요.
사용 시기
- 외부 API에 요청을 보내야 하는 경우(예: k8s API로의 요청). 응용 프로그램 서버 워커가 외부 요청의 기간 동안 차단되는 것은 좋지 않습니다.
- 모델이 많은 데이터베이스 호출이나 다른 시간 소모형 계산을 수행해야 하는 경우.
사용 방법
모델 및 통합에서
ReactiveCaching
관심사는 모델뿐만 아니라 통합(app/models/integrations
)에서도 사용할 수 있습니다.
-
모델에 관심사를 포함합니다.
모델에 관심사를 포함하려면:
include ReactiveCaching
통합에 관심사를 포함하려면:
include Integrations::ReactivelyCached
- 모델이나 통합에서
calculate_reactive_cache
메서드를 구현합니다. - 모델이나 통합에서 캐시된 값이 필요한 경우
with_reactive_cache
를 호출합니다. -
reactive_cache_work_type
을 해당되는 대로 설정합니다.
컨트롤러에서
ReactiveCaching
을 사용하는 모델이나 서비스 메서드를 호출하는 컨트롤러 엔드포인트는 백그라운드 작업이 완료될 때까지 기다리지 않아야 합니다.
-
ReactiveCaching
을 사용하는 모델이나 서비스 메서드를 호출하는 API는 캐시가 계산되고 있는 경우(#with_reactive_cache
가nil
을 반환하는 경우)202 accepted
를 반환해야 합니다. - 또한 폴링 간격 헤더를 설정해야 합니다. (
Gitlab::PollingInterval.set_header
). - API의 사용자는 API를 폴링해야 합니다.
- 서버 부하를 줄이기 위해 ETag 캐싱을 구현하는 것도 고려해 볼 수 있습니다.
모델이나 서비스에 구현할 메서드
이러한 메서드는 ReactiveCaching
을 포함하는 모델/서비스에서 구현해야 하는 메서드입니다.
#calculate_reactive_cache
(필수)
- 이 메서드는 반드시 구현되어야 합니다. 반환 값이 캐시됩니다.
- 캐시를 채우어야 할 때
ReactiveCaching
에 의해 호출됩니다. -
with_reactive_cache
에 전달된 임의의 인수는calculate_reactive_cache
에도 전달됩니다.
#reactive_cache_updated
(선택 사항)
- 필요한 경우 이 메서드를 구현할 수 있습니다.
-
ReactiveCaching
관심사가 캐시를 업데이트할 때마다 호출됩니다. 새 캐시 값이 이전 캐시 값과 동일한 경우에는 호출되지 않습니다. 새 값이 캐시에 저장될 때만 호출됩니다. - 캐시가 업데이트될 때마다 작업을 수행할 수 있습니다.
모델이나 서비스에서 호출되는 메서드
이들은 ReactiveCaching
에서 제공되는 메서드로서 모델/서비스에서 호출되어야 합니다.
#with_reactive_cache
(필수)
-
with_reactive_cache
는calculate_reactive_cache
의 결과가 필요한 곳에서 호출되어야 합니다. -
with_reactive_cache
에는 블록을 제공할 수 있습니다.with_reactive_cache
는 또한 임의의 수의 인수를 받을 수 있습니다.with_reactive_cache
에 전달된 인수는calculate_reactive_cache
로 전달됩니다.with_reactive_cache
에 전달된 인수는 캐시 키 이름에 추가됩니다. - 이미 결과가 캐시되어 있는 상태에서
with_reactive_cache
가 호출된 경우, 블록이 호출되어 캐시된 값이 생성되며, 블록의 반환 값은with_reactive_cache
에 의해 반환됩니다. 또한 캐시의 타임아웃이reactive_cache_lifetime
값으로 재설정됩니다. - 아직 결과가 캐시되지 않은 경우
with_reactive_cache
는nil
을 반환합니다. 이는calculate_reactive_cache
를 호출하고 결과를 캐시하는 백그라운드 작업을 예약합니다. - 백그라운드 작업이 완료되고 결과가 캐시된 후, 다음
with_reactive_cache
호출은 캐시된 값을 사용합니다. -
아래 예시에서
data
는with_reactive_cache
에 주어진 블록에 전달된 캐시된 값입니다.class Foo < ApplicationRecord include ReactiveCaching def calculate_reactive_cache(param1, param2) # 여기서 비용이 많이든 작업이 수행됩니다. 이 메서드의 반환 값은 캐시됩니다 end def result with_reactive_cache(param1, param2) do |data| # ... end end end
#clear_reactive_cache!
(선택 사항)
- 이 메서드는 캐시가 만료/지워져야 하는 경우에 호출될 수 있습니다. 예를 들어, 모델의
after_save
콜백에서 모델이 수정된 후에 캐시가 지워지도록 호출될 수 있습니다. - 이 메서드는
with_reactive_cache
에 전달된 것과 같은 매개변수로 호출되어야 합니다. 왜냐하면 매개변수는 캐시 키의 일부이기 때문입니다.
#without_reactive_cache
(선택 사항)
- 이는 디버깅 목적으로 사용할 수 있는 편리한 메서드입니다.
- 이 메서드는 백그라운드 워커가 아닌 현재 프로세스에서
calculate_reactive_cache
를 호출합니다.
구성 가능한 옵션들
조정할 수 있는 class_attribute
옵션이 몇 가지 있습니다.
self.reactive_cache_key
- 이 속성의 값은
data
및alive
캐시 키 이름의 접두사입니다.with_reactive_cache
에 전달된 매개변수는 캐시 키 이름의 나머지를 형성합니다. -
기본적으로 이 키는 모델의 이름과 레코드의 ID를 사용합니다.
self.reactive_cache_key = -> (record) { [model_name.singular, record.id] }
-
data
캐시 키는"ExampleModel:1:arg1:arg2"
이고alive
캐시 키는"ExampleModel:1:arg1:arg2:alive"
입니다. 여기서ExampleModel
은 모델의 이름이고,1
은 레코드의 ID이며,arg1
와arg2
는with_reactive_cache
에 전달된 매개변수입니다. -
이 concern을 통합(
app/models/integrations/
)에 포함시키는 경우, 기본값을 재정의해야 합니다:self.reactive_cache_key = ->(integration) { [integration.class.model_name.singular, integration.project_id] }
만약
reactive_cache_key
가 위와 정확히 같다면, 기존의Integrations::ReactivelyCached
concern을 사용할 수 있습니다.
self.reactive_cache_lease_timeout
-
ReactiveCaching
은 캐시 계산이 여러 워커에 의해 동시에 실행되지 않도록Gitlab::ExclusiveLease
를 사용합니다. - 이 속성은
Gitlab::ExclusiveLease
의 타임아웃입니다. - 기본값은 2분이지만, 다른 타임아웃이 필요한 경우 재정의할 수 있습니다.
self.reactive_cache_lease_timeout = 2.minutes
self.reactive_cache_refresh_interval
- 이는 캐시가 새로 고쳐지는 간격입니다.
- 기본값은 1분입니다.
self.reactive_cache_refresh_interval = 1.minute
self.reactive_cache_lifetime
- 이는 캐시가 만료되는 기간입니다.
- 기본값은 10분입니다. 이 캐시 값에 대한 요청이 10분 동안 없으면 캐시가 만료됩니다.
- 캐시 값이 만료되기 전에 요청이 올 경우, 캐시의 타임아웃이
reactive_cache_lifetime
으로 재설정됩니다.
self.reactive_cache_lifetime = 10.minutes
self.reactive_cache_hard_limit
- 이것은
ReactiveCaching
이 캐시할 수 있는 최대 데이터 크기입니다. - 기본값은 1 메가바이트입니다. 이 값을 초과하는 데이터는 캐시되지 않으며, Sentry에서는
ReactiveCaching::ExceededReactiveCacheLimit
을 조용히 발생시킵니다.
self.reactive_cache_hard_limit = 5.megabytes
self.reactive_cache_work_type
- 이것은
calculate_reactive_cache
메서드에 의해 수행되는 작업의 유형입니다. 이 속성에 따라 올바른 워커를 선택하여 캐싱 작업을 처리할 수 있습니다. 작업이 외부 요청을 수행하는 경우 (예: Kubernetes, Sentry):external_dependency
로 설정하고, 그렇지 않은 경우:no_dependency
로 설정하세요.
self.reactive_cache_worker_finder
- 이것은 백그라운드 워커가
calculate_reactive_cache
를 호출할 수 있는 객체를 찾거나 생성하는 데 사용되는 메서드입니다. -
기본적으로 모델의 기본 키를 사용하여 객체를 찾습니다:
self.reactive_cache_worker_finder = ->(id, *_args) do find_by(primary_key => id) end
-
기본 동작은 사용자 정의
reactive_cache_worker_finder
를 정의하여 재정의할 수 있습니다.class Foo < ApplicationRecord include ReactiveCaching self.reactive_cache_worker_finder = ->(_id, *args) { from_cache(*args) } def self.from_cache(var1, var2) # 이 메서드는 백그라운드 워커에 의해 "bar1"과 "bar2"로 인수가 전달되어 호출됩니다. new(var1, var2) end def initialize(var1, var2) # ... end def calculate_reactive_cache(var1, var2) # 여기서 비용이 많이 드는 작업이 수행됩니다. 이 메서드의 반환 값이 캐시됩니다. end def result with_reactive_cache("bar1", "bar2") do |data| # ... end end end
- 이 예제에서, 기본 키 ID가
reactive_cache_worker_finder
에 전달되고with_reactive_cache
에 전달된 매개변수와 함께 전달됩니다. - 사용자 정의
reactive_cache_worker_finder
는with_reactive_cache
에 전달된 매개변수를 사용하여.from_cache
를 호출합니다.
- 이 예제에서, 기본 키 ID가