rubycdp / ferrum

Headless Chrome Ruby API
https://ferrum.rubycdp.com
MIT License
1.72k stars 123 forks source link

Ferrum script has process_timeout errors after upgrading to the Heroku Chrome-for-testing buildpack #490

Open kanejamison opened 1 week ago

kanejamison commented 1 week ago

Describe the bug

Our Ferrum script starts having process_timeout errors after upgrading to the Heroku/Chrome-for-testing buildpack. The old heroku/google-chrome buildpack is deprecated in favor of chrome-for-testing so we were testing this before moving to their new Heroku-24 stack.

I originally posted this as an issue in the chrome-for-testing buildpack repo since it was related to migrating the buildpack, but after confirming that which chrome and which chromedriver were finding the correct paths, it feels relevant to post it here instead.

image

To Reproduce

I switched over from heroku/google-chrome this week and followed the instructions on Migrating from Separate Buildpacks (though I used the Heroku app settings UI to remove and add the new buildpacks instead of CLI): image

Since then we have a cron job running screenshots using Ferrum that has started failing with the following error:

ERROR taking screenshot: Browser did not produce websocket url within 10 seconds, try to increase ":process_timeout". See https://github.com/rubycdp/ferrum#customization

Actions I've taken to try and resolve:

Relevant app settings:

Here's the script I'm running that is failing in case it's helpful. We don't have a specific Ferrum config outside of this call to Ferrum::Browser.new(). The final rescue StandardError => e is where my exception is getting tracked.

# frozen_string_literal: true

module ScreenshotCaptureable
  extend ActiveSupport::Concern

  included do
    BROWSER_WINDOW_SIZE = [1200, 900]

    def filename
      @filename ||= ["screenshot", self.class.name, id, Time.now.to_i].compact.map(&:to_s).map(&:parameterize).join("-")
    end

    def file_path
      @file_path ||= "public/screenshot-#{self.class.name}-#{self.id}.jpeg"
    end

    def save_screenshot_to_s3
      screenshot.attach({filename: "#{filename}.png", io: File.open(file_path), content_type: "image/jpeg"})
      self.update_columns(screenshot_updated_at: Time.current)
    end

    def capture_screenshot_via_ferrum(context)
      page = context.create_page
      url = "#{ENV['BASE_URL']}/l/#{special_screenshot_link&.uuid}/template?headless=1"
      Rails.logger.info "#{'*' * 50} #{url.inspect} #{'*' * 50}"

      page.go_to(url)
      page.mouse.scroll_to(10, 60)
      page.screenshot(path: file_path)
    end

    def self.update_next_batch_pending_screenshots
      if next_batch_for_screenshot.count == 0
        Rails.logger.info "No Templates to capture screenshots"
        return
      end

      begin
        next_batch_for_screenshot.each {|model| model.create_sharing_links unless model.readonly_sharing_link&.uuid }

        browser = Ferrum::Browser.new(slowmo: 6, window_size: BROWSER_WINDOW_SIZE, timeout: 15, process_timeout: 20)
        context = browser.contexts.create
        threads = []

        Rails.logger.info "Processing next #{next_batch_for_screenshot.count} Templates"

        next_batch_for_screenshot.each do |model|
          threads << Thread.new(context) do |c|
            begin
              model.capture_screenshot_via_ferrum(context)
              model.save_screenshot_to_s3
            rescue StandardError => e
              Rails.logger.error e.message
            end
          end
        end

        threads.each(&:join)
        context.dispose # dispose the context
        browser.quit # quit the browser instance
      rescue StandardError => e
        Rails.logger.error e.message
        Honeybadger.notify("ERROR taking screenshot: #{e.message}")
      end
    end
  end

end
maland commented 2 hours ago

setting the no-sandbox option solved the issue for me:

Ferrum::Browser.new(browser_options: { 'no-sandbox': nil })