49
Memory Issues in Rails Applications

Memory Issues in Ruby on Rails Applications

Embed Size (px)

DESCRIPTION

Boston Ruby Meetup presentation by Joe Ferris, CTO of thoughtbot, and Simeon Simeonov, CTO of Swoop, on ways to optimize the memory footprint of data intensive Ruby on Rails applications.

Citation preview

Page 1: Memory Issues in Ruby on Rails Applications

Memory Issuesin Rails Applications

Page 2: Memory Issues in Ruby on Rails Applications

I am @simeons

Page 3: Memory Issues in Ruby on Rails Applications

recruit amazing people

solve hard problems !

ship !

make users happy !

repeat

Page 4: Memory Issues in Ruby on Rails Applications
Page 5: Memory Issues in Ruby on Rails Applications

Problems of Success (good problems)

Too many users Too much traffic Too much data

Page 6: Memory Issues in Ruby on Rails Applications

Memory Issuesin Rails Applications

Common Problem of Success

Page 7: Memory Issues in Ruby on Rails Applications
Page 8: Memory Issues in Ruby on Rails Applications

Display AdvertisingMakes the Web Suck

User-focused optimization Tens of millions of users

1000+% better than average 200+% better than Google

Swoop Fixes That

Page 9: Memory Issues in Ruby on Rails Applications

Mobile  SDKs  iOS  &  Android

Web  SDK  RequireJS  &  jQuery

Components  AngularJS

NLP,  etc.  Python

Targe<ng  High-­‐Perf  Java

Analy<cs  Ruby  2.0

Internal  Apps  Ruby  2.0  /  Rails  3

Pub  Portal  Ruby  2.0  /  Rails  3

Ad  Portal  Ruby  2.0  /  Rails  4

Page 10: Memory Issues in Ruby on Rails Applications

Before 1hr @ 4Gb

Page 11: Memory Issues in Ruby on Rails Applications

Before 1hr @ 4Gb

When problems grow faster than the rate at which you can throw HW at them, you actually have to solve them

Page 12: Memory Issues in Ruby on Rails Applications

Before 1hr @ 4Gb

After 5min @ 230Mb

Page 13: Memory Issues in Ruby on Rails Applications

Resolving Memory Issuesin Rails ApplicationsUsing Streams

Page 14: Memory Issues in Ruby on Rails Applications

CSV

Page 15: Memory Issues in Ruby on Rails Applications

0

125

250

375

500

0 25,000 50,000 75,000 100,000

Rows

Mem

ory

(Mb)

Page 16: Memory Issues in Ruby on Rails Applications

0

125

250

375

500

0 25,000 50,000 75,000 100,000

Rows

Mem

ory

(Mb)

You are here

Page 17: Memory Issues in Ruby on Rails Applications

0

125

250

375

500

0 25,000 50,000 75,000 100,000

Rows

Mem

ory

(Mb)

You are here

This sucks

Page 18: Memory Issues in Ruby on Rails Applications
Page 19: Memory Issues in Ruby on Rails Applications

0

125

250

375

500

0 25,000 50,000 75,000 100,000

Rows

Mem

ory

(Mb)

You are here

This sucks

Start thinking here

Page 20: Memory Issues in Ruby on Rails Applications

Memory Leaks

Page 21: Memory Issues in Ruby on Rails Applications

class AddDomainsStep def call(hashes) hashes.map do |hash| transform_and_return(hash) end end end

Page 22: Memory Issues in Ruby on Rails Applications

1 class AddDomainsStep 2 def initialize 3 @domain_config = DomainConfig.instance 4 end 5 6 def call(hashes) 7 hashes.each do |hash| 8 hash['domain'] = 9 @domain_config. 10 domain_for(hash['domain_id']) 11 end 12 end 13 end

Page 23: Memory Issues in Ruby on Rails Applications

1 class DomainConfig 2 include Singleton 3 4 def initialize 5 @domains = {} 6 end 7 8 def domain_for(id) 9 @domains[id] ||= Domain.name_for(id) || '' 10 end 11 end

Page 24: Memory Issues in Ruby on Rails Applications

@domains[id] ||= Domain.name_for(id) || ''

