activerecord-turntable 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (130) hide show
  1. data/.document +5 -0
  2. data/.gitignore +25 -0
  3. data/.rspec +3 -0
  4. data/Gemfile +25 -0
  5. data/Guardfile +9 -0
  6. data/LICENSE.txt +20 -0
  7. data/README.rdoc +290 -0
  8. data/Rakefile +101 -0
  9. data/activerecord-turntable.gemspec +47 -0
  10. data/lib/active_record/turntable.rb +58 -0
  11. data/lib/active_record/turntable/active_record_ext.rb +26 -0
  12. data/lib/active_record/turntable/active_record_ext/.gitkeep +0 -0
  13. data/lib/active_record/turntable/active_record_ext/abstract_adapter.rb +50 -0
  14. data/lib/active_record/turntable/active_record_ext/clever_load.rb +90 -0
  15. data/lib/active_record/turntable/active_record_ext/fixtures.rb +131 -0
  16. data/lib/active_record/turntable/active_record_ext/log_subscriber.rb +64 -0
  17. data/lib/active_record/turntable/active_record_ext/persistence.rb +95 -0
  18. data/lib/active_record/turntable/active_record_ext/schema_dumper.rb +107 -0
  19. data/lib/active_record/turntable/active_record_ext/sequencer.rb +28 -0
  20. data/lib/active_record/turntable/active_record_ext/transactions.rb +33 -0
  21. data/lib/active_record/turntable/algorithm.rb +7 -0
  22. data/lib/active_record/turntable/algorithm/.gitkeep +0 -0
  23. data/lib/active_record/turntable/algorithm/base.rb +11 -0
  24. data/lib/active_record/turntable/algorithm/range_algorithm.rb +37 -0
  25. data/lib/active_record/turntable/algorithm/range_bsearch_algorithm.rb +41 -0
  26. data/lib/active_record/turntable/base.rb +130 -0
  27. data/lib/active_record/turntable/cluster.rb +70 -0
  28. data/lib/active_record/turntable/compatible.rb +19 -0
  29. data/lib/active_record/turntable/config.rb +24 -0
  30. data/lib/active_record/turntable/connection_proxy.rb +218 -0
  31. data/lib/active_record/turntable/connection_proxy/mixable.rb +39 -0
  32. data/lib/active_record/turntable/error.rb +8 -0
  33. data/lib/active_record/turntable/helpers.rb +5 -0
  34. data/lib/active_record/turntable/helpers/test_helper.rb +25 -0
  35. data/lib/active_record/turntable/master_shard.rb +28 -0
  36. data/lib/active_record/turntable/migration.rb +132 -0
  37. data/lib/active_record/turntable/mixer.rb +203 -0
  38. data/lib/active_record/turntable/mixer/fader.rb +34 -0
  39. data/lib/active_record/turntable/mixer/fader/calculate_shards_sum_result.rb +15 -0
  40. data/lib/active_record/turntable/mixer/fader/insert_shards_merge_result.rb +24 -0
  41. data/lib/active_record/turntable/mixer/fader/select_shards_merge_result.rb +22 -0
  42. data/lib/active_record/turntable/mixer/fader/specified_shard.rb +12 -0
  43. data/lib/active_record/turntable/mixer/fader/update_shards_merge_result.rb +17 -0
  44. data/lib/active_record/turntable/pool_proxy.rb +56 -0
  45. data/lib/active_record/turntable/rack.rb +5 -0
  46. data/lib/active_record/turntable/rack/connection_management.rb +18 -0
  47. data/lib/active_record/turntable/railtie.rb +14 -0
  48. data/lib/active_record/turntable/railties/databases.rake +205 -0
  49. data/lib/active_record/turntable/seq_shard.rb +14 -0
  50. data/lib/active_record/turntable/sequencer.rb +46 -0
  51. data/lib/active_record/turntable/sequencer/api.rb +36 -0
  52. data/lib/active_record/turntable/sequencer/mysql.rb +32 -0
  53. data/lib/active_record/turntable/shard.rb +48 -0
  54. data/lib/active_record/turntable/sql_tree_patch.rb +199 -0
  55. data/lib/active_record/turntable/version.rb +5 -0
  56. data/lib/activerecord-turntable.rb +2 -0
  57. data/lib/generators/active_record/turntable/install_generator.rb +14 -0
  58. data/lib/generators/templates/turntable.yml +40 -0
  59. data/sample_app/.gitignore +16 -0
  60. data/sample_app/Gemfile +41 -0
  61. data/sample_app/README.rdoc +261 -0
  62. data/sample_app/Rakefile +7 -0
  63. data/sample_app/app/assets/images/rails.png +0 -0
  64. data/sample_app/app/assets/javascripts/application.js +15 -0
  65. data/sample_app/app/assets/stylesheets/application.css +13 -0
  66. data/sample_app/app/controllers/application_controller.rb +3 -0
  67. data/sample_app/app/helpers/application_helper.rb +2 -0
  68. data/sample_app/app/mailers/.gitkeep +0 -0
  69. data/sample_app/app/models/.gitkeep +0 -0
  70. data/sample_app/app/models/user.rb +4 -0
  71. data/sample_app/app/views/layouts/application.html.erb +14 -0
  72. data/sample_app/config.ru +4 -0
  73. data/sample_app/config/application.rb +65 -0
  74. data/sample_app/config/boot.rb +6 -0
  75. data/sample_app/config/database.yml +70 -0
  76. data/sample_app/config/environment.rb +5 -0
  77. data/sample_app/config/environments/development.rb +37 -0
  78. data/sample_app/config/environments/production.rb +67 -0
  79. data/sample_app/config/environments/test.rb +37 -0
  80. data/sample_app/config/initializers/backtrace_silencers.rb +7 -0
  81. data/sample_app/config/initializers/inflections.rb +15 -0
  82. data/sample_app/config/initializers/mime_types.rb +5 -0
  83. data/sample_app/config/initializers/secret_token.rb +7 -0
  84. data/sample_app/config/initializers/session_store.rb +8 -0
  85. data/sample_app/config/initializers/wrap_parameters.rb +14 -0
  86. data/sample_app/config/locales/en.yml +5 -0
  87. data/sample_app/config/routes.rb +58 -0
  88. data/sample_app/config/turntable.yml +64 -0
  89. data/sample_app/db/migrate/20120316073058_create_users.rb +11 -0
  90. data/sample_app/db/seeds.rb +7 -0
  91. data/sample_app/lib/assets/.gitkeep +0 -0
  92. data/sample_app/lib/tasks/.gitkeep +0 -0
  93. data/sample_app/log/.gitkeep +0 -0
  94. data/sample_app/public/404.html +26 -0
  95. data/sample_app/public/422.html +26 -0
  96. data/sample_app/public/500.html +25 -0
  97. data/sample_app/public/favicon.ico +0 -0
  98. data/sample_app/public/index.html +241 -0
  99. data/sample_app/public/robots.txt +5 -0
  100. data/sample_app/script/rails +6 -0
  101. data/sample_app/vendor/assets/javascripts/.gitkeep +0 -0
  102. data/sample_app/vendor/assets/stylesheets/.gitkeep +0 -0
  103. data/sample_app/vendor/plugins/.gitkeep +0 -0
  104. data/script/performance/algorithm +32 -0
  105. data/spec/active_record/turntable/active_record_ext/clever_load_spec.rb +81 -0
  106. data/spec/active_record/turntable/active_record_ext/persistence_spec.rb +151 -0
  107. data/spec/active_record/turntable/algorithm/range_algorithm_spec.rb +35 -0
  108. data/spec/active_record/turntable/algorithm_spec.rb +69 -0
  109. data/spec/active_record/turntable/base_spec.rb +13 -0
  110. data/spec/active_record/turntable/cluster_spec.rb +18 -0
  111. data/spec/active_record/turntable/config_spec.rb +17 -0
  112. data/spec/active_record/turntable/connection_proxy_spec.rb +186 -0
  113. data/spec/active_record/turntable/finder_spec.rb +27 -0
  114. data/spec/active_record/turntable/mixer/fader_spec.rb +4 -0
  115. data/spec/active_record/turntable/mixer_spec.rb +114 -0
  116. data/spec/active_record/turntable/shard_spec.rb +21 -0
  117. data/spec/active_record/turntable_spec.rb +30 -0
  118. data/spec/config/database.yml +45 -0
  119. data/spec/config/turntable.yml +17 -0
  120. data/spec/fabricators/.gitkeep +0 -0
  121. data/spec/fabricators/turntable_fabricator.rb +14 -0
  122. data/spec/migrations/.gitkeep +0 -0
  123. data/spec/migrations/001_create_users.rb +16 -0
  124. data/spec/migrations/002_create_user_statuses.rb +16 -0
  125. data/spec/migrations/003_create_cards.rb +14 -0
  126. data/spec/migrations/004_create_cards_users.rb +15 -0
  127. data/spec/spec_helper.rb +23 -0
  128. data/spec/test_models.rb +27 -0
  129. data/spec/turntable_helper.rb +29 -0
  130. metadata +367 -0
