Skip to content

Instantly share code, notes, and snippets.

@krasnoukhov
krasnoukhov / 2013-01-07-profiling-memory-leaky-sidekiq-applications-with-ruby-2.1.md
Last active October 4, 2023 21:53
Profiling memory leaky Sidekiq applications with Ruby 2.1

My largest Sidekiq application had a memory leak and I was able to find and fix it in just few hours spent on analyzing Ruby's heap. In this post I'll show my profiling setup.

As you might know Ruby 2.1 introduced a few great changes to ObjectSpace, so now it's much easier to find a line of code that is allocating too many objects. Here is great post explaining how it's working.

I was too lazy to set up some seeding and run it locally, so I checked that test suite passes when profiling is enabled and pushed debugging to production. Production environment also suited me better since my jobs data can't be fully random generated.

So, in order to profile your worker, add this to your Sidekiq configuration:

if ENV["PROFILE"]

Keybase proof

I hereby claim:

  • I am krasnoukhov on github.
  • I am krasnoukhov (https://keybase.io/krasnoukhov) on keybase.
  • I have a public key ASBgn51BbUX1N1f3Sa7m7ZKIEHfT_FXhmmQIOO0m2CeWkwo

To claim this, I am signing this object:

import Ember from 'ember';
export default Ember.Controller.extend({
appName: 'Ember Twiddle'
});
{"clients":{},"devices":{"00:1A:22:12:D8:38":{"availableFirmwareVersion":null,"firmwareVersion":null,"groupId":"17f51411-4be0-4155-98f6-9a03bbde5681","groups":[],"id":"00:1A:22:12:D8:38","label":"Device","lastStatusUpdate":0,"lowBat":false,"unreach":false,"updateState":null,"operationLockActive":false,"temperatureOffset":0,"valvePosition":null,"type":"HEATING_THERMOSTAT"}},"groups":{"17f51411-4be0-4155-98f6-9a03bbde5681":{"channels":null,"devices":["00:1A:22:12:D8:38"],"homeId":null,"id":"17f51411-4be0-4155-98f6-9a03bbde5681","label":"Room","lastStatusUpdate":0,"lowBat":null,"metaGroupId":null,"unreach":null,"activeProfile":null,"actualTemperature":null,"boostDuration":0,"boostMode":false,"controlMode":"AUTOMATIC","maxTemperature":29.5,"minTemperature":5,"nextSwitchPoint":null,"profiles":null,"setPointTemperature":17,"windowOpen":null,"windowOpenTemperature":12,"type":"HEATING"}},"home":{"absenceEndTime":"","absenceType":"NOT_ABSENT","availableAPVersion":null,"clients":[],"connected":false,"currentAPVersion":
@krasnoukhov
krasnoukhov / heap.json
Last active January 21, 2016 15:17
ObjectSpace.dump_all output
{"address":"0x7f8b8df20628", "type":"STRING", "class":"0x7f8b8a029668", "frozen":true, "embedded":true, "fstring":true, "bytesize":3, "value":"=5F", "encoding":"UTF-8", "flags":{"wb_protected":true, "old":true, "marked":true}}
{"address":"0x7f8b8df20650", "type":"STRING", "class":"0x7f8b8a029668", "frozen":true, "embedded":true, "fstring":true, "bytesize":3, "value":"=3F", "encoding":"UTF-8", "flags":{"wb_protected":true, "old":true, "marked":true}}
{"address":"0x7f8b8df20678", "type":"STRING", "class":"0x7f8b8a029668", "frozen":true, "embedded":true, "fstring":true, "bytesize":3, "value":"=29", "encoding":"UTF-8", "flags":{"wb_protected":true, "old":true, "marked":true}}
{"address":"0x7f8b8df206a0", "type":"STRING", "class":"0x7f8b8a029668", "frozen":true, "embedded":true, "fstring":true, "bytesize":3, "value":"=28", "encoding":"UTF-8", "flags":{"wb_protected":true, "old":true, "marked":true}}
{"address":"0x7f8b8df206c8", "type":"STRING", "class":"0x7f8b8a029668", "frozen":true, "embedded":true, "fstring":tr
upstream balancer {
server app01.yoursite.com:3500 weight=1 max_fails=3;
server app02.yoursite.com:3500 weight=1 max_fails=3;
server app03.yoursite.com:3500 weight=1 max_fails=3;
}
server {
listen 80 default_server;
# Location of our static files
@krasnoukhov
krasnoukhov / config
Created November 4, 2013 20:54
Proper ssh config
# Make it so ssh-ing from one server to another passes keys around automagically
Host *
ForwardAgent yes
# Get rid of SSH connection delays
GSSAPIAuthentication no
# Stop timing out connections
ServerAliveInterval 300
ServerAliveCountMax 20
<link rel="alternate" type="application/rss+xml" href="http://candybox2.net/blog/rss.xml" title="candybox2 blog RSS" />
var jq = document.createElement('script')
jq.src = "//ajax.googleapis.com/ajax/libs/jquery/1/jquery.min.js"
document.getElementsByTagName('head')[0].appendChild(jq)
setTimeout(function() {
// Stole shekels
setInterval(function() { gibeShekel() }, 10)
// Press random buttons
setInterval(function() {
require "moped"
require "pp"
class Mongochunks
attr_accessor :interval, :config_session, :database_session, :sort_by, :stat
def initialize(config_host: "localhost:27017", host: "localhost:27017", database: "test", sort_by: :total, interval: 1)
self.interval = interval
self.config_session = Moped::Session.new([config_host])
self.config_session.use(:config)