Page 25: Memory Issues in Ruby on Rails Applications
Page 26: Memory Issues in Ruby on Rails Applications

Memory Leak

•Memory that will never be released by the garbage collector.

•Memory usage grows the longer the process runs.

Page 27: Memory Issues in Ruby on Rails Applications

Avoid Global State

•Global variables

•Class variables

•Singletons

•Per-process instance state

Page 28: Memory Issues in Ruby on Rails Applications

Memory Churn

Page 29: Memory Issues in Ruby on Rails Applications

hashes.map do |hash| hash['domain'].downcase.strip end

hashes.each do |hash| hash['domain'].downcase! hash['domain'].strip! end

vs

Page 30: Memory Issues in Ruby on Rails Applications

Memory Churn

•Allocating and deallocating tons of objects slows down processing

•Mutation limits allocations, but makes it easier to introduce bugs

Page 31: Memory Issues in Ruby on Rails Applications

1 hashes.each do |hash| 2 hash['domain'].downcase! 3 hash['domain'].strip! 4 end

Spot the Bug!

Page 32: Memory Issues in Ruby on Rails Applications

# In shared state: @domains[id] ||= Domain.name_for(id) || '' !

# Much later: hash['domain'].downcase! hash['domain'].strip!

Page 33: Memory Issues in Ruby on Rails Applications

Good News!•Allocating and freeing objects is

fairly fast in Ruby •Keeping your stack frame light

will limit the effects of memory churn

Page 34: Memory Issues in Ruby on Rails Applications

Memory Bloat

Page 35: Memory Issues in Ruby on Rails Applications

def to_csv csv = [CSV.generate_line(headers)] !

rows.each do |row| values = headers.map do |header| row[header] || defaults[header] end !

csv << CSV.generate_line(values) end !

csv.join('') end

Page 36: Memory Issues in Ruby on Rails Applications

def to_csv csv = [CSV.generate_line(headers)] !

rows.each do |row| values = headers.map do |header| row[header] || defaults[header] end !

csv << CSV.generate_line(values) end !

csv.join('') end

Page 37: Memory Issues in Ruby on Rails Applications

def to_csv csv = [CSV.generate_line(headers)] !

rows.each do |row| values = headers.map do |header| row[header] || defaults[header] end !

csv << CSV.generate_line(values) end !

csv.join('') end

Page 38: Memory Issues in Ruby on Rails Applications

Memory Bloat

•Memory usage grows with data set

•Loading too much data at once

Page 39: Memory Issues in Ruby on Rails Applications

Laziness

Page 40: Memory Issues in Ruby on Rails Applications

rename_report_fields( squash( add_domains( add_properties( unwind_variations( rows ) ) ) ) )

Page 41: Memory Issues in Ruby on Rails Applications

def duplicate(number, count) if count > 0 [number] + repeat(number, count - 1) else [] end end !

def sum(list) list.inject(0) do |result, number| result + number end end

Page 42: Memory Issues in Ruby on Rails Applications

sum(repeat(5,10)) # => 50

Page 43: Memory Issues in Ruby on Rails Applications

duplicate :: Int -> Int -> [Int] duplicate number count | count <= 0 = [] | otherwise = number:duplicate number (count - 1) !sum :: [Int] -> Int sum [x] = x sum (x:remaining) = x + sum remaining

Page 44: Memory Issues in Ruby on Rails Applications

> sum $ duplicate 5 10 50

Page 45: Memory Issues in Ruby on Rails Applications

Be ProactiveAbout Being Lazy

Page 46: Memory Issues in Ruby on Rails Applications

Enumerable

Page 47: Memory Issues in Ruby on Rails Applications

class AddDomainsStep def initialize(source) @source = source end !

def each @source.each do |hash| hash['domain'] = DomainConfig. instance. domain_for(hash['domain_id']) yield hash end end end

Page 48: Memory Issues in Ruby on Rails Applications

RenameReportFieldsStep.new( SquashStep.new( AddDomainsStep.new( AddPropertiesStep.new( UnwindVariationsStep.new( rows ) ) ) ) )

Page 49: Memory Issues in Ruby on Rails Applications

Buffering