Scale & Performance

Understanding Performance

 

Application

 

 

Relational DB

 

 

API

 

 

Files

 

 

NoSQL DB

 

External

Dependencies

Faster

Slower

Cache

RAM

How to optimise for this?

  • Caching 
  • Avoid queries wherever possible

Big O Notation

  • A way of understanding complexity in your code 
  • The greater the complexity the slower your code

O(1) - Same speed, no matter how big the input size

def first_element(array)
  array.first
end

O(n) - Complexity gets bigger in proportion to the size of input

def print_elements(array)
  array.each do |element|
    # We have to run this line of
    # code array.size times
    puts element
  end
end

O(n²) - Complexity proportional to the size of the input squared

def matching_values(array)
  array.each do |element|
    array.each do |other_element|
      # We have to run this line of
      # code array.size * array.size times
      # so if the array has 5 elements,
      # we run it 25 times
      puts "array matches" if element == other_element
    end
  end
end

O(n) + 1 is the only one I've really needed

# 1 DB query
users = User.all

users.each do |user|
  # This will do another `users.length` DB queries
  # So if there are 50 users this will do 50 queries
  puts user.blogs
end

# If n = users.length
# Total of n + 1 queries

Solution to n plus one

# 1 DB query
users = User.all

# 1 DB query
blogs = Blog.where("user_id IN ?", users)

users.each do |user|
  # We still have to do this each time
  # But it's much quicker to do this in RAM
  # than go to the DB each time
  puts blogs.find { |blog| blog.user_id == user }
end

# Total of 2 queries regardless of size of users.length

Scaling

  • Vertical Scaling (bigger server)
  • Horizontal Scaling (add more servers)

Scaling means keeping the behavior of an application the same as load (broadly, the number of users) increases

Optimising

By Leo Allen

Optimising

  • 629