503 Carrierwave를 사용하여 S3에 업로드 할 때 느려짐

Nov 18 2020

백그라운드 작업에서 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 JamesMead Nov 24 2020 at 07:52

에서 여기

예를 들어 애플리케이션은 버킷의 접두사 당 초당 최소 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 /에 있음을 알아야한다는 입니다 . .. 등등. 즉, 객체와 객체가 저장된 지점 간의 관계를 저장해야합니다. 반면에 원하는 물체를 찾는 모든 지점을 검색하는 것이 허용되는 경우에는 그렇게 할 필요조차 없습니다.

나는 이것이 당신의 문제를 해결할 것이라고 강력하게 생각합니다.

1 HubertJakubiak Nov 21 2020 at 03:44

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
HubertJakubiak Nov 23 2020 at 05:13

에 따르면 AWS 워드 프로세서 앞에 일명 키 접두사는 당신이 양동이에 같은 디렉토리 아래에 유사한 데이터를 저장할 수있는 디렉토리 이름과 비슷합니다. 업로드를 그룹화하는 방법을 찾아야합니다. 귀하의 경우 object.id에는 이름으로 값 에서 추가 디렉토리를 만들 수 있습니다 .

ardochhigh Nov 26 2020 at 00:39

나는 사용해 보았다 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]