503 Carrierwave를 사용하여 S3에 업로드 할 때 느려짐
백그라운드 작업에서 Rails 및 Carrierwave를 사용하여 많은 작은 파일을 S3에 업로드하고 있으며 S3 속도 제한에 도달하고 있습니다. 내 즉각적인 생각은 sleep 0.1
각 업로드 전에 a를 넣는 것이지만 좋은 해결책은 아닌 것 같습니다.
S3 API 및 일부 유형의 백 오프를 통해이 문제를 처리하는 방법에 대한 제안이 있습니까?
업로드를 수행하는 루비 코드,이 메서드는 루프에서 수천 번 호출됩니다.
def attach_audio(object:, audio_field:, attachment:)
return true if Rails.env.test?
language_code, voice_id = language_and_voice(object)
resp = polly.synthesize_speech(
output_format: 'mp3',
voice_id: voice_id,
text: audio_field.to_s,
language_code: language_code
)
audio_filename = "#{object.class.to_s.downcase}_#{attachment}_#{object.id}_#{voice_id}.mp3"
audio_path = "#{Rails.root}/db/audio/#{audio_filename}"
IO.copy_stream(resp.audio_stream, audio_path)
object.send(attachment + '=', Pathname.new(audio_path).open)
object.save!
end
업 로더 클래스
class AudioUploader < BaseUploader
def store_dir
"uploads/audio/#{model.target_language}/#{self.class.to_s.underscore}/#{model.class.to_s.underscore}/#{mounted_as}/#{model.id}"
end
def extension_whitelist
%w[mp3]
end
end
class BaseUploader < CarrierWave::Uploader::Base
if Rails.env.test?
storage :file
else
storage :fog
end
def store_dir
"uploads/#{self.class.to_s.underscore}/#{model.class.to_s.underscore}/#{mounted_as}/#{model.id}"
end
end
AWS의 응답
Message
Excon::Error::ServiceUnavailable: Expected(200) <=> Actual(503 Service Unavailable) excon.error.response :body => "<Error><Code>SlowDown</Code><Message>Please reduce your request rate.</Message><RequestId>176C22715A856A29</RequestId><HostId>L/+
Traceback
Excon::Error::ServiceUnavailable: Expected(200) <=> Actual(503 Service Unavailable)
excon.error.response
:body => "<Error><Code>SlowDown</Code><Message>Please reduce your request rate.</Message><RequestId>176C22715A856A29</RequestId><HostId>xxxxxxxxxxxxxxxxxxxxxxxxx</HostId></Error>"
:cookies => [
]
:headers => {
"Connection" => "close"
"Content-Type" => "application/xml"
"Date" => "Wed, 18 Nov 2020 07:31:29 GMT"
"Server" => "AmazonS3"
"x-amz-id-2" => "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
"x-amz-request-id" => "176C22715A856A29"
}
:host => "example-production.s3-eu-west-1.amazonaws.com"
:local_address => "xxx.xx.xxx.xxx"
:local_port => 50276
:path => "/uploads/audio/fr/audio_uploader/word/audio_file/8015423/word_audio_file_8015423_Mathieu.mp3"
:port => 443
:reason_phrase => "Slow Down"
:remote_ip => "xx.xxx.xx.x"
:status => 503
:status_line => "HTTP/1.1 503 Slow Down\r\n"
File "/app/vendor/bundle/ruby/2.6.0/gems/excon-0.71.1/lib/excon/middlewares/expects.rb", line 13, in response_call
File "/app/vendor/bundle/ruby/2.6.0/gems/excon-0.71.1/lib/excon/middlewares/response_parser.rb", line 12, in response_call
File "/app/vendor/bundle/ruby/2.6.0/gems/excon-0.71.1/lib/excon/connection.rb", line 448, in response
File "/app/vendor/bundle/ruby/2.6.0/gems/excon-0.71.1/lib/excon/connection.rb", line 279, in request
File "/app/vendor/bundle/ruby/2.6.0/gems/fog-xml-0.1.3/lib/fog/xml/sax_parser_connection.rb", line 35, in request
etc
편집하다
링크 된 AWS 설명서는 문제를 해결하는 것처럼 보이는 접두사를 참조합니다.
Amazon S3는 높은 요청 속도로 자동 확장됩니다. 예를 들어 애플리케이션은 버킷의 접두사 당 초당 최소 3,500 개의 PUT / COPY / POST / DELETE 또는 5,500 개의 GET / HEAD 요청을 달성 할 수 있습니다. 버킷의 접두사 수에는 제한이 없습니다. 읽기를 병렬화하여 읽기 또는 쓰기 성능을 높일 수 있습니다. 예를 들어 Amazon S3 버킷에 10 개의 접두사를 생성하여 읽기를 병렬화하는 경우 읽기 성능을 초당 55,000 개의 읽기 요청으로 확장 할 수 있습니다.
그러나 Carrierwave의 맥락에서 구현하는 방법을 이해하지 못합니다.
답변
에서 여기
예를 들어 애플리케이션은 버킷의 접두사 당 초당 최소 3,500 개의 PUT / COPY / POST / DELETE 또는 5,500 개의 GET / HEAD 요청을 달성 할 수 있습니다.
당신은 당신의 한계가 무엇인지 배웁니다. 이제 접두사 가 무엇인지 이해해야하며 쉽습니다. 이걸 고려하세요:
/uploads/audio/fr/audio_uploader/word/audio_file/8015423/word_audio_file_8015423_Mathieu.mp3
여기서 접두사 는 무엇입니까 ? 대답:
/ uploads / audio / fr / audio_uploader / word / audio_file / 8015423
접두사는 개체 이름을 제외한 모든입니다. 따라서 문제에 대한 답은 각 접두사 에 대해 Amazon에서 정의한 제한을 초과하지 않도록 계획을 설계 할 수있는 능력에 있습니다 .
예를 들어 0에서 99까지의 회전 카운터 를 사용 하고 저장되는 객체와 저장된 회전 카운터 지점 사이의 관계를 어딘가에 저장할 수 있습니다 [나중에 읽을 수 있도록]. 이것을 구현한다면 문제는 현재의 100 분의 1로 줄어들 것입니다. 실제로 100까지 끝까지 갈 필요는 없으며 필요할 경우 언제든지 늘릴 수 있습니다. 이제 다음과 같습니다.
/uploads/audio/fr/audio_uploader/word/audio_file/8015423/word_audio_file_8015423_Mathieu.mp3
다음 위치에 저장됩니다.
/ uploads / audio / fr / audio_uploader / word / audio_file / 00 / 8015423 / word_audio_file_8015423_Mathieu.mp3
다음 항목은 ... / 01 / ... 등등에, 100 번째 객체는 ... / 99 / ...에 저장되고 101 번째 객체는 ... / 00 / 에 다시 저장됩니다 . .. [분명히 두 문자를 사용할 필요는 없습니다].
이 프로세스가 로직에 가져 오는 추가 단계는 검색을 위해 word_audio_file_8015423_Mathieu.mp3 가 ... / 00 / ...에 있고 예를 들어 word_audio_file_8015424_Mark.mp3 이 ... / 01 /에 있음을 알아야한다는 것 입니다 . .. 등등. 즉, 객체와 객체가 저장된 지점 간의 관계를 저장해야합니다. 반면에 원하는 물체를 찾는 모든 지점을 검색하는 것이 허용되는 경우에는 그렇게 할 필요조차 없습니다.
나는 이것이 당신의 문제를 해결할 것이라고 강력하게 생각합니다.
ActiveJob없이 Sidekiq을 사용하는 경우 sidekiq-throttled gem 및 임계 값 옵션을 사용하여 백그라운드 작업에서 업로드 속도를 늦출 수 있습니다.
예:
class UploadWorker
include Sidekiq::Worker
include Sidekiq::Throttled::Worker
sidekiq_options :queue => :uploads
sidekiq_throttle({
# Allow maximum 1K jobs being processed within one second window.
:threshold => { :limit => 1_000, :period => 1.second }
})
def perform
# do your thing
end
end
에 따르면 AWS 워드 프로세서 앞에 일명 키 접두사는 당신이 양동이에 같은 디렉토리 아래에 유사한 데이터를 저장할 수있는 디렉토리 이름과 비슷합니다. 업로드를 그룹화하는 방법을 찾아야합니다. 귀하의 경우 object.id
에는 이름으로 값 에서 추가 디렉토리를 만들 수 있습니다 .
나는 사용해 보았다 https://github.com/nickelser/activejob-traffic_control, 그러나 작업이 제대로 작동하도록 할 수 없습니다.
결국 작동하는 매우 간단한 솔루션을 찾았습니다. 각 단어에 대해 S3의 오디오 생성 및 저장을 새로운 ActiveJob 클래스로 옮겼습니다. 그런 다음 수천 번 호출했으며 Sidekiq 동시성 설정에 의해 자동으로 제한됩니다.
config / sidekiq.yml
---
:concurrency: 10
:max_retries: 3
:queues:
- [urgent, 4]
- [nlp, 3]
- [default, 2]
- [low]