@@ -0,0 +1,47 @@
1
+ $:.push File.expand_path("../lib", __FILE__)
2
+ require "active_record/turntable/version"
3
+
4
+ Gem::Specification.new do |s|
5
+ s.name = "activerecord-turntable"
6
+ s.version = ActiveRecord::Turntable::VERSION
7
+ s.authors = ["gussan"]
8
+ s.homepage = "https://github.com/drecom/activerecord-turntable"
9
+ s.summary = %q{ActiveRecord Sharding plugin}
10
+ s.description = %q{AcviveRecord Sharding plugin}
11
+
12
+ s.rubyforge_project = "activerecord-turntable"
13
+ s.extra_rdoc_files = [
14
+ "LICENSE.txt",
15
+ "README.rdoc"
16
+ ]
17
+
18
+ s.files = `git ls-files | grep -v "^spec"`.split("\n")
19
+ s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
20
+ s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
21
+ s.require_paths = ["lib"]
22
+ s.licenses = ["MIT"]
23
+ s.rubygems_version = "1.8.16"
24
+
25
+ # runtime dependencies
26
+ s.add_runtime_dependency(%q<activerecord>, [">= 3.0.0"])
27
+ s.add_runtime_dependency(%q<activesupport>, [">=3.0.0"])
28
+ s.add_runtime_dependency(%q<sql_tree>, ["= 0.2.0"])
29
+ s.add_runtime_dependency(%q<bsearch>, ["~> 1.5"])
30
+ s.add_runtime_dependency(%q<httpclient>, [">= 0"])
31
+
32
+ # development dependencies
33
+ s.add_development_dependency(%q<rake>, ["~> 0.9.2"])
34
+ s.add_development_dependency(%q<rspec>, [">= 0"])
35
+ s.add_development_dependency(%q<rr>, [">= 0"])
36
+ s.add_development_dependency(%q<mysql2>, [">= 0"])
37
+ s.add_development_dependency(%q<fabrication>, [">= 0"])
38
+ s.add_development_dependency(%q<faker>, [">= 0"])
39
+ s.add_development_dependency(%q<activerecord-import>, [">= 0"])
40
+ s.add_development_dependency(%q<pry>, [">= 0"])
41
+ s.add_development_dependency(%q<guard-rspec>, [">= 0"])
42
+
43
+ if RUBY_PLATFORM =~ /darwin/
44
+ s.add_development_dependency(%q<growl>, [">= 0"])
45
+ end
46
+ end
47
+
@@ -0,0 +1,58 @@
1
+ #
2
+ #= ActiveRecord::Turntable
3
+ #
4
+ # ActiveRecord Sharding Plugin
5
+ #
6
+ require 'active_record/turntable/version'
7
+ require 'active_record'
8
+ require 'active_record/fixtures'
9
+ require 'active_support/concern'
10
+ require 'active_record/turntable/error'
11
+ require 'logger'
12
+ require 'singleton'
13
+
14
+ module ActiveRecord::Turntable
15
+ extend ActiveSupport::Concern
16
+
17
+ autoload :ActiveRecordExt, 'active_record/turntable/active_record_ext'
18
+ autoload :Algorithm, 'active_record/turntable/algorithm'
19
+ autoload :Base, 'active_record/turntable/base'
20
+ autoload :Cluster, 'active_record/turntable/cluster'
21
+ autoload :Config, 'active_record/turntable/config'
22
+ autoload :Compatible, "active_record/turntable/compatible"
23
+ autoload :ConnectionProxy, 'active_record/turntable/connection_proxy'
24
+ autoload :Helpers, 'active_record/turntable/helpers'
25
+ autoload :MasterShard, 'active_record/turntable/master_shard'
26
+ autoload :Migration, 'active_record/turntable/migration'
27
+ autoload :Mixer, 'active_record/turntable/mixer'
28
+ autoload :PoolProxy, 'active_record/turntable/pool_proxy'
29
+ autoload :Rack, 'active_record/turntable/rack'
30
+ autoload :SeqShard, 'active_record/turntable/seq_shard'
31
+ autoload :Sequencer, 'active_record/turntable/sequencer'
32
+ autoload :Shard, 'active_record/turntable/shard'
33
+
34
+ included do
35
+ include ActiveRecordExt
36
+ include Base
37
+ end
38
+
39
+ module ClassMethods
40
+ DEFAULT_PATH = File.dirname(File.dirname(__FILE__))
41
+
42
+ def turntable_config_file
43
+ @@turntable_config_file ||=
44
+ File.join(defined?(::Rails) ?
45
+ ::Rails.root.to_s : DEFAULT_PATH, 'config/turntable.yml')
46
+ end
47
+
48
+ def turntable_config_file=(filename)
49
+ @@turntable_config_file = filename
50
+ end
51
+
52
+ def turntable_config
53
+ ActiveRecord::Turntable::Config.instance
54
+ end
55
+ end
56
+
57
+ require "active_record/turntable/railtie" if defined?(Rails)
58
+ end
@@ -0,0 +1,26 @@
1
+ module ActiveRecord::Turntable
2
+ module ActiveRecordExt
3
+ extend ActiveSupport::Concern
4
+
5
+ autoload :AbstractAdapter, 'active_record/turntable/active_record_ext/abstract_adapter'
6
+ autoload :CleverLoad, 'active_record/turntable/active_record_ext/clever_load'
7
+ autoload :LogSubscriber, 'active_record/turntable/active_record_ext/log_subscriber'
8
+ autoload :Persistence, 'active_record/turntable/active_record_ext/persistence'
9
+ autoload :SchemaDumper, 'active_record/turntable/active_record_ext/schema_dumper'
10
+ autoload :Sequencer, 'active_record/turntable/active_record_ext/sequencer'
11
+ autoload :Transactions, 'active_record/turntable/active_record_ext/transactions'
12
+
13
+ included do
14
+ include Transactions
15
+ ActiveRecord::ConnectionAdapters::AbstractAdapter.send(:include, Sequencer)
16
+ ActiveRecord::ConnectionAdapters::AbstractAdapter.send(:include, AbstractAdapter)
17
+ ActiveRecord::LogSubscriber.send(:include, LogSubscriber)
18
+ ActiveRecord::Persistence.send(:include, Persistence)
19
+ ActiveRecord::Relation.send(:include, CleverLoad)
20
+ ActiveRecord::VERSION::STRING < '3.1' ?
21
+ ActiveRecord::Migration.extend(ActiveRecord::Turntable::Migration) :
22
+ ActiveRecord::Migration.send(:include, ActiveRecord::Turntable::Migration)
23
+ require 'active_record/turntable/active_record_ext/fixtures'
24
+ end
25
+ end
26
+ end
@@ -0,0 +1,50 @@
1
+ module ActiveRecord::Turntable
2
+ module ActiveRecordExt
3
+ module AbstractAdapter
4
+ extend ActiveSupport::Concern
5
+
6
+ included do
7
+ protected
8
+
9
+ if ActiveRecord::VERSION::STRING < '3.1'
10
+ def log(sql, name)
11
+ name ||= "SQL"
12
+ @instrumenter.instrument("sql.active_record",
13
+ :sql => sql, :name => name, :connection_id => object_id,
14
+ :turntable_shard_name => turntable_shard_name) do
15
+ yield
16
+ end
17
+ rescue Exception => e
18
+ message = "#{e.class.name}: #{e.message}: #{sql} : #{turntable_shard_name}"
19
+ @logger.debug message if @logger
20
+ raise translate_exception(e, message)
21
+ end
22
+ else
23
+ def log(sql, name = "SQL", binds = [])
24
+ @instrumenter.instrument(
25
+ "sql.active_record",
26
+ :sql => sql,
27
+ :name => name,
28
+ :connection_id => object_id,
29
+ :binds => binds,
30
+ :turntable_shard_name => turntable_shard_name) { yield }
31
+ rescue Exception => e
32
+ message = "#{e.class.name}: #{e.message}: #{sql} : #{turntable_shard_name}"
33
+ @logger.debug message if @logger
34
+ exception = translate_exception(e, message)
35
+ exception.set_backtrace e.backtrace
36
+ raise exception
37
+ end
38
+ end
39
+ end
40
+
41
+ def turntable_shard_name=(name)
42
+ @turntable_shard_name = name.to_s
43
+ end
44
+
45
+ def turntable_shard_name
46
+ @turntable_shard_name
47
+ end
48
+ end
49
+ end
50
+ end
@@ -0,0 +1,90 @@
1
+ module ActiveRecord::Turntable::ActiveRecordExt
2
+ module CleverLoad
3
+ extend ActiveSupport::Concern
4
+
5
+ included do
6
+ ActiveRecord::VERSION::STRING < '3.1' ?
7
+ include(AR30):
8
+ include(AR31)
9
+
10
+ class << ActiveRecord::Base
11
+ delegate :clever_load!, :to => :scoped
12
+ end
13
+ end
14
+
15
+ module AR30
16
+ def clever_load!(association_name)
17
+ # load records
18
+ records = self.to_a
19
+ klass = records.first.class
20
+ reflection = klass.reflections[association_name]
21
+
22
+ if reflection
23
+ foreign_class = reflection.klass
24
+ foreign_objects = case reflection.macro
25
+ when :has_one
26
+ foreign_class.where(reflection.primary_key_name => records.map(&reflection.association_primary_key.to_sym).uniq)
27
+ when :belongs_to
28
+ foreign_class.where(reflection.association_primary_key => records.map(&reflection.primary_key_name.to_sym).uniq)
29
+ else
30
+ []
31
+ end
32
+
33
+ self.each do |obj|
34
+ matched_object = case reflection.macro
35
+ when :has_one
36
+ foreign_objects.find {|fo|
37
+ obj.send(reflection.association_primary_key) == fo.send(reflection.primary_key_name)
38
+ }
39
+ when :belongs_to
40
+ foreign_objects.find {|fo|
41
+ obj.send(reflection.primary_key_name) == fo.send(reflection.association_primary_key)
42
+ }
43
+ end
44
+ association_proxy = obj.send("set_#{reflection.name}_target", matched_object)
45
+ # TODO: set reverse_instance
46
+ # association_proxy.send(:set_inverse_instance, matched_object, obj)
47
+ end
48
+ end
49
+ records
50
+ end
51
+ end
52
+
53
+ module AR31
54
+ def clever_load!(association_name)
55
+ # load records
56
+ records = self.to_a
57
+ klass = records.first.class
58
+ reflection = klass.reflections[association_name]
59
+
60
+ if reflection
61
+ foreign_class = reflection.klass
62
+ foreign_objects = case reflection.macro
63
+ when :has_one
64
+ foreign_class.where(reflection.foreign_key => records.map(&reflection.association_primary_key.to_sym).uniq)
65
+ when :belongs_to
66
+ foreign_class.where(reflection.association_primary_key => records.map(&reflection.foreign_key.to_sym).uniq)
67
+ else
68
+ []
69
+ end
70
+
71
+ self.each do |obj|
72
+ matched_object = case reflection.macro
73
+ when :has_one
74
+ foreign_objects.find {|fo|
75
+ obj.send(reflection.association_primary_key) == fo.send(reflection.foreign_key)
76
+ }
77
+ when :belongs_to
78
+ foreign_objects.find {|fo|
79
+ obj.send(reflection.foreign_key) == fo.send(reflection.association_primary_key)
80
+ }
81
+ end
82
+ obj.association(association_name).target = matched_object
83
+ obj.association(association_name).send(:set_inverse_instance, matched_object)
84
+ end
85
+ end
86
+ records
87
+ end
88
+ end
89
+ end
90
+ end
@@ -0,0 +1,131 @@
1
+ #
2
+ # force TestFixtures to begin transaction with all shards.
3
+ #
4
+ require 'active_record/fixtures'
5
+ module ActiveRecord
6
+ class Fixtures
7
+ def self.create_fixtures(fixtures_directory, table_names, class_names = {})
8
+ table_names = [table_names].flatten.map { |n| n.to_s }
9
+ table_names.each { |n|
10
+ class_names[n.tr('/', '_').to_sym] = n.classify if n.include?('/')
11
+ }
12
+
13
+ # FIXME: Apparently JK uses this.
14
+ connection = block_given? ? yield : ActiveRecord::Base.connection
15
+
16
+ files_to_read = table_names.reject { |table_name|
17
+ fixture_is_cached?(connection, table_name)
18
+ }
19
+
20
+ unless files_to_read.empty?
21
+ connection.disable_referential_integrity do
22
+ fixtures_map = {}
23
+
24
+ fixture_files = files_to_read.map do |path|
25
+ table_name = path.tr '/', '_'
26
+
27
+ fixtures_map[path] = ActiveRecord::Fixtures.new(
28
+ connection,
29
+ table_name,
30
+ class_names[table_name.to_sym] || table_name.classify,
31
+ ::File.join(fixtures_directory, path))
32
+ end
33
+
34
+ all_loaded_fixtures.update(fixtures_map)
35
+
36
+ ActiveRecord::Turntable::Base.force_transaction_all_shards!(:requires_new => true) do
37
+ fixture_files.each do |ff|
38
+ conn = ff.model_class.respond_to?(:connection) ? ff.model_class.connection : connection
39
+ table_rows = ff.table_rows
40
+
41
+ table_rows.keys.each do |table|
42
+ conn.delete "DELETE FROM #{conn.quote_table_name(table)}", 'Fixture Delete'
43
+ end
44
+
45
+ table_rows.each do |table_name,rows|
46
+ rows.each do |row|
47
+ conn.insert_fixture(row, table_name)
48
+ end
49
+ end
50
+ end
51
+
52
+ # Cap primary key sequences to max(pk).
53
+ if connection.respond_to?(:reset_pk_sequence!)
54
+ table_names.each do |table_name|
55
+ connection.reset_pk_sequence!(table_name.tr('/', '_'))
56
+ end
57
+ end
58
+ end
59
+
60
+ cache_fixtures(connection, fixtures_map)
61
+ end
62
+ end
63
+ cached_fixtures(connection, table_names)
64
+ end
65
+
66
+ end
67
+
68
+ module TestFixtures
69
+ def setup_fixtures
70
+ return unless !ActiveRecord::Base.configurations.blank?
71
+
72
+ if pre_loaded_fixtures && !use_transactional_fixtures
73
+ raise RuntimeError, 'pre_loaded_fixtures requires use_transactional_fixtures'
74
+ end
75
+
76
+ @fixture_cache = {}
77
+ @fixture_connections = []
78
+ @@already_loaded_fixtures ||= {}
79
+
80
+ # Load fixtures once and begin transaction.
81
+ if run_in_transaction?
82
+ if @@already_loaded_fixtures[self.class]
83
+ @loaded_fixtures = @@already_loaded_fixtures[self.class]
84
+ else
85
+ @loaded_fixtures = load_fixtures
86
+ @@already_loaded_fixtures[self.class] = @loaded_fixtures
87
+ end
88
+ ActiveRecord::Base.force_connect_all_shards!
89
+ @fixture_connections = enlist_fixture_connections
90
+ @fixture_connections.each do |connection|
91
+ connection.increment_open_transactions
92
+ connection.transaction_joinable = false
93
+ connection.begin_db_transaction
94
+ end
95
+ # Load fixtures for every test.
96
+ else
97
+ ActiveRecord::Fixtures.reset_cache
98
+ @@already_loaded_fixtures[self.class] = nil
99
+ @loaded_fixtures = load_fixtures
100
+ end
101
+
102
+ # Instantiate fixtures for every test if requested.
103
+ instantiate_fixtures if use_instantiated_fixtures
104
+ end
105
+
106
+ def enlist_fixture_connections
107
+ ActiveRecord::Base.connection_handler.connection_pools.values.map(&:connection) +
108
+ ActiveRecord::Base.turntable_connections.values.map(&:connection)
109
+ end
110
+
111
+ def teardown_fixtures
112
+ return unless defined?(ActiveRecord) && !ActiveRecord::Base.configurations.blank?
113
+
114
+ unless run_in_transaction?
115
+ ActiveRecord::Fixtures.reset_cache
116
+ end
117
+
118
+ # Rollback changes if a transaction is active.
119
+ if run_in_transaction?
120
+ @fixture_connections.each do |connection|
121
+ if connection.open_transactions != 0
122
+ connection.rollback_db_transaction
123
+ connection.decrement_open_transactions
124
+ end
125
+ end
126
+ @fixture_connections.clear
127
+ end
128
+ ActiveRecord::Base.clear_active_connections!
129
+ end
130
+ end
131
+ end
@@ -0,0 +1,64 @@
1
+ module ActiveRecord::Turntable
2
+ module ActiveRecordExt
3
+ module LogSubscriber
4
+ extend ActiveSupport::Concern
5
+
6
+ included do
7
+ if ActiveRecord::VERSION::STRING < '3.1'
8
+ def sql(event)
9
+ self.class.runtime += event.duration
10
+ return unless logger.debug?
11
+
12
+ name = '%s (%.1fms)' % [event.payload[:name], event.duration]
13
+ shard = '[Shard: %s]' % (event.payload[:turntable_shard_name] ? event.payload[:turntable_shard_name] : nil)
14
+ sql = event.payload[:sql].squeeze(' ')
15
+
16
+ if odd?
17
+ name = color(name, ActiveRecord::LogSubscriber::CYAN, true)
18
+ shard = color(shard, ActiveRecord::LogSubscriber::CYAN, true)
19
+ sql = color(sql, nil, true)
20
+ else
21
+ name = color(name, ActiveRecord::LogSubscriber::MAGENTA, true)
22
+ shard = color(shard, ActiveRecord::LogSubscriber::MAGENTA, true)
23
+ end
24
+
25
+ debug " #{name} #{shard} #{sql}"
26
+ end
27
+
28
+ else
29
+ def sql(event)
30
+ self.class.runtime += event.duration
31
+ return unless logger.debug?
32
+
33
+ payload = event.payload
34
+
35
+ return if 'SCHEMA' == payload[:name]
36
+
37
+ name = '%s (%.1fms)' % [payload[:name], event.duration]
38
+ shard = '[Shard: %s]' % (event.payload[:turntable_shard_name] ? event.payload[:turntable_shard_name] : nil)
39
+ sql = payload[:sql].squeeze(' ')
40
+ binds = nil
41
+
42
+ unless (payload[:binds] || []).empty?
43
+ binds = " " + payload[:binds].map { |col,v|
44
+ [col.name, v]
45
+ }.inspect
46
+ end
47
+
48
+ if odd?
49
+ name = color(name, ActiveRecord::LogSubscriber::CYAN, true)
50
+ shard = color(shard, ActiveRecord::LogSubscriber::CYAN, true)
51
+ sql = color(sql, nil, true)
52
+ else
53
+ name = color(name, ActiveRecord::LogSubscriber::MAGENTA, true)
54
+ shard = color(shard, ActiveRecord::LogSubscriber::MAGENTA, true)
55
+ end
56
+
57
+ debug " #{name} #{shard} #{sql}#{binds}"
58
+ end
59
+ end
60
+ end
61
+ end
62
+ end
63
+ end
64
+