From e1b500ec96987de595da1541a73a7d5fb9eece9c Mon Sep 17 00:00:00 2001 From: Aaron Patterson Date: Wed, 7 Sep 2011 15:26:26 -0700 Subject: LRU cache in mysql and sqlite are now per-process caches. --- activerecord/CHANGELOG | 6 ++++- .../connection_adapters/mysql_adapter.rb | 27 +++++++++++++--------- .../connection_adapters/sqlite_adapter.rb | 24 +++++++++++-------- .../cases/adapters/mysql/statement_pool_test.rb | 23 ++++++++++++++++++ .../cases/adapters/sqlite3/statement_pool_test.rb | 24 +++++++++++++++++++ 5 files changed, 82 insertions(+), 22 deletions(-) create mode 100644 activerecord/test/cases/adapters/mysql/statement_pool_test.rb create mode 100644 activerecord/test/cases/adapters/sqlite3/statement_pool_test.rb (limited to 'activerecord') diff --git a/activerecord/CHANGELOG b/activerecord/CHANGELOG index 700e11ff94..143de81925 100644 --- a/activerecord/CHANGELOG +++ b/activerecord/CHANGELOG @@ -1,4 +1,8 @@ -*Rails 3.2.0 (unreleased)* +Wed Sep 7 15:25:02 2011 Aaron Patterson + + * lib/active_record/connection_adapters/mysql_adapter.rb: LRU cache + keys are per process id. + * lib/active_record/connection_adapters/sqlite_adapter.rb: ditto * Support bulk change_table in mysql2 adapter, as well as the mysql one. [Jon Leighton] diff --git a/activerecord/lib/active_record/connection_adapters/mysql_adapter.rb b/activerecord/lib/active_record/connection_adapters/mysql_adapter.rb index 73e3afe1d5..a1824fe396 100644 --- a/activerecord/lib/active_record/connection_adapters/mysql_adapter.rb +++ b/activerecord/lib/active_record/connection_adapters/mysql_adapter.rb @@ -94,27 +94,32 @@ module ActiveRecord class StatementPool < ConnectionAdapters::StatementPool def initialize(connection, max = 1000) super - @cache = {} + @cache = Hash.new { |h,pid| h[pid] = {} } end - def each(&block); @cache.each(&block); end - def key?(key); @cache.key?(key); end - def [](key); @cache[key]; end - def length; @cache.length; end - def delete(key); @cache.delete(key); end + def each(&block); cache.each(&block); end + def key?(key); cache.key?(key); end + def [](key); cache[key]; end + def length; cache.length; end + def delete(key); cache.delete(key); end def []=(sql, key) - while @max <= @cache.size - @cache.shift.last[:stmt].close + while @max <= cache.size + cache.shift.last[:stmt].close end - @cache[sql] = key + cache[sql] = key end def clear - @cache.values.each do |hash| + cache.values.each do |hash| hash[:stmt].close end - @cache.clear + cache.clear + end + + private + def cache + @cache[$$] end end diff --git a/activerecord/lib/active_record/connection_adapters/sqlite_adapter.rb b/activerecord/lib/active_record/connection_adapters/sqlite_adapter.rb index 7c7e762c19..1932a849ee 100644 --- a/activerecord/lib/active_record/connection_adapters/sqlite_adapter.rb +++ b/activerecord/lib/active_record/connection_adapters/sqlite_adapter.rb @@ -52,29 +52,33 @@ module ActiveRecord class StatementPool < ConnectionAdapters::StatementPool def initialize(connection, max) super - @cache = {} + @cache = Hash.new { |h,pid| h[pid] = {} } end - def each(&block); @cache.each(&block); end - def key?(key); @cache.key?(key); end - def [](key); @cache[key]; end - def length; @cache.length; end + def each(&block); cache.each(&block); end + def key?(key); cache.key?(key); end + def [](key); cache[key]; end + def length; cache.length; end def []=(sql, key) - while @max <= @cache.size - dealloc(@cache.shift.last[:stmt]) + while @max <= cache.size + dealloc(cache.shift.last[:stmt]) end - @cache[sql] = key + cache[sql] = key end def clear - @cache.values.each do |hash| + cache.values.each do |hash| dealloc hash[:stmt] end - @cache.clear + cache.clear end private + def cache + @cache[$$] + end + def dealloc(stmt) stmt.close unless stmt.closed? end diff --git a/activerecord/test/cases/adapters/mysql/statement_pool_test.rb b/activerecord/test/cases/adapters/mysql/statement_pool_test.rb new file mode 100644 index 0000000000..83de90f179 --- /dev/null +++ b/activerecord/test/cases/adapters/mysql/statement_pool_test.rb @@ -0,0 +1,23 @@ +require 'cases/helper' + +module ActiveRecord::ConnectionAdapters + class MysqlAdapter + class StatementPoolTest < ActiveRecord::TestCase + def test_cache_is_per_pid + return skip('must support fork') unless Process.respond_to?(:fork) + + cache = StatementPool.new nil, 10 + cache['foo'] = 'bar' + assert_equal 'bar', cache['foo'] + + pid = fork { + lookup = cache['foo']; + exit!(!lookup) + } + + Process.waitpid pid + assert $?.success?, 'process should exit successfully' + end + end + end +end diff --git a/activerecord/test/cases/adapters/sqlite3/statement_pool_test.rb b/activerecord/test/cases/adapters/sqlite3/statement_pool_test.rb new file mode 100644 index 0000000000..ae272e2c4b --- /dev/null +++ b/activerecord/test/cases/adapters/sqlite3/statement_pool_test.rb @@ -0,0 +1,24 @@ +require 'cases/helper' + +module ActiveRecord::ConnectionAdapters + class SQLiteAdapter + class StatementPoolTest < ActiveRecord::TestCase + def test_cache_is_per_pid + return skip('must support fork') unless Process.respond_to?(:fork) + + cache = StatementPool.new nil, 10 + cache['foo'] = 'bar' + assert_equal 'bar', cache['foo'] + + pid = fork { + lookup = cache['foo']; + exit!(!lookup) + } + + Process.waitpid pid + assert $?.success?, 'process should exit successfully' + end + end + end +end + -- cgit v1.2.3