RiotGames/berkshelf — pull request #857 by keiths-osc (2013-10-04 15:08:44+00:00)
Title: Berkshelf can get "out of memory" and/or "cannot create thread" errors when downloading cookbooks from a chef server

This will require a rather lengthy explanation, so please bear with me.

When using Berkshelf to download cookbooks from a chef server, it is possible to start getting "out of memory" and/or "cannot create thread" errors. The problem is especially easy to reproduce on Windows systems (though as I will explain, there is an issue on Linux as well).

Here is an example. Suppose you have a chef server configured in your config.json that contains all the cookbooks you wish to download. Also, suppose your berkshelf cookbook cache is empty (important), and suppose you have this berksfile:

chef_api :config

cookbook 'yum', '=0.8.0'
cookbook 'apt', '=1.7.0'
cookbook 'ruby_build', '=0.8.0'
cookbook 'ohai', '=1.1.8'
cookbook 'ms_dotnet35', '=0.0.2'
cookbook 'apache2', '=1.1.12'
cookbook 'selinux', '=0.5.2'
cookbook 'php', '=1.2.2'
cookbook 'python', '=1.3.6'
cookbook 'windows', '=1.10.0'
cookbook 'chef_handler', '=1.0.6'
cookbook 'jenkins', '=0.6.3'
cookbook 'nagios', '=4.1.4'
cookbook 'iptables', '=0.12.0'
cookbook 'git', '=2.5.2'
cookbook 'ubuntu', '=1.1.2'
cookbook 'god', '=1.0.2'

A berks install will produce an error that looks like this on a Windows system:

