8

I'm using CarrierWave (0.9.0), Fog (1.14.0) and S3 to store user avatars. It seems to be taking a long time to determine the avatar URL for a given user. Subsequent calls have a greatly reduced time.

config/initializers/fog.rb

CarrierWave.configure do |config|
  config.fog_credentials = {
    provider: 'AWS',
    aws_access_key_id: ENV['AWS_ACCESS_KEY_ID'],
    aws_secret_access_key: ENV['AWS_SECRET_ACCESS_KEY']
  }
  config.fog_directory = ENV['AWS_S3_BUCKET_AVATAR']
  config.fog_public = false
end

user.rb

mount_uploader :avatar, ImageUploader

Console

irb(main):002:0> Benchmark.measure { user.avatar_url }
=>   0.500000   0.020000   0.520000 (  0.537884)

irb(main):003:0> Benchmark.measure { user.avatar_url }
=>   0.000000   0.000000   0.000000 (  0.001830)

irb(main):004:0> Benchmark.measure { user.avatar_url }
=>   0.000000   0.000000   0.000000 (  0.001454)

irb(main):005:0> Benchmark.measure { user.avatar_url }
=>   0.000000   0.000000   0.000000 (  0.000998)

New Relic reports that sometimes user.avatar_url takes up to 1 second. What could be causing this to be so slow? There's a discussion on slow URL generation for public files, but my avatars are not public.


Update 1:

Explicitly requiring Fog and CarrierWave before the first call makes no difference, as false is returned, indicating that they're already loaded.

irb(main):002:0> require 'carrierwave'
=> false
irb(main):003:0> require 'fog'
=> false
irb(main):004:0> Benchmark.measure { user.avatar_url }
=>   0.510000   0.030000   0.540000 (  1.627774)

Update 2:

Here's the uploader:

# encoding: utf-8

class ImageUploader < CarrierWave::Uploader::Base

  include CarrierWave::RMagick

  storage :fog

  def store_dir
    "uploads/#{model.class.to_s.underscore}/#{mounted_as}/#{model.id}"
  end

  version :s_32 do
   process resize_to_fill: [32, 32]
  end

  version :s_40 do
   process resize_to_fill: [40, 40]
  end

  version :s_50 do
   process resize_to_fill: [50, 50]
  end

  version :s_115_120 do
   process resize_to_fill: [115, 120]
  end

  version :s_128 do
   process resize_to_fill: [128, 128]
  end

  def extension_white_list
    %w(jpg jpeg gif png)
  end

end

Update 3:

user.avatar.url doesn't seem to make a difference:

irb(main):003:0> Benchmark.measure { user.avatar.url }
=>   0.500000   0.030000   0.530000 (  0.926975)
4

2 回答 2

4

我认为雾需求可能仍然存在问题(尽管现在不太明显)。由于雾中有很多不同的东西,我们很久以前就做出了选择,将许多依赖项推迟到需要它们时加载。这有加速“雾”的好处,但可能会在某些事情第一次发生时减慢速度。不知道我是怎么忘记这部分的,但是在我的机器上做一些小的基准测试时,考虑到这一点,我当然可以看到速度变慢。

为了解决这个问题,您可以将上面相关的要求基准更改为:

require 'benchmark'
require 'fog'
Fog::Storage.new(
  provider: 'AWS',
  aws_access_key_id: ENV['AWS_ACCESS_KEY_ID'],
  aws_secret_access_key: ENV['AWS_SECRET_ACCESS_KEY']
)
Benchmark.measure { ... }

您不使用该连接可能看起来有点奇怪,但我将其设置为延迟加载 S3 细节,直到您这样做(这样您就不必加载 S3 细节才能使用EC2)。但是,通过在较早的时间初始化连接,您可以避免这种开销。希望这至少能让你更接近你想去的地方。

于 2013-07-31T15:06:00.393 回答
2

我花了一些时间在这上面。虽然我还没有找出根本原因,但这里有一些发现。你可以从我所做的开始,希望你不需要太担心这个。

  • 根据你说的和我的实验。这种缓慢只有在您第一次调用user.avatar_url时才会发生。如果你在第一次调用后调用user.avatar_urlanother_user.avatar_url会很快。基本上只有一个请求受到这个“问题”的影响,所以我认为这不是一个大问题。
  • 第一次调用和后续调用有什么区别?我建议您使用ruby​​-prof来检查差异。我不是专家,但从报告中您会看到第一次运行加载的内容比第二次运行更多。ruby-prof 提供了一些其他可能对您有帮助的报告,尤其是树报告。

  • 我认为 CarrierWave/Fog 可能需要访问 S3 以确保 url 确实存在。这已通过使用不正确的 S3 存储桶排除。

上面的报告是由下面的代码生成的。

user = User.first
RubyProf.start
user.avatar_url
results = RubyProf.stop

File.open "/tmp/profile-graph1.txt", 'w' do |file|
  RubyProf::FlatPrinter.new(results).print(file)
end

RubyProf.start
user.avatar_url
results = RubyProf.stop

File.open "/tmp/profile-graph2.txt", 'w' do |file|
  RubyProf::FlatPrinter.new(results).print(file)
end
于 2013-08-10T08:05:47.883 回答