0

我已经完成了这项工作,但是对于十几个左右的文件来说,它的速度非常慢(到了超时的程度)。

它从 Dropbox 中获取目录列表,并将其与表的内容进行比较。我想对此进行优化,使其尽可能快速有效地运行。我知道每次查询都不是最佳的,但我认为主要的延迟是在Photo.create方法期间,因为它将文件从保管箱文件夹复制到 Amazon S3(通过carrierwave gem)。我正在考虑花时间进行操作,以了解延迟的来源。对于包含 10 个文件的文件夹,加载页面需要一分钟多的时间。奇怪的是,即使它跳过这些文件也需要这么长时间,因为它们已经存在,这对我来说毫无意义。

这是我的控制器代码:

def sync
    photo_size = 1024
    @event = Event.find(params[:id])

    @client = Dropbox::API::Client.new(:token  => 'derp', :secret => 'herp')
    @dropbox_files = @client.ls "images/#{@event.keyword}/#{photo_size}/"

    @existing_photos = @event.photos.all
    @data = []

    # TODO: need to make it not add files multiple times


    @dropbox_files.each do |f|


      photo_exists = Photo.where(:dropbox_path => f.direct_url.url).count
      if photo_exists == 0
        @photo = Photo.create(:remote_filename_url => f.direct_url.url, 
                              :dropbox_path => f.direct_url.url,
                              :event_id => @event.id)
        @data << "Added: #{f.direct_url.url.split('/').last}"
      else
        @data << "Skipped: #{f.direct_url.url.split('/').last}"
      end
    end
  end

理想情况下,我想将每个Photo.create调用分成一个异步请求,但这可能是一个完整的“没什么”。现在,如果它可以处理从 100 张照片中添加 5 张照片而不会超时的东西,我会很高兴。

做这个的最好方式是什么?我是一名 PHP 程序员,刚接触 RoR3。请帮忙。谢谢!

注意:目前,这会输出到屏幕,但最终它将成为后台操作。

4

2 回答 2

1

我有几件事你可以试试。我不熟悉 Dropbox API,但您应该能够弄清楚:

存储上次同步的日期,并仅检索新的或更改的文件。

将您的方法提取sync到一个新类中 - 控制器可能不是此职责的最佳选择。这是一个如何做到这一点的示例:

class EventSync
  attr_reader :event

  def initialize(event_or_id)
    @event = Event.find(event_or_id)
  end

  def sync
    dropbox_files.each do |f|
      process_file(f)
    end
  end

  private
    def photo_size
      1024
    end

    def process_file(file)
      event.photos.where(dropbox_path: file.direct_url.url).first_or_create do |file|
        file.remote_filename_url = file.direct_url.url
      end 
    end

    def client
      @client ||= Dropbox::API::Client.new(:token  => 'derp', :secret => 'herp')
    end

    def dropbox_files
      @dropbox_files ||= client.ls "images/#{event.keyword}/#{photo_size}/"
    end

end

这将像这样使用:EventSync.new(params[:event_id]).sync.

通过将其拆分为许多较小的方法,基准测试将更容易(您可以单独测试每种方法),这意味着您将能够更好地确定减速的位置。

于 2012-11-24T21:40:39.177 回答
0

在我尝试 Zach 的方法之前,这就是我现在的工作方式。

在控制器中:

  def syncall
    #TODO: Refactor sync and syncall
    photo_size = 1024
    @event = Event.find(params[:id])

    new_image_dir = "images/#{@event.keyword}/#{photo_size}/"
    @client = Dropbox::API::Client.new(:token  => 'uuzpqar2m5839eo', :secret => 'nr9tmx0vc8qh892')
    @dropbox_files = @client.ls new_image_dir
    start = Time.now  

    existing_photos = @event.photos.all
    @data = []
    photo_list = []

    existing_photos.each do |ep|
      filename = URI.unescape(ep.dropbox_path.split('/').last) #dropbox_path is url encoded...
      photo_list << filename
    end
    @data << photo_list

    skipped_files = 0

    @dropbox_files.each do |f|
      sql_start = Time.now
      db_filename = f.path.split('/').last

      if photo_list.include? db_filename 
        skipped_files += 1
      else
        pc_start = Time.now
        if db_filename.split('.').last == 'jpg'
          db_path = f.direct_url.url
          @photo = Photo.create(:remote_filename_url => db_path, 
                                :dropbox_path => db_path,
                                :event_id => @event.id)
          @data << "#{db_filename} added in #{Time.now - pc_start} seconds"
        else
          @data << "#{db_filename} was skipped in #{Time.now - pc_start} seconds"
        end
      end
    end    
    @data << "Total Time: #{Time.now - start} (#{skipped_files} skipped.)"
  end

这样,如果没有要添加的文件,则只有一个查询运行。另一个问题是direct_url.url调用非常繁重,因为每次调用它都会连接到保管箱。

每张跳过的照片从大约 2 秒变为 0.01 秒,每张上传的照片从 5-7 秒变为 2-4 秒。我还是更喜欢 Zach 的方法,所以我现在要试试。

于 2012-11-27T20:26:04.473 回答