E:\test>berks install
Installing yum (0.8.0) from chef_api: 'https://chef.example.com/'
Installing apt (1.7.0) from chef_api: 'https://chef.example.com/'
Installing ruby_build (0.8.0) from chef_api: 'https://chef.example.com/'
Installing ohai (1.1.8) from chef_api: 'https://chef.example.com/'
Installing ms_dotnet35 (0.0.2) from chef_api: 'https://chef.example.com/'
Installing apache2 (1.1.12) from chef_api: 'https://chef.example.com/'
Installing selinux (0.5.2) from chef_api: 'https://chef.example.com/'
Installing php (1.2.2) from chef_api: 'https://chef.example.com/'
Installing python (1.3.6) from chef_api: 'https://chef.example.com/'
Installing windows (1.10.0) from chef_api: 'https://chef.example.com/'
Installing chef_handler (1.0.6) from chef_api: 'https://chef.example.com/'
Installing jenkins (0.6.3) from chef_api: 'https://chef.example.com/'
Installing nagios (4.1.4) from chef_api: 'https://chef.example.com/'
Installing iptables (0.12.0) from chef_api: 'https://chef.example.com/'
Installing git (2.5.2) from chef_api: 'https://chef.example.com/'
Installing ubuntu (1.1.2) from chef_api: 'https://chef.example.com/'
Installing god (1.0.2) from chef_api: 'https://chef.example.com/'
Installing build-essential (1.4.0) from chef_api: 'https://chef.example.com/'
Installing xml (1.1.2) from chef_api: 'https://chef.example.com/'
Installing mysql (3.0.2) from chef_api: 'https://chef.example.com/'
>>>>>> Ridley::Client::ResourcesSupervisor crashed!
ThreadError: can't create Thread (12)
C:/Ruby193/lib/ruby/gems/1.9.1/gems/celluloid-0.14.1/lib/celluloid/internal_pool.rb:56:in `initialize'
C:/Ruby193/lib/ruby/gems/1.9.1/gems/celluloid-0.14.1/lib/celluloid/internal_pool.rb:56:in `new'
C:/Ruby193/lib/ruby/gems/1.9.1/gems/celluloid-0.14.1/lib/celluloid/internal_pool.rb:56:in `create'
C:/Ruby193/lib/ruby/gems/1.9.1/gems/celluloid-0.14.1/lib/celluloid/internal_pool.rb:26:in `block in get'
:10:in `synchronize'
C:/Ruby193/lib/ruby/gems/1.9.1/gems/celluloid-0.14.1/lib/celluloid/internal_pool.rb:23:in `get'
C:/Ruby193/lib/ruby/gems/1.9.1/gems/celluloid-0.14.1/lib/celluloid/thread_handle.rb:10:in `initialize'
C:/Ruby193/lib/ruby/gems/1.9.1/gems/celluloid-0.14.1/lib/celluloid/actor.rb:155:in `new'
C:/Ruby193/lib/ruby/gems/1.9.1/gems/celluloid-0.14.1/lib/celluloid/actor.rb:155:in `initialize'
C:/Ruby193/lib/ruby/gems/1.9.1/gems/celluloid-0.14.1/lib/celluloid.rb:138:in `new'
C:/Ruby193/lib/ruby/gems/1.9.1/gems/celluloid-0.14.1/lib/celluloid.rb:138:in `new_link'
C:/Ruby193/lib/ruby/gems/1.9.1/gems/celluloid-0.14.1/lib/celluloid/supervision_group.rb:136:in `start'
C:/Ruby193/lib/ruby/gems/1.9.1/gems/celluloid-0.14.1/lib/celluloid/supervision_group.rb:124:in `initialize'
C:/Ruby193/lib/ruby/gems/1.9.1/gems/celluloid-0.14.1/lib/celluloid/supervision_group.rb:82:in `new'
C:/Ruby193/lib/ruby/gems/1.9.1/gems/celluloid-0.14.1/lib/celluloid/supervision_group.rb:82:in `add'
C:/Ruby193/lib/ruby/gems/1.9.1/gems/celluloid-0.14.1/lib/celluloid/supervision_group.rb:73:in `supervise_as'
C:/Ruby193/lib/ruby/gems/1.9.1/gems/ridley-1.5.3/lib/ridley/client.rb:21:in `initialize'
C:/Ruby193/lib/ruby/gems/1.9.1/gems/celluloid-0.14.1/lib/celluloid/calls.rb:25:in `public_send'
C:/Ruby193/lib/ruby/gems/1.9.1/gems/celluloid-0.14.1/lib/celluloid/calls.rb:25:in `dispatch'
C:/Ruby193/lib/ruby/gems/1.9.1/gems/celluloid-0.14.1/lib/celluloid/calls.rb:67:in `dispatch'
C:/Ruby193/lib/ruby/gems/1.9.1/gems/celluloid-0.14.1/lib/celluloid/actor.rb:326:in `block in handle_message'
C:/Ruby193/lib/ruby/gems/1.9.1/gems/celluloid-0.14.1/lib/celluloid/tasks.rb:42:in `block in initialize'
C:/Ruby193/lib/ruby/gems/1.9.1/gems/celluloid-0.14.1/lib/celluloid/tasks/task_fiber.rb:11:in `block in create'
>>>>>> Ridley::Client crashed!
ThreadError: can't create Thread (12)
C:/Ruby193/lib/ruby/gems/1.9.1/gems/celluloid-0.14.1/lib/celluloid/internal_pool.rb:56:in `initialize'
C:/Ruby193/lib/ruby/gems/1.9.1/gems/celluloid-0.14.1/lib/celluloid/internal_pool.rb:56:in `new'
C:/Ruby193/lib/ruby/gems/1.9.1/gems/celluloid-0.14.1/lib/celluloid/internal_pool.rb:56:in `create'
C:/Ruby193/lib/ruby/gems/1.9.1/gems/celluloid-0.14.1/lib/celluloid/internal_pool.rb:26:in `block in get'
:10:in `synchronize'
C:/Ruby193/lib/ruby/gems/1.9.1/gems/celluloid-0.14.1/lib/celluloid/internal_pool.rb:23:in `get'
C:/Ruby193/lib/ruby/gems/1.9.1/gems/celluloid-0.14.1/lib/celluloid/thread_handle.rb:10:in `initialize'
C:/Ruby193/lib/ruby/gems/1.9.1/gems/celluloid-0.14.1/lib/celluloid/actor.rb:155:in `new'
C:/Ruby193/lib/ruby/gems/1.9.1/gems/celluloid-0.14.1/lib/celluloid/actor.rb:155:in `initialize'
C:/Ruby193/lib/ruby/gems/1.9.1/gems/celluloid-0.14.1/lib/celluloid.rb:138:in `new'
C:/Ruby193/lib/ruby/gems/1.9.1/gems/celluloid-0.14.1/lib/celluloid.rb:138:in `new_link'
C:/Ruby193/lib/ruby/gems/1.9.1/gems/celluloid-0.14.1/lib/celluloid/supervision_group.rb:136:in `start'
C:/Ruby193/lib/ruby/gems/1.9.1/gems/celluloid-0.14.1/lib/celluloid/supervision_group.rb:124:in `initialize'
C:/Ruby193/lib/ruby/gems/1.9.1/gems/celluloid-0.14.1/lib/celluloid/supervision_group.rb:82:in `new'
C:/Ruby193/lib/ruby/gems/1.9.1/gems/celluloid-0.14.1/lib/celluloid/supervision_group.rb:82:in `add'
C:/Ruby193/lib/ruby/gems/1.9.1/gems/celluloid-0.14.1/lib/celluloid/supervision_group.rb:73:in `supervise_as'
C:/Ruby193/lib/ruby/gems/1.9.1/gems/ridley-1.5.3/lib/ridley/client.rb:21:in `initialize'
C:/Ruby193/lib/ruby/gems/1.9.1/gems/celluloid-0.14.1/lib/celluloid/calls.rb:25:in `public_send'
C:/Ruby193/lib/ruby/gems/1.9.1/gems/celluloid-0.14.1/lib/celluloid/calls.rb:25:in `dispatch'
C:/Ruby193/lib/ruby/gems/1.9.1/gems/celluloid-0.14.1/lib/celluloid/calls.rb:67:in `dispatch'
C:/Ruby193/lib/ruby/gems/1.9.1/gems/celluloid-0.14.1/lib/celluloid/actor.rb:326:in `block in handle_message'
C:/Ruby193/lib/ruby/gems/1.9.1/gems/celluloid-0.14.1/lib/celluloid/tasks.rb:42:in `block in initialize'
C:/Ruby193/lib/ruby/gems/1.9.1/gems/celluloid-0.14.1/lib/celluloid/tasks/task_fiber.rb:11:in `block in create'
(celluloid):0:in `remote procedure call'
C:/Ruby193/lib/ruby/gems/1.9.1/gems/celluloid-0.14.1/lib/celluloid/calls.rb:95:in `value'
C:/Ruby193/lib/ruby/gems/1.9.1/gems/celluloid-0.14.1/lib/celluloid/proxies/sync_proxy.rb:28:in `method_missing'
C:/Ruby193/lib/ruby/gems/1.9.1/gems/celluloid-0.14.1/lib/celluloid/legacy.rb:14:in `method_missing'
C:/Ruby193/lib/ruby/gems/1.9.1/gems/celluloid-0.14.1/lib/celluloid/proxies/actor_proxy.rb:25:in `_send_'
C:/Ruby193/lib/ruby/gems/1.9.1/gems/celluloid-0.14.1/lib/celluloid.rb:129:in `new'
C:/Ruby193/lib/ruby/gems/1.9.1/gems/ridley-1.5.3/lib/ridley/client.rb:162:in `initialize'
C:/Ruby193/lib/ruby/gems/1.9.1/gems/celluloid-0.14.1/lib/celluloid/calls.rb:25:in `public_send'
C:/Ruby193/lib/ruby/gems/1.9.1/gems/celluloid-0.14.1/lib/celluloid/calls.rb:25:in `dispatch'
C:/Ruby193/lib/ruby/gems/1.9.1/gems/celluloid-0.14.1/lib/celluloid/calls.rb:67:in `dispatch'
C:/Ruby193/lib/ruby/gems/1.9.1/gems/celluloid-0.14.1/lib/celluloid/actor.rb:326:in `block in handle_message'
C:/Ruby193/lib/ruby/gems/1.9.1/gems/celluloid-0.14.1/lib/celluloid/tasks.rb:42:in `block in initialize'
C:/Ruby193/lib/ruby/gems/1.9.1/gems/celluloid-0.14.1/lib/celluloid/tasks/task_fiber.rb:11:in `block in create'
C:/Ruby193/lib/ruby/1.9.1/timeout.rb:60:in `start': can't create Thread (12) (ThreadError)
from C:/Ruby193/lib/ruby/1.9.1/timeout.rb:60:in `timeout'
from C:/Ruby193/lib/ruby/gems/1.9.1/gems/celluloid-0.14.1/lib/celluloid.rb:93:in `shutdown'
from C:/Ruby193/lib/ruby/gems/1.9.1/gems/celluloid-0.14.1/lib/celluloid.rb:82:in `block in register_shutdown'
>>>>>> Ridley::Client#finalize crashed!
RuntimeError:
C:/Ruby193/lib/ruby/gems/1.9.1/gems/celluloid-0.14.1/lib/celluloid/tasks/task_fiber.rb:21:in `resume'
C:/Ruby193/lib/ruby/gems/1.9.1/gems/celluloid-0.14.1/lib/celluloid/tasks/task_fiber.rb:21:in `deliver'
C:/Ruby193/lib/ruby/gems/1.9.1/gems/celluloid-0.14.1/lib/celluloid/tasks.rb:69:in `resume'
C:/Ruby193/lib/ruby/gems/1.9.1/gems/celluloid-0.14.1/lib/celluloid/actor.rb:422:in `task'
C:/Ruby193/lib/ruby/gems/1.9.1/gems/celluloid-0.14.1/lib/celluloid/actor.rb:395:in `run_finalizer'
C:/Ruby193/lib/ruby/gems/1.9.1/gems/celluloid-0.14.1/lib/celluloid/actor.rb:376:in `shutdown'
C:/Ruby193/lib/ruby/gems/1.9.1/gems/celluloid-0.14.1/lib/celluloid/actor.rb:369:in `handle_crash'
C:/Ruby193/lib/ruby/gems/1.9.1/gems/celluloid-0.14.1/lib/celluloid/actor.rb:187:in `rescue in run'
C:/Ruby193/lib/ruby/gems/1.9.1/gems/celluloid-0.14.1/lib/celluloid/actor.rb:171:in `run'
C:/Ruby193/lib/ruby/gems/1.9.1/gems/celluloid-0.14.1/lib/celluloid/actor.rb:157:in `block in initialize'
C:/Ruby193/lib/ruby/gems/1.9.1/gems/celluloid-0.14.1/lib/celluloid/thread_handle.rb:13:in `block in initialize'
C:/Ruby193/lib/ruby/gems/1.9.1/gems/celluloid-0.14.1/lib/celluloid/internal_pool.rb:59:in `call'
C:/Ruby193/lib/ruby/gems/1.9.1/gems/celluloid-0.14.1/lib/celluloid/internal_pool.rb:59:in `block in create'
>>>>>> Ridley::Client::ResourcesSupervisor#finalize crashed!
RuntimeError:
C:/Ruby193/lib/ruby/gems/1.9.1/gems/celluloid-0.14.1/lib/celluloid/tasks/task_fiber.rb:21:in `resume'
C:/Ruby193/lib/ruby/gems/1.9.1/gems/celluloid-0.14.1/lib/celluloid/tasks/task_fiber.rb:21:in `deliver'
C:/Ruby193/lib/ruby/gems/1.9.1/gems/celluloid-0.14.1/lib/celluloid/tasks.rb:69:in `resume'
C:/Ruby193/lib/ruby/gems/1.9.1/gems/celluloid-0.14.1/lib/celluloid/actor.rb:422:in `task'
C:/Ruby193/lib/ruby/gems/1.9.1/gems/celluloid-0.14.1/lib/celluloid/actor.rb:395:in `run_finalizer'
C:/Ruby193/lib/ruby/gems/1.9.1/gems/celluloid-0.14.1/lib/celluloid/actor.rb:376:in `shutdown'
C:/Ruby193/lib/ruby/gems/1.9.1/gems/celluloid-0.14.1/lib/celluloid/actor.rb:369:in `handle_crash'
C:/Ruby193/lib/ruby/gems/1.9.1/gems/celluloid-0.14.1/lib/celluloid/actor.rb:187:in `rescue in run'
C:/Ruby193/lib/ruby/gems/1.9.1/gems/celluloid-0.14.1/lib/celluloid/actor.rb:171:in `run'
C:/Ruby193/lib/ruby/gems/1.9.1/gems/celluloid-0.14.1/lib/celluloid/actor.rb:157:in `block in initialize'
C:/Ruby193/lib/ruby/gems/1.9.1/gems/celluloid-0.14.1/lib/celluloid/thread_handle.rb:13:in `block in initialize'
C:/Ruby193/lib/ruby/gems/1.9.1/gems/celluloid-0.14.1/lib/celluloid/internal_pool.rb:59:in `call'
C:/Ruby193/lib/ruby/gems/1.9.1/gems/celluloid-0.14.1/lib/celluloid/internal_pool.rb:59:in `block in create'

After a good deal of research, I have tracked the cause of this down to the way that berkshelf is using ridley in chef_api_location.rb. Here is a very simple script that demonstrates the problem:

require 'berkshelf'
require 'thread'

25.times do
chef_api_location = Berkshelf::ChefAPILocation.new('apt', '= 1.7.0',
chef_api: 'https://chef.example.com',
node_name: 'admin',
client_key: "E:/vagrant/chefserver/keys/chef/admin.pem",
verify_ssl: false)

chef_api_location = nil # nil this out so it's eligible for garbage collection
puts "Thread.list.length = #{Thread.list.length}"
end

This script produces the following output:

Thread.list.length = 33
Thread.list.length = 65
Thread.list.length = 97
Thread.list.length = 129
Thread.list.length = 161
Thread.list.length = 193
Thread.list.length = 225
Thread.list.length = 257
Thread.list.length = 289
Thread.list.length = 321
Thread.list.length = 353
Thread.list.length = 385
Thread.list.length = 417
Thread.list.length = 449
Thread.list.length = 481
Thread.list.length = 513
Thread.list.length = 545
Thread.list.length = 577
Thread.list.length = 609
Thread.list.length = 641
C:/Ruby193/lib/ruby/1.9.1/timeout.rb:60:in `start': failed to allocate memory (NoMemoryError)
from C:/Ruby193/lib/ruby/1.9.1/timeout.rb:60:in `timeout'
from C:/Ruby193/lib/ruby/gems/1.9.1/gems/celluloid-0.14.1/lib/celluloid.rb:93:in `shutdown'
from C:/Ruby193/lib/ruby/gems/1.9.1/gems/celluloid-0.14.1/lib/celluloid.rb:82:in `block in register_shutdown'
C:/Ruby193/lib/ruby/gems/1.9.1/gems/celluloid-0.14.1/lib/celluloid/internal_pool.rb:56:in `new': failed to allocate memory (NoMemoryError)
from C:/Ruby193/lib/ruby/gems/1.9.1/gems/celluloid-0.14.1/lib/celluloid/internal_pool.rb:56:in `create'
from C:/Ruby193/lib/ruby/gems/1.9.1/gems/celluloid-0.14.1/lib/celluloid/internal_pool.rb:26:in `block in get'
from :10:in `synchronize'
from C:/Ruby193/lib/ruby/gems/1.9.1/gems/celluloid-0.14.1/lib/celluloid/internal_pool.rb:23:in `get'
from C:/Ruby193/lib/ruby/gems/1.9.1/gems/celluloid-0.14.1/lib/celluloid/thread_handle.rb:10:in `initialize'
from C:/Ruby193/lib/ruby/gems/1.9.1/gems/celluloid-0.14.1/lib/celluloid/actor.rb:155:in `new'
from C:/Ruby193/lib/ruby/gems/1.9.1/gems/celluloid-0.14.1/lib/celluloid/actor.rb:155:in `initialize'
from C:/Ruby193/lib/ruby/gems/1.9.1/gems/celluloid-0.14.1/lib/celluloid.rb:138:in `new'
from C:/Ruby193/lib/ruby/gems/1.9.1/gems/celluloid-0.14.1/lib/celluloid.rb:138:in `new_link'
from C:/Ruby193/lib/ruby/gems/1.9.1/gems/ridley-1.5.3/lib/ridley/resources/node_resource.rb:47:in `initialize'
from C:/Ruby193/lib/ruby/gems/1.9.1/gems/celluloid-0.14.1/lib/celluloid/calls.rb:25:in `public_send'
from C:/Ruby193/lib/ruby/gems/1.9.1/gems/celluloid-0.14.1/lib/celluloid/calls.rb:25:in `dispatch'
from C:/Ruby193/lib/ruby/gems/1.9.1/gems/celluloid-0.14.1/lib/celluloid/calls.rb:67:in `dispatch'
from C:/Ruby193/lib/ruby/gems/1.9.1/gems/celluloid-0.14.1/lib/celluloid/actor.rb:326:in `block in handle_message'
from C:/Ruby193/lib/ruby/gems/1.9.1/gems/celluloid-0.14.1/lib/celluloid/tasks.rb:42:in `block in initialize'
from C:/Ruby193/lib/ruby/gems/1.9.1/gems/celluloid-0.14.1/lib/celluloid/tasks/task_fiber.rb:11:in `block in create'
from (celluloid):0:in `remote procedure call'
from C:/Ruby193/lib/ruby/gems/1.9.1/gems/celluloid-0.14.1/lib/celluloid/calls.rb:95:in `value'
from C:/Ruby193/lib/ruby/gems/1.9.1/gems/celluloid-0.14.1/lib/celluloid/proxies/sync_proxy.rb:28:in `method_missing'
from C:/Ruby193/lib/ruby/gems/1.9.1/gems/celluloid-0.14.1/lib/celluloid/legacy.rb:14:in `method_missing'
from C:/Ruby193/lib/ruby/gems/1.9.1/gems/celluloid-0.14.1/lib/celluloid/proxies/actor_proxy.rb:25:in `_send_'
from C:/Ruby193/lib/ruby/gems/1.9.1/gems/celluloid-0.14.1/lib/celluloid.rb:140:in `new_link'
from C:/Ruby193/lib/ruby/gems/1.9.1/gems/celluloid-0.14.1/lib/celluloid/supervision_group.rb:136:in `start'
from C:/Ruby193/lib/ruby/gems/1.9.1/gems/celluloid-0.14.1/lib/celluloid/supervision_group.rb:124:in `initialize'
from C:/Ruby193/lib/ruby/gems/1.9.1/gems/celluloid-0.14.1/lib/celluloid/supervision_group.rb:82:in `new'
from C:/Ruby193/lib/ruby/gems/1.9.1/gems/celluloid-0.14.1/lib/celluloid/supervision_group.rb:82:in `add'
from C:/Ruby193/lib/ruby/gems/1.9.1/gems/celluloid-0.14.1/lib/celluloid/supervision_group.rb:73:in `supervise_as'
from C:/Ruby193/lib/ruby/gems/1.9.1/gems/ridley-1.5.3/lib/ridley/client.rb:24:in `initialize'
from C:/Ruby193/lib/ruby/gems/1.9.1/gems/celluloid-0.14.1/lib/celluloid/calls.rb:25:in `public_send'
from C:/Ruby193/lib/ruby/gems/1.9.1/gems/celluloid-0.14.1/lib/celluloid/calls.rb:25:in `dispatch'
from C:/Ruby193/lib/ruby/gems/1.9.1/gems/celluloid-0.14.1/lib/celluloid/calls.rb:67:in `dispatch'
from C:/Ruby193/lib/ruby/gems/1.9.1/gems/celluloid-0.14.1/lib/celluloid/actor.rb:326:in `block in handle_message'
from C:/Ruby193/lib/ruby/gems/1.9.1/gems/celluloid-0.14.1/lib/celluloid/tasks.rb:42:in `block in initialize'
from C:/Ruby193/lib/ruby/gems/1.9.1/gems/celluloid-0.14.1/lib/celluloid/tasks/task_fiber.rb:11:in `block in create'
from (celluloid):0:in `remote procedure call'
from C:/Ruby193/lib/ruby/gems/1.9.1/gems/celluloid-0.14.1/lib/celluloid/calls.rb:95:in `value'
from C:/Ruby193/lib/ruby/gems/1.9.1/gems/celluloid-0.14.1/lib/celluloid/proxies/sync_proxy.rb:28:in `method_missing'
from C:/Ruby193/lib/ruby/gems/1.9.1/gems/celluloid-0.14.1/lib/celluloid/legacy.rb:14:in `method_missing'
from C:/Ruby193/lib/ruby/gems/1.9.1/gems/celluloid-0.14.1/lib/celluloid/proxies/actor_proxy.rb:25:in `_send_'
from C:/Ruby193/lib/ruby/gems/1.9.1/gems/celluloid-0.14.1/lib/celluloid.rb:129:in `new'
from C:/Ruby193/lib/ruby/gems/1.9.1/gems/ridley-1.5.3/lib/ridley/client.rb:162:in `initialize'
from C:/Ruby193/lib/ruby/gems/1.9.1/gems/celluloid-0.14.1/lib/celluloid/calls.rb:25:in `public_send'
from C:/Ruby193/lib/ruby/gems/1.9.1/gems/celluloid-0.14.1/lib/celluloid/calls.rb:25:in `dispatch'
from C:/Ruby193/lib/ruby/gems/1.9.1/gems/celluloid-0.14.1/lib/celluloid/calls.rb:67:in `dispatch'
from C:/Ruby193/lib/ruby/gems/1.9.1/gems/celluloid-0.14.1/lib/celluloid/actor.rb:326:in `block in handle_message'
from C:/Ruby193/lib/ruby/gems/1.9.1/gems/celluloid-0.14.1/lib/celluloid/tasks.rb:42:in `block in initialize'
from C:/Ruby193/lib/ruby/gems/1.9.1/gems/celluloid-0.14.1/lib/celluloid/tasks/task_fiber.rb:11:in `block in create'
from (celluloid):0:in `remote procedure call'
from C:/Ruby193/lib/ruby/gems/1.9.1/gems/celluloid-0.14.1/lib/celluloid/calls.rb:95:in `value'
from C:/Ruby193/lib/ruby/gems/1.9.1/gems/celluloid-0.14.1/lib/celluloid/proxies/sync_proxy.rb:28:in `method_missing'
from C:/Ruby193/lib/ruby/gems/1.9.1/gems/celluloid-0.14.1/lib/celluloid/legacy.rb:14:in `method_missing'
from C:/Ruby193/lib/ruby/gems/1.9.1/gems/celluloid-0.14.1/lib/celluloid/proxies/actor_proxy.rb:25:in `_send_'
from C:/Ruby193/lib/ruby/gems/1.9.1/gems/celluloid-0.14.1/lib/celluloid.rb:129:in `new'
from C:/Ruby193/lib/ruby/gems/1.9.1/gems/ridley-1.5.3/lib/ridley.rb:27:in `new'
from C:/Ruby193/lib/ruby/gems/1.9.1/gems/berkshelf-2.0.10/lib/berkshelf/locations/chef_api_location.rb:131:in `initialize'
from E:/RubyScripts/ridleytest/berkshelftest.rb:5:in `new'
from E:/RubyScripts/ridleytest/berkshelftest.rb:5:in `block in '
from E:/RubyScripts/ridleytest/berkshelftest.rb:4:in `times'
from E:/RubyScripts/ridleytest/berkshelftest.rb:4:in `'
from -e:1:in `load'
from -e:1:in `

'

Notice how the number of active threads quickly climbs simply by instantiating an instance of Berkshelf::ChefAPILocation. The initializer of the ChefAPILocation class instantiates a Ridley connection. This starts up a pool of Celluloid actors. The ChefAPILocation class has a finalizer defined that calls terminate on the ridley connection. As is demonstrated by the above script, you cannot depend on the finalizer to run at any particular time to clean up the connection. This same script on linux (Ubuntu in my testing) also demonstrates that the number of threads increases dramatically. However, Windows systems have a much lower number of threads that can be active in a process before errors begin to occur.

This pull request solves this issue by re-using ridley connections for the same chef server. A reference count is kept, and when the number of references goes to 0, the ridley connection is terminated.

I also found that the terminate call in the finalizer in the original code was the synchronous version (as opposed to terminate!, which is async). When running the rspec tests, I noticed that I would sometimes get an error message that said "Couldn't cleanly terminate all actors in 10 seconds!" The tests call Celluloid.shutdown before each example, and the message was coming from the shutdown call. After some painstaking research, I found that the cause was that the finalizer on the ChefAPILocation class was calling terminate on the ridley connection rather than terminate! (synchronously rather than asynchronously).