Stefflan00
Stefflan00

Reputation: 155

ruby on rails 4 - how to boost performance

So my code in my overview.html.erb looks like this.

<h1>Socialapi Overview</h1>

<table class="table table-striped">
  <thead>
    <tr>
      <th>Name</th>
      <th>Facebook ID</th>
      <th>Facebook Likes</th>
      <th>Behance Follower</th>
      <th>Instagram Follower</th>
      <th>Pinterest Follower</th>
      <th>Twitter Follower</th>
      <th>Social Reach</th>

    </tr>
    </thead>
    <tbody>
      <% @artists.each do |artist| %>
      <tr>
        <td><%= artist.name %></td>
        <td><%= facebook_id(artist.facebook) %></td>
        <td><%= fb_likes = facebook_likes(artist.facebook) %></td>
        <td><%= behance(artist.behance) %></td>
        <td><%= instagram = instagram(artist.instagram) %></td>
        <td><%= pinterest = pinterest(artist.pinterest) %></td>
        <td><%= twitter = twitter(artist.twitter) %></td>
        <td><%= social_reach(fb_likes,instagram,twitter,pinterest) %></td>
      </tr>
      <% end %>
    </tbody>
  </table>

the Controller:

class SocialapiController < ApplicationController
  before_action :authenticate_admin!
  def overview
    @artists = Artist.all
  end

end

and my helpers:

module ApplicationHelper
  def social_reach(facebook_likes,instagram_follower,twitter_follower,pinterest_follower)

    social_reach = (facebook_likes.to_i*0.7)+(instagram_follower.to_i*0.05)+(twitter_follower.to_i*0.15)+(pinterest_follower.to_i*0.1)

   return social_reach

  end

  def facebook_id(facebook_name)
    graph = Koala::Facebook::API.new
    begin
      facebook_id = graph.get_object(facebook_name)["id"]
    rescue Exception => exc
      logger.error("Message for the log file #{exc.message}")
      facebook_id = "fb_name is not correct"
    end
    return facebook_id
  end



  def facebook_likes(facebook_name)
    graph = Koala::Facebook::API.new
    begin
      facebook_likes = graph.get_object(facebook_name)["likes"]
    rescue Exception => exc
      logger.error("Message for the log file #{exc.message}")
      facebook_id = "fb_name is not correct"
    end
    return facebook_likes
  end



  def instagram(instagram_name)
    instagram_client = Instagram.client(access_token: "#######")
    Instagram.configure do |config|
      config.client_id = "####"
      config.client_secret = "####"
      # For secured endpoints only
      #config.client_ips = '<Comma separated list of IPs>'
    end
    begin
      for user in instagram_client.user_search("#{instagram_name}")
       instagram_follower = instagram_client.user_followed_by(user.id).count
      end
    rescue Exception => exc
      logger.error("Message for log file #{exc.message}")
      instagram_follower = "instagram profile is private"
    end
    return instagram_follower
  end



  def twitter(twitter_name)
    twitter_client = Twitter::REST::Client.new do |config|
      config.consumer_key        = "###"
      config.consumer_secret     = "###"
      config.access_token        = "###"
      config.access_token_secret = "###"
    end
    begin
      twitter_follower = twitter_client.user(twitter_name).followers_count
    rescue Exception => exc
      logger.error("Message for the log file #{exc.message}")
      twitter_follower = "twitter name is not correct"
    end
    return twitter_follower
  end



  def behance(behance_name)
    behance_client = Behance::Client.new(access_token: "###")
    begin
      behance_follower = behance_client.user(behance_name)["stats"]["followers"]
    rescue Exception => exc
      logger.error("Message for the log file #{exc.message}")
      behance_follower = "behance name is not correct"
    end
    return behance_follower
  end



  def pinterest(pinterest_name)
    begin
      require 'open-uri'
      url = "http://www.pinterest.com/#{pinterest_name}/followers/"
      doc = Nokogiri::HTML(open(url))
      pinterest_follower = doc.css(".FollowerCount").text
      pinterest_follower ["Followers"] = ""
      if pinterest_follower.include? ","
          pinterest_follower [","] = ""
      end
    rescue Exception => exc
      logger.error("Message for log file #{exc.message}")
      pinterest_follower = "pinterest name is not correct"
    end
    return pinterest_follower
  end

end

This tool delivers some "live" data that I get with my application_helpers and with some gems.

First I want to know if this is the right way to make methods that I use in several controllers? I' new to RoR so please give me some advice if I did s.th. wrong.

Second ... now I want to boost my performance. I think multithreading could be one option, am I right?

So where exactly do I do this? in the controller and how does this work?

thanks, I thankful for any help.

best Stefan

Upvotes: 0

Views: 137

Answers (1)

tadman
tadman

Reputation: 211560

You've got the right idea here, helpers are a good place to put code that's shared amongst different views, but it's not exactly optimal. My biggest concern here is how much application code you have in your helper, a place that can be more difficult to test than if it were exposed another way, like as a Rails concern module instead.

You're going to have a lot of trouble getting this page to load in reasonable time with so many external dependencies. Each of those method calls is going to take an unpredictable amount of time to execute, where that time could be anything from zero seconds to two minutes. That's unacceptable.

Instead, queue up a job to retrieve that information and populate it in a database table of some sort. Postgres has a fast native JSON data type that is great for storing "whatever" type data. There's also a number of background-job solutions for Rails to delegate this to.

Here's an example method of yours that bucks the traditional Ruby style re-written to be more conventional:

require 'open-air'
# ^ Generally dependencies go at the top of the file, not buried inside methods.

def pinterest(pinterest_name)
  url = "http://www.pinterest.com/#{pinterest_name}/followers/"
  doc = Nokogiri::HTML(open(url))

  pinterest_follower = doc.css(".FollowerCount").text
  pinterest_follower["Followers"] = ""
  # ^ No space between variables and method calls like () or []

  pinterest_follower.gsub!(/,/, '')
  # ^ Rewritten as an in-place modification to remove the commas

rescue Exception => exc
  # ^ A rescue can exist at the same level as a method definition, avoiding
  #   the need for a begin/end block around it.
  logger.error("Message for log file #{exc.message}")

  pinterest_follower = "pinterest name is not correct"

ensure
  # ^ The ensure block is always run regardless of exceptions.

  pinterest_follower
  # ^ return is not necessary unless you're exiting the method early.
end

As for performance, the general pattern in Rails is to approach issues in the following priority:

  • Spin up more application server instances to handle more requests in parallel.
  • Reduce the number of queries being made to the database by using eager loading where necessary or optimal. Use includes on your models when loading them.
  • Optimize those queries you can't avoid making by fetching only the data you need, and side-step loading the model if that's not required by using pluck.
  • Tune your database server to perform faster by analyzing the queries it's running and adjusting system and database configuration parameters.
  • Upgrade your database hardware if that's an option, either by re-sizing to a bigger VPS, by switching from HDD to SSD, or by adding more memory.
  • Use caching to avoid repeating expensive queries or making slow network operations.
  • Split your database across multiple replicas or shards to even out the load.

Update: Expanded on Michal's observations.

Upvotes: 1

Related Questions