activerecord-turntable 1.1.2 → 2.0.0.rc1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.gitignore +5 -0
- data/.travis.yml +3 -5
- data/CHANGELOG.md +70 -0
- data/Guardfile +7 -5
- data/README.md +490 -0
- data/Rakefile +37 -22
- data/activerecord-turntable.gemspec +37 -34
- data/gemfiles/rails4_0.gemfile +6 -0
- data/gemfiles/rails4_1.gemfile +6 -0
- data/lib/active_record/turntable/active_record_ext/abstract_adapter.rb +14 -29
- data/lib/active_record/turntable/active_record_ext/activerecord_import_ext.rb +45 -0
- data/lib/active_record/turntable/active_record_ext/acts_as_archive_extension.rb +21 -0
- data/lib/active_record/turntable/active_record_ext/association.rb +85 -0
- data/lib/active_record/turntable/active_record_ext/association_preloader.rb +37 -0
- data/lib/active_record/turntable/active_record_ext/clever_load.rb +33 -76
- data/lib/active_record/turntable/active_record_ext/connection_handler_extension.rb +31 -0
- data/lib/active_record/turntable/active_record_ext/database_tasks.rb +81 -0
- data/lib/active_record/turntable/active_record_ext/fixtures.rb +54 -42
- data/lib/active_record/turntable/active_record_ext/locking_optimistic.rb +101 -0
- data/lib/active_record/turntable/active_record_ext/log_subscriber.rb +28 -46
- data/lib/active_record/turntable/active_record_ext/migration_proxy.rb +7 -0
- data/lib/active_record/turntable/active_record_ext/persistence.rb +96 -94
- data/lib/active_record/turntable/active_record_ext/relation.rb +31 -0
- data/lib/active_record/turntable/active_record_ext/schema_dumper.rb +18 -28
- data/lib/active_record/turntable/active_record_ext/transactions.rb +9 -3
- data/lib/active_record/turntable/active_record_ext.rb +26 -11
- data/lib/active_record/turntable/algorithm/base.rb +1 -1
- data/lib/active_record/turntable/algorithm/range_bsearch_algorithm.rb +1 -1
- data/lib/active_record/turntable/algorithm.rb +7 -3
- data/lib/active_record/turntable/base.rb +67 -14
- data/lib/active_record/turntable/cluster.rb +46 -2
- data/lib/active_record/turntable/config.rb +1 -1
- data/lib/active_record/turntable/connection_proxy/mixable.rb +7 -29
- data/lib/active_record/turntable/connection_proxy.rb +61 -72
- data/lib/active_record/turntable/error.rb +5 -6
- data/lib/active_record/turntable/helpers.rb +5 -1
- data/lib/active_record/turntable/migration.rb +9 -49
- data/lib/active_record/turntable/mixer/fader/calculate_shards_sum_result.rb +13 -2
- data/lib/active_record/turntable/mixer/fader/select_shards_merge_result.rb +17 -6
- data/lib/active_record/turntable/mixer/fader/specified_shard.rb +3 -1
- data/lib/active_record/turntable/mixer/fader.rb +12 -10
- data/lib/active_record/turntable/mixer.rb +59 -29
- data/lib/active_record/turntable/plugin.rb +6 -0
- data/lib/active_record/turntable/pool_proxy.rb +12 -19
- data/lib/active_record/turntable/rack/query_cache.rb +20 -23
- data/lib/active_record/turntable/rack.rb +4 -2
- data/lib/active_record/turntable/railtie.rb +4 -3
- data/lib/active_record/turntable/railties/databases.rake +81 -122
- data/lib/active_record/turntable/seq_shard.rb +1 -1
- data/lib/active_record/turntable/sequencer/api.rb +1 -1
- data/lib/active_record/turntable/sequencer/barrage.rb +28 -0
- data/lib/active_record/turntable/sequencer.rb +27 -9
- data/lib/active_record/turntable/shard.rb +2 -2
- data/lib/active_record/turntable/sql_tree_patch.rb +1 -1
- data/lib/active_record/turntable/version.rb +1 -1
- data/lib/active_record/turntable.rb +26 -16
- data/lib/generators/templates/turntable.yml +9 -7
- data/spec/active_record/turntable/active_record_ext/association_preloader_spec.rb +78 -0
- data/spec/active_record/turntable/active_record_ext/association_spec.rb +72 -0
- data/spec/active_record/turntable/active_record_ext/clever_load_spec.rb +25 -46
- data/spec/active_record/turntable/active_record_ext/locking_optimistic_spec.rb +28 -0
- data/spec/active_record/turntable/active_record_ext/persistence_spec.rb +46 -25
- data/spec/active_record/turntable/algorithm/range_algorithm_spec.rb +4 -4
- data/spec/active_record/turntable/algorithm/range_bsearch_algorithm_spec.rb +35 -0
- data/spec/active_record/turntable/algorithm_spec.rb +28 -12
- data/spec/active_record/turntable/base_spec.rb +1 -1
- data/spec/active_record/turntable/cluster_spec.rb +27 -5
- data/spec/active_record/turntable/config_spec.rb +2 -2
- data/spec/active_record/turntable/connection_proxy_spec.rb +112 -45
- data/spec/active_record/turntable/finder_spec.rb +24 -11
- data/spec/active_record/turntable/mixer_spec.rb +21 -21
- data/spec/active_record/turntable/rack/query_cache_spec.rb +19 -0
- data/spec/active_record/turntable/sequencer/api_spec.rb +38 -0
- data/spec/active_record/turntable/sequencer/barrage_spec.rb +22 -0
- data/spec/active_record/turntable/sequencer/mysql_spec.rb +22 -0
- data/spec/active_record/turntable/shard_spec.rb +1 -1
- data/spec/active_record/turntable/transaction_spec.rb +35 -0
- data/spec/active_record/turntable_spec.rb +4 -4
- data/spec/config/database.yml +24 -34
- data/spec/config/turntable.yml +18 -1
- data/spec/fabricators/turntable_fabricator.rb +0 -2
- data/spec/models/card.rb +3 -0
- data/spec/models/cards_user.rb +10 -0
- data/spec/models/cards_users_histories.rb +7 -0
- data/spec/models/events_users_history.rb +7 -0
- data/spec/models/user.rb +7 -0
- data/spec/models/user_status.rb +6 -0
- data/spec/spec_helper.rb +10 -4
- data/spec/support/matchers/be_saved_to.rb +6 -0
- data/spec/support/turntable_helper.rb +29 -0
- metadata +124 -74
- data/README.rdoc +0 -294
- data/gemfiles/rails3_0.gemfile +0 -7
- data/gemfiles/rails3_1.gemfile +0 -6
- data/gemfiles/rails3_2.gemfile +0 -6
- data/lib/active_record/turntable/compatible.rb +0 -19
- data/sample_app/.gitignore +0 -16
- data/sample_app/Gemfile +0 -41
- data/sample_app/README.rdoc +0 -261
- data/sample_app/Rakefile +0 -7
- data/sample_app/app/assets/images/rails.png +0 -0
- data/sample_app/app/assets/javascripts/application.js +0 -15
- data/sample_app/app/assets/stylesheets/application.css +0 -13
- data/sample_app/app/controllers/application_controller.rb +0 -3
- data/sample_app/app/helpers/application_helper.rb +0 -2
- data/sample_app/app/mailers/.gitkeep +0 -0
- data/sample_app/app/models/.gitkeep +0 -0
- data/sample_app/app/models/user.rb +0 -4
- data/sample_app/app/views/layouts/application.html.erb +0 -14
- data/sample_app/config/application.rb +0 -65
- data/sample_app/config/boot.rb +0 -6
- data/sample_app/config/database.yml +0 -70
- data/sample_app/config/environment.rb +0 -5
- data/sample_app/config/environments/development.rb +0 -37
- data/sample_app/config/environments/production.rb +0 -67
- data/sample_app/config/environments/test.rb +0 -37
- data/sample_app/config/initializers/backtrace_silencers.rb +0 -7
- data/sample_app/config/initializers/inflections.rb +0 -15
- data/sample_app/config/initializers/mime_types.rb +0 -5
- data/sample_app/config/initializers/secret_token.rb +0 -7
- data/sample_app/config/initializers/session_store.rb +0 -8
- data/sample_app/config/initializers/wrap_parameters.rb +0 -14
- data/sample_app/config/locales/en.yml +0 -5
- data/sample_app/config/routes.rb +0 -58
- data/sample_app/config/turntable.yml +0 -64
- data/sample_app/config.ru +0 -4
- data/sample_app/db/migrate/20120316073058_create_users.rb +0 -11
- data/sample_app/db/seeds.rb +0 -7
- data/sample_app/lib/assets/.gitkeep +0 -0
- data/sample_app/lib/tasks/.gitkeep +0 -0
- data/sample_app/log/.gitkeep +0 -0
- data/sample_app/public/404.html +0 -26
- data/sample_app/public/422.html +0 -26
- data/sample_app/public/500.html +0 -25
- data/sample_app/public/favicon.ico +0 -0
- data/sample_app/public/index.html +0 -241
- data/sample_app/public/robots.txt +0 -5
- data/sample_app/script/rails +0 -6
- data/sample_app/vendor/assets/javascripts/.gitkeep +0 -0
- data/sample_app/vendor/assets/stylesheets/.gitkeep +0 -0
- data/sample_app/vendor/plugins/.gitkeep +0 -0
- data/spec/test_models.rb +0 -27
- data/spec/turntable_helper.rb +0 -29
@@ -0,0 +1,81 @@
|
|
1
|
+
require 'active_record/tasks/database_tasks'
|
2
|
+
|
3
|
+
module ActiveRecord
|
4
|
+
module Tasks
|
5
|
+
module DatabaseTasks
|
6
|
+
def create_all_turntable_cluster
|
7
|
+
each_local_turntable_cluster_configuration { |name, configuration|
|
8
|
+
puts "[turntable] *** executing to database: #{configuration['database']}"
|
9
|
+
create configuration
|
10
|
+
}
|
11
|
+
end
|
12
|
+
|
13
|
+
def drop_all_turntable_cluster
|
14
|
+
each_local_turntable_cluster_configuration { |name, configuration|
|
15
|
+
puts "[turntable] *** executing to database: #{configuration['database']}"
|
16
|
+
drop configuration
|
17
|
+
}
|
18
|
+
end
|
19
|
+
|
20
|
+
def create_current_turntable_cluster(environment = env)
|
21
|
+
each_current_turntable_cluster_configuration(true, environment) { |name, configuration|
|
22
|
+
puts "[turntable] *** executing to database: #{configuration['database']}"
|
23
|
+
create configuration
|
24
|
+
}
|
25
|
+
ActiveRecord::Base.establish_connection environment.to_sym
|
26
|
+
end
|
27
|
+
|
28
|
+
def drop_current_turntable_cluster(environment = env)
|
29
|
+
each_current_turntable_cluster_configuration(true, environment) { |name, configuration|
|
30
|
+
puts "[turntable] *** executing to database: #{configuration['database']}"
|
31
|
+
drop configuration
|
32
|
+
}
|
33
|
+
end
|
34
|
+
|
35
|
+
def each_current_turntable_cluster_connected(with_test = false, environment = env)
|
36
|
+
each_current_turntable_cluster_configuration(with_test, environment) do |name, configuration|
|
37
|
+
ActiveRecord::Base.clear_active_connections!
|
38
|
+
ActiveRecord::Base.establish_connection(configuration)
|
39
|
+
ActiveRecord::Migration.current_shard = name
|
40
|
+
yield(name, configuration)
|
41
|
+
end
|
42
|
+
ActiveRecord::Base.clear_active_connections!
|
43
|
+
ActiveRecord::Base.establish_connection environment.to_sym
|
44
|
+
end
|
45
|
+
|
46
|
+
def each_current_turntable_cluster_configuration(with_test = false, environment = env)
|
47
|
+
environments = [environment]
|
48
|
+
environments << 'test' if with_test and environment == 'development'
|
49
|
+
|
50
|
+
current_turntable_cluster_configurations(*environments).each do |name, configuration|
|
51
|
+
yield(name, configuration) unless configuration['database'].blank?
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
def each_local_turntable_cluster_configuration
|
56
|
+
ActiveRecord::Base.configurations.keys.each do |k|
|
57
|
+
current_turntable_cluster_configurations(k).each do |name, configuration|
|
58
|
+
next if configuration['database'].blank?
|
59
|
+
|
60
|
+
if local_database?(configuration)
|
61
|
+
yield(name,configuration)
|
62
|
+
else
|
63
|
+
$stderr.puts "This task only modifies local databases. #{configuration['database']} is on a remote host."
|
64
|
+
end
|
65
|
+
end
|
66
|
+
end
|
67
|
+
end
|
68
|
+
|
69
|
+
def current_turntable_cluster_configurations(*environments)
|
70
|
+
configurations = []
|
71
|
+
environments.each do |environ|
|
72
|
+
config = ActiveRecord::Base.configurations[environ]
|
73
|
+
%w(shards seq).each do |name|
|
74
|
+
configurations += config[name].to_a
|
75
|
+
end
|
76
|
+
end
|
77
|
+
configurations
|
78
|
+
end
|
79
|
+
end
|
80
|
+
end
|
81
|
+
end
|
@@ -3,56 +3,52 @@
|
|
3
3
|
#
|
4
4
|
require 'active_record/fixtures'
|
5
5
|
module ActiveRecord
|
6
|
-
class
|
7
|
-
def self.create_fixtures(fixtures_directory, table_names, class_names = {})
|
8
|
-
|
9
|
-
|
10
|
-
class_names[n.tr('/', '_').to_sym] = n.classify if n.include?('/')
|
11
|
-
}
|
6
|
+
class FixtureSet
|
7
|
+
def self.create_fixtures(fixtures_directory, table_names, class_names = {}, config = ActiveRecord::Base)
|
8
|
+
fixture_set_names = Array(fixture_set_names).map(&:to_s)
|
9
|
+
class_names = class_names.stringify_keys
|
12
10
|
|
13
11
|
# FIXME: Apparently JK uses this.
|
14
12
|
connection = block_given? ? yield : ActiveRecord::Base.connection
|
15
13
|
|
16
|
-
files_to_read =
|
17
|
-
fixture_is_cached?(connection,
|
14
|
+
files_to_read = fixture_set_names.reject { |fs_name|
|
15
|
+
fixture_is_cached?(connection, fs_name)
|
18
16
|
}
|
19
17
|
|
20
18
|
unless files_to_read.empty?
|
21
19
|
connection.disable_referential_integrity do
|
22
20
|
fixtures_map = {}
|
23
21
|
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
fixtures_map[path] = ActiveRecord::Fixtures.new(
|
22
|
+
fixture_sets = files_to_read.map do |fs_name|
|
23
|
+
fixtures_map[fs_name] = new( # ActiveRecord::FixtureSet.new
|
28
24
|
connection,
|
29
|
-
|
30
|
-
class_names[
|
31
|
-
::File.join(fixtures_directory,
|
25
|
+
fs_name,
|
26
|
+
class_names[fs_name] || default_fixture_model_name(fs_name),
|
27
|
+
::File.join(fixtures_directory, fs_name))
|
32
28
|
end
|
33
29
|
|
34
30
|
all_loaded_fixtures.update(fixtures_map)
|
35
31
|
|
36
32
|
ActiveRecord::Turntable::Base.force_transaction_all_shards!(:requires_new => true) do
|
37
|
-
|
38
|
-
conn =
|
39
|
-
table_rows =
|
33
|
+
fixture_sets.each do |fs|
|
34
|
+
conn = fs.model_class.respond_to?(:connection) ? fs.model_class.connection : connection
|
35
|
+
table_rows = fs.table_rows
|
40
36
|
|
41
37
|
table_rows.keys.each do |table|
|
42
38
|
conn.delete "DELETE FROM #{conn.quote_table_name(table)}", 'Fixture Delete'
|
43
39
|
end
|
44
40
|
|
45
|
-
table_rows.each do |
|
41
|
+
table_rows.each do |fixture_set_name, rows|
|
46
42
|
rows.each do |row|
|
47
|
-
conn.insert_fixture(row,
|
43
|
+
conn.insert_fixture(row, fixture_set_name)
|
48
44
|
end
|
49
45
|
end
|
50
46
|
end
|
51
47
|
|
52
48
|
# Cap primary key sequences to max(pk).
|
53
49
|
if connection.respond_to?(:reset_pk_sequence!)
|
54
|
-
|
55
|
-
connection.reset_pk_sequence!(table_name
|
50
|
+
fixture_sets.each do |fs|
|
51
|
+
connection.reset_pk_sequence!(fs.table_name)
|
56
52
|
end
|
57
53
|
end
|
58
54
|
end
|
@@ -60,13 +56,12 @@ module ActiveRecord
|
|
60
56
|
cache_fixtures(connection, fixtures_map)
|
61
57
|
end
|
62
58
|
end
|
63
|
-
cached_fixtures(connection,
|
59
|
+
cached_fixtures(connection, fixture_set_names)
|
64
60
|
end
|
65
|
-
|
66
61
|
end
|
67
62
|
|
68
63
|
module TestFixtures
|
69
|
-
def setup_fixtures
|
64
|
+
def setup_fixtures(config = ActiveRecord::Base)
|
70
65
|
return unless !ActiveRecord::Base.configurations.blank?
|
71
66
|
|
72
67
|
if pre_loaded_fixtures && !use_transactional_fixtures
|
@@ -82,50 +77,67 @@ module ActiveRecord
|
|
82
77
|
if @@already_loaded_fixtures[self.class]
|
83
78
|
@loaded_fixtures = @@already_loaded_fixtures[self.class]
|
84
79
|
else
|
85
|
-
@loaded_fixtures =
|
80
|
+
@loaded_fixtures = turntable_load_fixtures(config)
|
86
81
|
@@already_loaded_fixtures[self.class] = @loaded_fixtures
|
87
82
|
end
|
88
83
|
ActiveRecord::Base.force_connect_all_shards!
|
89
84
|
@fixture_connections = enlist_fixture_connections
|
90
85
|
@fixture_connections.each do |connection|
|
91
|
-
connection.
|
92
|
-
connection.transaction_joinable = false
|
93
|
-
connection.begin_db_transaction
|
86
|
+
connection.begin_transaction joinable: false
|
94
87
|
end
|
95
|
-
|
88
|
+
# Load fixtures for every test.
|
96
89
|
else
|
97
90
|
ActiveRecord::Fixtures.reset_cache
|
98
91
|
@@already_loaded_fixtures[self.class] = nil
|
99
|
-
@loaded_fixtures =
|
92
|
+
@loaded_fixtures = turntable_load_fixtures(config)
|
100
93
|
end
|
101
94
|
|
102
95
|
# Instantiate fixtures for every test if requested.
|
103
|
-
|
96
|
+
turntable_instantiate_fixtures(config) if use_instantiated_fixtures
|
104
97
|
end
|
105
98
|
|
106
99
|
def enlist_fixture_connections
|
107
|
-
ActiveRecord::Base.connection_handler.
|
100
|
+
ActiveRecord::Base.connection_handler.connection_pool_list.map(&:connection) +
|
108
101
|
ActiveRecord::Base.turntable_connections.values.map(&:connection)
|
109
102
|
end
|
110
103
|
|
111
104
|
def teardown_fixtures
|
112
|
-
return
|
113
|
-
|
114
|
-
unless run_in_transaction?
|
115
|
-
ActiveRecord::Fixtures.reset_cache
|
116
|
-
end
|
105
|
+
return if ActiveRecord::Base.configurations.blank?
|
117
106
|
|
118
107
|
# Rollback changes if a transaction is active.
|
119
108
|
if run_in_transaction?
|
120
109
|
@fixture_connections.each do |connection|
|
121
|
-
if connection.
|
122
|
-
connection.rollback_db_transaction
|
123
|
-
connection.decrement_open_transactions
|
124
|
-
end
|
110
|
+
connection.rollback_transaction if connection.transaction_open?
|
125
111
|
end
|
126
112
|
@fixture_connections.clear
|
113
|
+
else
|
114
|
+
ActiveRecord::FixtureSet.reset_cache
|
127
115
|
end
|
116
|
+
|
128
117
|
ActiveRecord::Base.clear_active_connections!
|
129
118
|
end
|
119
|
+
|
120
|
+
private
|
121
|
+
|
122
|
+
def turntable_load_fixtures(config)
|
123
|
+
if ActiveRecord::Turntable.rails41_later?
|
124
|
+
load_fixtures(config)
|
125
|
+
elsif ActiveRecord::Turntable.rails4?
|
126
|
+
load_fixtures
|
127
|
+
else
|
128
|
+
raise NotImplementedError
|
129
|
+
end
|
130
|
+
end
|
131
|
+
|
132
|
+
def turntable_instantiate_fixtures(config)
|
133
|
+
if ActiveRecord::Turntable.rails41_later?
|
134
|
+
instantiate_fixtures(config)
|
135
|
+
elsif ActiveRecord::Turntable.rails4?
|
136
|
+
instantiate_fixtures
|
137
|
+
else
|
138
|
+
raise NotImplementedError
|
139
|
+
end
|
140
|
+
end
|
141
|
+
|
130
142
|
end
|
131
143
|
end
|
@@ -0,0 +1,101 @@
|
|
1
|
+
module ActiveRecord::Turntable::ActiveRecordExt
|
2
|
+
module LockingOptimistic
|
3
|
+
|
4
|
+
::ActiveRecord::Locking::Optimistic.class_eval do
|
5
|
+
|
6
|
+
ar_version = ActiveRecord::VERSION::STRING
|
7
|
+
if ar_version < "4.1"
|
8
|
+
method_name = ar_version =~ /\A4\.0\.[0-5]\z/ ? "update_record" : "_update_record"
|
9
|
+
|
10
|
+
class_eval <<-EOD
|
11
|
+
def #{method_name}(attribute_names = @attributes.keys) #:nodoc:
|
12
|
+
return super unless locking_enabled?
|
13
|
+
return 0 if attribute_names.empty?
|
14
|
+
|
15
|
+
klass = self.class
|
16
|
+
lock_col = self.class.locking_column
|
17
|
+
previous_lock_value = send(lock_col).to_i
|
18
|
+
increment_lock
|
19
|
+
|
20
|
+
attribute_names += [lock_col]
|
21
|
+
attribute_names.uniq!
|
22
|
+
|
23
|
+
begin
|
24
|
+
relation = self.class.unscoped
|
25
|
+
|
26
|
+
condition_scope = relation.where(
|
27
|
+
relation.table[self.class.primary_key].eq(id).and(
|
28
|
+
relation.table[lock_col].eq(self.class.quote_value(previous_lock_value, column_for_attribute(lock_col)))
|
29
|
+
)
|
30
|
+
)
|
31
|
+
if klass.turntable_enabled? and klass.primary_key != klass.turntable_shard_key.to_s
|
32
|
+
condition_scope = condition_scope.where(klass.turntable_shard_key => self.send(turntable_shard_key))
|
33
|
+
end
|
34
|
+
stmt = condition_scope.arel.compile_update(arel_attributes_with_values_for_update(attribute_names))
|
35
|
+
|
36
|
+
affected_rows = self.class.connection.update stmt
|
37
|
+
|
38
|
+
unless affected_rows == 1
|
39
|
+
raise ActiveRecord::StaleObjectError.new(self, "update")
|
40
|
+
end
|
41
|
+
|
42
|
+
affected_rows
|
43
|
+
|
44
|
+
# If something went wrong, revert the version.
|
45
|
+
rescue Exception
|
46
|
+
send(lock_col + '=', previous_lock_value)
|
47
|
+
raise
|
48
|
+
end
|
49
|
+
end
|
50
|
+
EOD
|
51
|
+
else
|
52
|
+
method_name = ar_version =~ /\A4\.1\.[01]\z/ ? "update_record" : "_update_record"
|
53
|
+
|
54
|
+
class_eval <<-EOD
|
55
|
+
def _update_record(attribute_names = @attributes.keys) #:nodoc:
|
56
|
+
return super unless locking_enabled?
|
57
|
+
return 0 if attribute_names.empty?
|
58
|
+
|
59
|
+
klass = self.class
|
60
|
+
lock_col = self.class.locking_column
|
61
|
+
previous_lock_value = send(lock_col).to_i
|
62
|
+
increment_lock
|
63
|
+
|
64
|
+
attribute_names += [lock_col]
|
65
|
+
attribute_names.uniq!
|
66
|
+
|
67
|
+
begin
|
68
|
+
relation = self.class.unscoped
|
69
|
+
|
70
|
+
condition_scope = relation.where(
|
71
|
+
relation.table[self.class.primary_key].eq(id).and(
|
72
|
+
relation.table[lock_col].eq(self.class.quote_value(previous_lock_value, column_for_attribute(lock_col)))
|
73
|
+
)
|
74
|
+
)
|
75
|
+
if klass.turntable_enabled? and klass.primary_key != klass.turntable_shard_key.to_s
|
76
|
+
condition_scope = condition_scope.where(klass.turntable_shard_key => self.send(turntable_shard_key))
|
77
|
+
end
|
78
|
+
stmt = condition_scope.arel.compile_update(
|
79
|
+
arel_attributes_with_values_for_update(attribute_names),
|
80
|
+
self.class.primary_key
|
81
|
+
)
|
82
|
+
|
83
|
+
affected_rows = self.class.connection.update stmt
|
84
|
+
|
85
|
+
unless affected_rows == 1
|
86
|
+
raise ActiveRecord::StaleObjectError.new(self, "update")
|
87
|
+
end
|
88
|
+
|
89
|
+
affected_rows
|
90
|
+
|
91
|
+
# If something went wrong, revert the version.
|
92
|
+
rescue Exception
|
93
|
+
send(lock_col + '=', previous_lock_value)
|
94
|
+
raise
|
95
|
+
end
|
96
|
+
end
|
97
|
+
EOD
|
98
|
+
end
|
99
|
+
end
|
100
|
+
end
|
101
|
+
end
|
@@ -1,64 +1,46 @@
|
|
1
|
+
require 'active_record/log_subscriber'
|
2
|
+
|
1
3
|
module ActiveRecord::Turntable
|
2
4
|
module ActiveRecordExt
|
3
5
|
module LogSubscriber
|
4
6
|
extend ActiveSupport::Concern
|
5
7
|
|
6
8
|
included do
|
7
|
-
|
8
|
-
|
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
|
9
|
+
alias_method_chain :sql, :turntable
|
10
|
+
end
|
27
11
|
|
28
|
-
|
29
|
-
def sql(event)
|
30
|
-
self.class.runtime += event.duration
|
31
|
-
return unless logger.debug?
|
12
|
+
protected
|
32
13
|
|
33
|
-
|
14
|
+
def sql_with_turntable(event)
|
15
|
+
self.class.runtime += event.duration
|
16
|
+
return unless logger.debug?
|
34
17
|
|
35
|
-
|
18
|
+
payload = event.payload
|
36
19
|
|
37
|
-
|
38
|
-
shard = '[Shard: %s]' % (event.payload[:turntable_shard_name] ? event.payload[:turntable_shard_name] : nil)
|
39
|
-
sql = payload[:sql].squeeze(' ')
|
40
|
-
binds = nil
|
20
|
+
return if ActiveRecord::LogSubscriber::IGNORE_PAYLOAD_NAMES.include?(payload[:name])
|
41
21
|
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
end
|
22
|
+
name = "#{payload[:name]} (#{event.duration.round(1)}ms)"
|
23
|
+
shard = '[Shard: %s]' % (event.payload[:turntable_shard_name] ? event.payload[:turntable_shard_name] : nil)
|
24
|
+
sql = payload[:sql].squeeze(' ')
|
25
|
+
binds = nil
|
47
26
|
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
name = color(name, ActiveRecord::LogSubscriber::MAGENTA, true)
|
54
|
-
shard = color(shard, ActiveRecord::LogSubscriber::MAGENTA, true)
|
55
|
-
end
|
27
|
+
unless (payload[:binds] || []).empty?
|
28
|
+
binds = " " + payload[:binds].map { |col,v|
|
29
|
+
render_bind(col, v)
|
30
|
+
}.inspect
|
31
|
+
end
|
56
32
|
|
57
|
-
|
58
|
-
|
33
|
+
if odd?
|
34
|
+
name = color(name, ActiveRecord::LogSubscriber::CYAN, true)
|
35
|
+
shard = color(shard, ActiveRecord::LogSubscriber::CYAN, true)
|
36
|
+
sql = color(sql, nil, true)
|
37
|
+
else
|
38
|
+
name = color(name, ActiveRecord::LogSubscriber::MAGENTA, true)
|
39
|
+
shard = color(shard, ActiveRecord::LogSubscriber::MAGENTA, true)
|
59
40
|
end
|
41
|
+
|
42
|
+
debug " #{name} #{shard} #{sql}#{binds}"
|
60
43
|
end
|
61
44
|
end
|
62
45
|
end
|
63
46
|
end
|
64
|
-
|
@@ -5,118 +5,120 @@ module ActiveRecord::Turntable::ActiveRecordExt
|
|
5
5
|
clear_aggregation_cache
|
6
6
|
clear_association_cache
|
7
7
|
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
else
|
23
|
-
block.call
|
24
|
-
end
|
8
|
+
finder_scope = if turntable_enabled? and self.class.primary_key != self.class.turntable_shard_key.to_s
|
9
|
+
self.class.unscoped.where(self.class.turntable_shard_key => self.send(turntable_shard_key))
|
10
|
+
else
|
11
|
+
self.class.unscoped
|
12
|
+
end
|
13
|
+
|
14
|
+
fresh_object =
|
15
|
+
if options && options[:lock]
|
16
|
+
finder_scope.lock.find(id)
|
17
|
+
else
|
18
|
+
finder_scope.find(id)
|
19
|
+
end
|
20
|
+
|
21
|
+
@attributes.update(fresh_object.instance_variable_get('@attributes'))
|
25
22
|
|
26
|
-
@
|
23
|
+
@column_types = self.class.column_types
|
24
|
+
@column_types_override = fresh_object.instance_variable_get('@column_types_override')
|
25
|
+
@attributes_cache = {}
|
27
26
|
self
|
28
27
|
end
|
29
|
-
end
|
30
28
|
|
31
|
-
|
32
|
-
|
33
|
-
def destroy
|
34
|
-
klass = self.class
|
35
|
-
if persisted?
|
36
|
-
condition_scope = klass.unscoped.where(klass.arel_table[klass.primary_key].eq(id))
|
37
|
-
if klass.turntable_enabled? and klass.primary_key != klass.turntable_shard_key.to_s
|
38
|
-
condition_scope = condition_scope.where(klass.turntable_shard_key => self.send(turntable_shard_key))
|
39
|
-
end
|
40
|
-
condition_scope.delete_all
|
41
|
-
end
|
29
|
+
def touch(name = nil)
|
30
|
+
raise ActiveRecordError, "can not touch on a new record object" unless persisted?
|
42
31
|
|
43
|
-
|
44
|
-
|
45
|
-
end
|
32
|
+
attributes = timestamp_attributes_for_update_in_model
|
33
|
+
attributes << name if name
|
46
34
|
|
47
|
-
|
35
|
+
unless attributes.empty?
|
36
|
+
current_time = current_time_from_proper_timezone
|
37
|
+
changes = {}
|
48
38
|
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
attributes_with_values = arel_attributes_values(false, false, attribute_names)
|
53
|
-
return 0 if attributes_with_values.empty?
|
54
|
-
condition_scope = klass.unscoped.where(klass.arel_table[klass.primary_key].eq(id))
|
55
|
-
if klass.turntable_enabled? and klass.primary_key != klass.turntable_shard_key.to_s
|
56
|
-
condition_scope = condition_scope.where(klass.turntable_shard_key => self.send(turntable_shard_key))
|
39
|
+
attributes.each do |column|
|
40
|
+
column = column.to_s
|
41
|
+
changes[column] = write_attribute(column, current_time)
|
57
42
|
end
|
58
|
-
|
43
|
+
|
44
|
+
changes[self.class.locking_column] = increment_lock if locking_enabled?
|
45
|
+
|
46
|
+
@changed_attributes.except!(*changes.keys)
|
47
|
+
primary_key = self.class.primary_key
|
48
|
+
|
49
|
+
finder_scope = if turntable_enabled? and primary_key != self.class.turntable_shard_key.to_s
|
50
|
+
self.class.unscoped.where(self.class.turntable_shard_key => self.send(turntable_shard_key))
|
51
|
+
else
|
52
|
+
self.class.unscoped
|
53
|
+
end
|
54
|
+
|
55
|
+
finder_scope.where(primary_key => self[primary_key]).update_all(changes) == 1
|
59
56
|
end
|
60
57
|
end
|
61
58
|
|
62
|
-
|
63
|
-
::ActiveRecord::Persistence.class_eval do
|
64
|
-
def destroy
|
65
|
-
klass = self.class
|
66
|
-
destroy_associations
|
67
|
-
|
68
|
-
if persisted?
|
69
|
-
ActiveRecord::IdentityMap.remove(self) if defined?(ActiveRecord::IdentityMap) and ActiveRecord::IdentityMap.enabled?
|
70
|
-
pk = klass.primary_key
|
71
|
-
column = klass.columns_hash[pk]
|
72
|
-
substitute = connection.substitute_at(column, 0)
|
73
|
-
|
74
|
-
relation = klass.unscoped.where(klass.arel_table[pk].eq(substitute))
|
75
|
-
if klass.turntable_enabled? and klass.primary_key != klass.turntable_shard_key.to_s
|
76
|
-
relation = relation.where(klass.turntable_shard_key => self.send(turntable_shard_key))
|
77
|
-
end
|
78
|
-
relation.bind_values = [[column, id]]
|
79
|
-
relation.delete_all
|
80
|
-
end
|
59
|
+
private
|
81
60
|
|
82
|
-
|
83
|
-
|
84
|
-
|
61
|
+
def relation_for_destroy
|
62
|
+
pk = self.class.primary_key
|
63
|
+
column = self.class.columns_hash[pk]
|
64
|
+
substitute = self.class.connection.substitute_at(column, 0)
|
65
|
+
klass = self.class
|
85
66
|
|
86
|
-
|
87
|
-
|
88
|
-
|
67
|
+
relation = self.class.unscoped.where(
|
68
|
+
self.class.arel_table[pk].eq(substitute))
|
69
|
+
if klass.turntable_enabled? and klass.primary_key != klass.turntable_shard_key.to_s
|
70
|
+
relation = relation.where(klass.turntable_shard_key => self.send(turntable_shard_key))
|
71
|
+
end
|
89
72
|
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
column = klass.columns_hash[pk]
|
94
|
-
substitute = connection.substitute_at(column, 0)
|
73
|
+
relation.bind_values = [[column, id]]
|
74
|
+
relation
|
75
|
+
end
|
95
76
|
|
96
|
-
|
97
|
-
|
98
|
-
|
77
|
+
ar_version = ActiveRecord::VERSION::STRING
|
78
|
+
if ar_version < "4.1"
|
79
|
+
method_name = ar_version =~ /\A4\.0\.[0-5]\z/ ? "update_record" : "_update_record"
|
80
|
+
class_eval <<-EOD
|
81
|
+
def #{method_name}(attribute_names = @attributes.keys)
|
82
|
+
attributes_with_values = arel_attributes_with_values_for_update(attribute_names)
|
83
|
+
if attributes_with_values.empty?
|
84
|
+
0
|
85
|
+
else
|
86
|
+
klass = self.class
|
87
|
+
column_hash = klass.connection.schema_cache.columns_hash klass.table_name
|
88
|
+
db_columns_with_values = attributes_with_values.map { |attr,value|
|
89
|
+
real_column = column_hash[attr.name]
|
90
|
+
[real_column, value]
|
91
|
+
}
|
92
|
+
bind_attrs = attributes_with_values.dup
|
93
|
+
bind_attrs.keys.each_with_index do |column, i|
|
94
|
+
real_column = db_columns_with_values[i].first
|
95
|
+
bind_attrs[column] = klass.connection.substitute_at(real_column, i)
|
96
|
+
end
|
97
|
+
condition_scope = klass.unscoped.where(klass.arel_table[klass.primary_key].eq(id_was || id))
|
98
|
+
if klass.turntable_enabled? and klass.primary_key != klass.turntable_shard_key.to_s
|
99
|
+
condition_scope = condition_scope.where(klass.turntable_shard_key => self.send(turntable_shard_key))
|
100
|
+
end
|
101
|
+
stmt = condition_scope.arel.compile_update(bind_attrs)
|
102
|
+
klass.connection.update stmt, 'SQL', db_columns_with_values
|
99
103
|
end
|
100
|
-
relation.bind_values = [[column, id]]
|
101
|
-
relation.delete_all
|
102
104
|
end
|
103
|
-
|
104
|
-
|
105
|
-
|
106
|
-
|
107
|
-
|
108
|
-
|
109
|
-
|
110
|
-
|
111
|
-
|
112
|
-
|
113
|
-
|
114
|
-
|
115
|
-
|
105
|
+
EOD
|
106
|
+
else
|
107
|
+
method_name = ar_version =~ /\A4\.1\.[01]\z/ ? "update_record" : "_update_record"
|
108
|
+
class_eval <<-EOD
|
109
|
+
def #{method_name}(attribute_names = @attributes.keys)
|
110
|
+
klass = self.class
|
111
|
+
attributes_values = arel_attributes_with_values_for_update(attribute_names)
|
112
|
+
if attributes_values.empty?
|
113
|
+
0
|
114
|
+
else
|
115
|
+
scope = if klass.turntable_enabled? and klass.primary_key != klass.turntable_shard_key.to_s
|
116
|
+
klass.unscoped.where(klass.turntable_shard_key => self.send(turntable_shard_key))
|
117
|
+
end
|
118
|
+
klass.unscoped.#{method_name} attributes_values, id, id_was, scope
|
119
|
+
end
|
116
120
|
end
|
117
|
-
|
118
|
-
klass.connection.update stmt.to_sql
|
119
|
-
end
|
121
|
+
EOD
|
120
122
|
end
|
121
123
|
end
|
122
124
|
end
|