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.
Files changed (144) hide show
  1. checksums.yaml +4 -4
  2. data/.gitignore +5 -0
  3. data/.travis.yml +3 -5
  4. data/CHANGELOG.md +70 -0
  5. data/Guardfile +7 -5
  6. data/README.md +490 -0
  7. data/Rakefile +37 -22
  8. data/activerecord-turntable.gemspec +37 -34
  9. data/gemfiles/rails4_0.gemfile +6 -0
  10. data/gemfiles/rails4_1.gemfile +6 -0
  11. data/lib/active_record/turntable/active_record_ext/abstract_adapter.rb +14 -29
  12. data/lib/active_record/turntable/active_record_ext/activerecord_import_ext.rb +45 -0
  13. data/lib/active_record/turntable/active_record_ext/acts_as_archive_extension.rb +21 -0
  14. data/lib/active_record/turntable/active_record_ext/association.rb +85 -0
  15. data/lib/active_record/turntable/active_record_ext/association_preloader.rb +37 -0
  16. data/lib/active_record/turntable/active_record_ext/clever_load.rb +33 -76
  17. data/lib/active_record/turntable/active_record_ext/connection_handler_extension.rb +31 -0
  18. data/lib/active_record/turntable/active_record_ext/database_tasks.rb +81 -0
  19. data/lib/active_record/turntable/active_record_ext/fixtures.rb +54 -42
  20. data/lib/active_record/turntable/active_record_ext/locking_optimistic.rb +101 -0
  21. data/lib/active_record/turntable/active_record_ext/log_subscriber.rb +28 -46
  22. data/lib/active_record/turntable/active_record_ext/migration_proxy.rb +7 -0
  23. data/lib/active_record/turntable/active_record_ext/persistence.rb +96 -94
  24. data/lib/active_record/turntable/active_record_ext/relation.rb +31 -0
  25. data/lib/active_record/turntable/active_record_ext/schema_dumper.rb +18 -28
  26. data/lib/active_record/turntable/active_record_ext/transactions.rb +9 -3
  27. data/lib/active_record/turntable/active_record_ext.rb +26 -11
  28. data/lib/active_record/turntable/algorithm/base.rb +1 -1
  29. data/lib/active_record/turntable/algorithm/range_bsearch_algorithm.rb +1 -1
  30. data/lib/active_record/turntable/algorithm.rb +7 -3
  31. data/lib/active_record/turntable/base.rb +67 -14
  32. data/lib/active_record/turntable/cluster.rb +46 -2
  33. data/lib/active_record/turntable/config.rb +1 -1
  34. data/lib/active_record/turntable/connection_proxy/mixable.rb +7 -29
  35. data/lib/active_record/turntable/connection_proxy.rb +61 -72
  36. data/lib/active_record/turntable/error.rb +5 -6
  37. data/lib/active_record/turntable/helpers.rb +5 -1
  38. data/lib/active_record/turntable/migration.rb +9 -49
  39. data/lib/active_record/turntable/mixer/fader/calculate_shards_sum_result.rb +13 -2
  40. data/lib/active_record/turntable/mixer/fader/select_shards_merge_result.rb +17 -6
  41. data/lib/active_record/turntable/mixer/fader/specified_shard.rb +3 -1
  42. data/lib/active_record/turntable/mixer/fader.rb +12 -10
  43. data/lib/active_record/turntable/mixer.rb +59 -29
  44. data/lib/active_record/turntable/plugin.rb +6 -0
  45. data/lib/active_record/turntable/pool_proxy.rb +12 -19
  46. data/lib/active_record/turntable/rack/query_cache.rb +20 -23
  47. data/lib/active_record/turntable/rack.rb +4 -2
  48. data/lib/active_record/turntable/railtie.rb +4 -3
  49. data/lib/active_record/turntable/railties/databases.rake +81 -122
  50. data/lib/active_record/turntable/seq_shard.rb +1 -1
  51. data/lib/active_record/turntable/sequencer/api.rb +1 -1
  52. data/lib/active_record/turntable/sequencer/barrage.rb +28 -0
  53. data/lib/active_record/turntable/sequencer.rb +27 -9
  54. data/lib/active_record/turntable/shard.rb +2 -2
  55. data/lib/active_record/turntable/sql_tree_patch.rb +1 -1
  56. data/lib/active_record/turntable/version.rb +1 -1
  57. data/lib/active_record/turntable.rb +26 -16
  58. data/lib/generators/templates/turntable.yml +9 -7
  59. data/spec/active_record/turntable/active_record_ext/association_preloader_spec.rb +78 -0
  60. data/spec/active_record/turntable/active_record_ext/association_spec.rb +72 -0
  61. data/spec/active_record/turntable/active_record_ext/clever_load_spec.rb +25 -46
  62. data/spec/active_record/turntable/active_record_ext/locking_optimistic_spec.rb +28 -0
  63. data/spec/active_record/turntable/active_record_ext/persistence_spec.rb +46 -25
  64. data/spec/active_record/turntable/algorithm/range_algorithm_spec.rb +4 -4
  65. data/spec/active_record/turntable/algorithm/range_bsearch_algorithm_spec.rb +35 -0
  66. data/spec/active_record/turntable/algorithm_spec.rb +28 -12
  67. data/spec/active_record/turntable/base_spec.rb +1 -1
  68. data/spec/active_record/turntable/cluster_spec.rb +27 -5
  69. data/spec/active_record/turntable/config_spec.rb +2 -2
  70. data/spec/active_record/turntable/connection_proxy_spec.rb +112 -45
  71. data/spec/active_record/turntable/finder_spec.rb +24 -11
  72. data/spec/active_record/turntable/mixer_spec.rb +21 -21
  73. data/spec/active_record/turntable/rack/query_cache_spec.rb +19 -0
  74. data/spec/active_record/turntable/sequencer/api_spec.rb +38 -0
  75. data/spec/active_record/turntable/sequencer/barrage_spec.rb +22 -0
  76. data/spec/active_record/turntable/sequencer/mysql_spec.rb +22 -0
  77. data/spec/active_record/turntable/shard_spec.rb +1 -1
  78. data/spec/active_record/turntable/transaction_spec.rb +35 -0
  79. data/spec/active_record/turntable_spec.rb +4 -4
  80. data/spec/config/database.yml +24 -34
  81. data/spec/config/turntable.yml +18 -1
  82. data/spec/fabricators/turntable_fabricator.rb +0 -2
  83. data/spec/models/card.rb +3 -0
  84. data/spec/models/cards_user.rb +10 -0
  85. data/spec/models/cards_users_histories.rb +7 -0
  86. data/spec/models/events_users_history.rb +7 -0
  87. data/spec/models/user.rb +7 -0
  88. data/spec/models/user_status.rb +6 -0
  89. data/spec/spec_helper.rb +10 -4
  90. data/spec/support/matchers/be_saved_to.rb +6 -0
  91. data/spec/support/turntable_helper.rb +29 -0
  92. metadata +124 -74
  93. data/README.rdoc +0 -294
  94. data/gemfiles/rails3_0.gemfile +0 -7
  95. data/gemfiles/rails3_1.gemfile +0 -6
  96. data/gemfiles/rails3_2.gemfile +0 -6
  97. data/lib/active_record/turntable/compatible.rb +0 -19
  98. data/sample_app/.gitignore +0 -16
  99. data/sample_app/Gemfile +0 -41
  100. data/sample_app/README.rdoc +0 -261
  101. data/sample_app/Rakefile +0 -7
  102. data/sample_app/app/assets/images/rails.png +0 -0
  103. data/sample_app/app/assets/javascripts/application.js +0 -15
  104. data/sample_app/app/assets/stylesheets/application.css +0 -13
  105. data/sample_app/app/controllers/application_controller.rb +0 -3
  106. data/sample_app/app/helpers/application_helper.rb +0 -2
  107. data/sample_app/app/mailers/.gitkeep +0 -0
  108. data/sample_app/app/models/.gitkeep +0 -0
  109. data/sample_app/app/models/user.rb +0 -4
  110. data/sample_app/app/views/layouts/application.html.erb +0 -14
  111. data/sample_app/config/application.rb +0 -65
  112. data/sample_app/config/boot.rb +0 -6
  113. data/sample_app/config/database.yml +0 -70
  114. data/sample_app/config/environment.rb +0 -5
  115. data/sample_app/config/environments/development.rb +0 -37
  116. data/sample_app/config/environments/production.rb +0 -67
  117. data/sample_app/config/environments/test.rb +0 -37
  118. data/sample_app/config/initializers/backtrace_silencers.rb +0 -7
  119. data/sample_app/config/initializers/inflections.rb +0 -15
  120. data/sample_app/config/initializers/mime_types.rb +0 -5
  121. data/sample_app/config/initializers/secret_token.rb +0 -7
  122. data/sample_app/config/initializers/session_store.rb +0 -8
  123. data/sample_app/config/initializers/wrap_parameters.rb +0 -14
  124. data/sample_app/config/locales/en.yml +0 -5
  125. data/sample_app/config/routes.rb +0 -58
  126. data/sample_app/config/turntable.yml +0 -64
  127. data/sample_app/config.ru +0 -4
  128. data/sample_app/db/migrate/20120316073058_create_users.rb +0 -11
  129. data/sample_app/db/seeds.rb +0 -7
  130. data/sample_app/lib/assets/.gitkeep +0 -0
  131. data/sample_app/lib/tasks/.gitkeep +0 -0
  132. data/sample_app/log/.gitkeep +0 -0
  133. data/sample_app/public/404.html +0 -26
  134. data/sample_app/public/422.html +0 -26
  135. data/sample_app/public/500.html +0 -25
  136. data/sample_app/public/favicon.ico +0 -0
  137. data/sample_app/public/index.html +0 -241
  138. data/sample_app/public/robots.txt +0 -5
  139. data/sample_app/script/rails +0 -6
  140. data/sample_app/vendor/assets/javascripts/.gitkeep +0 -0
  141. data/sample_app/vendor/assets/stylesheets/.gitkeep +0 -0
  142. data/sample_app/vendor/plugins/.gitkeep +0 -0
  143. data/spec/test_models.rb +0 -27
  144. data/spec/turntable_helper.rb +0 -29
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 2cf165fcac886a12b91e1c524f6e39909d771396
4
- data.tar.gz: c86ffe9b2f0a5e10718483199aa1aa9fbf7453f6
3
+ metadata.gz: 2d2ed24b4fc25e1ec4752797494ce5f7e01aba00
4
+ data.tar.gz: e9686336be4cac05c60c3d129d3b94c3d562674c
5
5
  SHA512:
6
- metadata.gz: 4f0f21e8874756dd0e7638759d1517a472a96a40fa7cadafe96399078a20598a7b5880b73f1da283784914bcdcbffbca839663693426a4aa039abb26ee4f2bb2
7
- data.tar.gz: 0e4c417bf23c6a4baf9a64fb0b72e175b44debfb9a9c7939cbf04cf1e04409e43383ee5e30a4b3f90235ccd329b172182667dd005c6fab66c57782d30d7d6213
6
+ metadata.gz: 948dc1c9c05cedd33df32a763b79a587ec7e748ef03e96b8b763b7e991716b82f619c2bfbb4e4980136ae507c78dda9e06b733768613ecca2468ce8bd33042f3
7
+ data.tar.gz: 887ec36800641c5f632acdc83cc8abcb22f428ee196743e1d64b8b371d93e99fedc776d4c4151b91b57fc8586da80f91692f75c5249a354b076a90060e423f6e
data/.gitignore CHANGED
@@ -26,3 +26,8 @@ TAGS
26
26
 
27
27
  # For rubinius:
28
28
  #*.rbc
29
+
30
+ # gtags
31
+ GTAGS
32
+ GRTAGS
33
+ GPATH
data/.travis.yml CHANGED
@@ -1,13 +1,11 @@
1
1
  language: ruby
2
2
  rvm:
3
- - 1.9.2
4
3
  - 1.9.3
5
4
  - 2.0.0
5
+ - 2.1.2
6
6
  gemfile:
7
- - gemfiles/rails3_0.gemfile
8
- - gemfiles/rails3_1.gemfile
9
- - gemfiles/rails3_2.gemfile
7
+ - gemfiles/rails4_0.gemfile
8
+ - gemfiles/rails4_1.gemfile
10
9
  before_script:
11
10
  - bundle exec rake turntable:db:reset
12
11
  script: bundle exec rake spec
13
-
data/CHANGELOG.md CHANGED
@@ -1,3 +1,73 @@
1
+ ## activerecord-turntable 2.0.0 (not released yet) ##
2
+
3
+ First release for ActiveRecord 4.x
4
+
5
+ ### Incompatible Changes
6
+
7
+ #### Support ActiveRecord 4.x, drop support AR 3.x
8
+
9
+ If you are using AR 3.x, use turntable 1.x.
10
+
11
+ #### Default, Migration will be executed to all shards
12
+
13
+ Migration had been executed to only master database on turntable 1.x.
14
+
15
+ On turntable 2.x, migration is executed to all shards as default behavior.
16
+
17
+ #### Multiple Sequencer
18
+
19
+ Multiple sequencers supported.
20
+
21
+ Please pass sequencer's name Symbol to `sequencer` DSL:
22
+
23
+ ```ruby
24
+ sequencer :user_seq
25
+ ```
26
+
27
+ ### Features
28
+
29
+ #### Barrage sequencer
30
+
31
+ Supported [barrage](http://github.com/drecom/barrage) gem as sequencer
32
+
33
+ #### Better association support
34
+
35
+ When using association(or association preloading), Turntable would add shard key condition to relation object if associated models has the same shard key name.
36
+
37
+ If two related models has different named keys(but same meaning), you can pass option `foreign_shard_key` to association option.
38
+
39
+ Example:
40
+
41
+ ```ruby
42
+ class UserReceivedItemHistory < ActiveRecord::Base
43
+ has_many :user, foreign_shard_key: :receiver_user_id
44
+ end
45
+ ```
46
+
47
+ #### Exception on performance notice
48
+
49
+ On development environment, you can receive exception about queries that may cause a performance problem.
50
+
51
+ Add follow option to `turntable.yml`:
52
+
53
+ * raise\_on\_not\_specified\_shard\_query(default: false)
54
+ * raise\_on\_not\_specified\_shard\_update(default: false)
55
+
56
+ #### Add cluster transaction
57
+
58
+ To create transaction to all shards on the cluster:
59
+
60
+ ```ruby
61
+ User.user_cluster_transaction do
62
+ # transaction opened on all shards in 'user_cluster'
63
+ end
64
+ ```
65
+
66
+ ### Bugfixes
67
+
68
+ * Fix thread-safety bug
69
+
70
+
1
71
  ## activerecord-turntable 1.1.2 ##
2
72
 
3
73
  * Increase sequence table performance (by hanabokuro)
data/Guardfile CHANGED
@@ -1,9 +1,11 @@
1
1
  # A sample Guardfile
2
2
  # More info at https://github.com/guard/guard#readme
3
3
 
4
- guard 'rspec', :version => 2 do
5
- watch(%r{^spec/.+_spec\.rb$})
6
- watch(%r{^lib/(.+)\.rb$}) { |m| "spec/lib/#{m[1]}_spec.rb" }
7
- watch('spec/spec_helper.rb') { "spec" }
4
+ guard 'rspec',
5
+ cmd: "bundle exec rspec",
6
+ all_after_pass: true,
7
+ all_on_start: true do
8
+ watch(%r{^spec/.+_spec\.rb$})
9
+ watch(%r{^lib/(.+)\.rb$}) { |m| "spec/#{m[1]}_spec.rb" }
10
+ watch('spec/spec_helper.rb') { "spec" }
8
11
  end
9
-
data/README.md ADDED
@@ -0,0 +1,490 @@
1
+ # ActiveRecord::Turntable
2
+
3
+ [![Gem Version](https://badge.fury.io/rb/activerecord-turntable.svg)](http://badge.fury.io/rb/activerecord-turntable)
4
+ [![Build Status](https://travis-ci.org/drecom/activerecord-turntable.svg?branch=master)](https://travis-ci.org/drecom/activerecord-turntable)
5
+ [![Dependency Status](https://gemnasium.com/drecom/activerecord-turntable.svg)](https://gemnasium.com/drecom/activerecord-turntable)
6
+ [![Coverage Status](https://coveralls.io/repos/drecom/activerecord-turntable/badge.png?branch=master)](https://coveralls.io/r/drecom/activerecord-turntable?branch=master)
7
+
8
+ ActiveRecord::Turntable is a database sharding extension for ActiveRecord.
9
+
10
+ ## Dependencies
11
+
12
+ activerecord(>=4.0.0)
13
+
14
+ if you are using activerecord 3.x, please use activerecord-turntable version 1.x.
15
+
16
+ ## Supported Database
17
+
18
+ Currently supports mysql only.
19
+
20
+ ## Installation
21
+
22
+ Add to Gemfile:
23
+
24
+ ```ruby
25
+ gem 'activerecord-turntable', '~> 2.0.0.beta'
26
+ ```
27
+
28
+ Run a bundle install:
29
+
30
+ ```ruby
31
+ bundle install
32
+ ```
33
+
34
+ Run install generator:
35
+
36
+ ```bash
37
+ bundle exec rails g active_record:turntable:install
38
+ ```
39
+
40
+ generator creates `#{Rails.root}/config/turntable.yml`
41
+
42
+ ## Terminologies
43
+
44
+ ### Shard
45
+
46
+ Shard is a database which is horizontal partitioned.
47
+
48
+ ### Cluster
49
+
50
+ Cluster of shards. i.e) set of userdb1, userdb2, userdb3
51
+ Shards in the same cluster should have the same schema structure.
52
+
53
+ ### Master
54
+
55
+ Default ActiveRecord::Base's connection.
56
+
57
+ ### Sequencer
58
+
59
+ Turntable's sequence system for clustered database.
60
+
61
+ This keeps primary key ids to be unique each shards.
62
+
63
+ ## Example
64
+
65
+ ### Example Databases Structure
66
+
67
+ One main database(default ActiveRecord::Base connection) and
68
+ three user databases sharded by user_id.
69
+
70
+ ```
71
+ +-------+
72
+ | App |
73
+ +-------+
74
+ |
75
+ +---------+---------+---------+---------+
76
+ | | | | |
77
+ `--------` `-------` `-------` `-------` `-------`
78
+ | Master | |UserDB1| |UserDB2| |UserDB3| | SeqDB |
79
+ `--------` `-------` `-------` `-------` `-------`
80
+
81
+ ```
82
+
83
+ ### Example Configuration
84
+
85
+ Edit turntable.yml and database.yml. See below example config.
86
+
87
+ * example turntable.yml
88
+
89
+ ```yaml
90
+ development:
91
+ clusters:
92
+ user_cluster: # <-- cluster name
93
+ algorithm: range_bsearch # <-- `range` or `range_bsearch`
94
+ seq:
95
+ user_seq: # <-- sequencer name
96
+ seq_type: mysql # <-- sequencer type
97
+ connection: user_seq_1 # <-- sequencer database connection setting
98
+ shards:
99
+ - connection: user_shard_1 # <-- shard name
100
+ less_than: 100 # <-- shard range(like mysql partitioning)
101
+ - connection: user_shard_2
102
+ less_than: 200
103
+ - connection: user_shard_3
104
+ less_than: 2000000000
105
+
106
+ ```
107
+
108
+ * database.yml
109
+
110
+ ```yaml
111
+ connection_spec: &spec
112
+ adapter: mysql2
113
+ encoding: utf8
114
+ reconnect: false
115
+ pool: 5
116
+ username: root
117
+ password: root
118
+ socket: /tmp/mysql.sock
119
+
120
+ development:
121
+ <<: *spec
122
+ database: sample_app_development
123
+ seq: # <-- sequence database definition
124
+ user_seq_1:
125
+ <<: *spec
126
+ database: sample_app_user_seq_development
127
+ shards: # <-- shards definition
128
+ user_shard_1:
129
+ <<: *spec
130
+ database: sample_app_user1_development
131
+ user_shard_2:
132
+ <<: *spec
133
+ database: sample_app_user2_development
134
+ user_shard_3:
135
+ <<: *spec
136
+ database: sample_app_user3_development
137
+ ```
138
+
139
+ ### Example Migration
140
+
141
+ Generate a model:
142
+
143
+ ```bash
144
+ bundle exec rails g model user name:string
145
+ ```
146
+
147
+ And Edit migration file:
148
+
149
+ ```ruby
150
+ class CreateUsers < ActiveRecord::Migration
151
+ # Specify cluster executes migration if you need.
152
+ # Default, migration would be executed to all databases.
153
+ # clusters :user_cluster
154
+
155
+ def change
156
+ create_table :users do |t|
157
+ t.string :name
158
+ t.timestamps
159
+ end
160
+ create_sequence_for(:users) # <-- create sequence table
161
+ end
162
+ end
163
+ ```
164
+
165
+ Then please execute rake tasks:
166
+
167
+ ```bash
168
+ bundle exec rake db:create
169
+ bundle exec rake db:migrate
170
+ ```
171
+
172
+ Those rake tasks would be executed to shards too.
173
+
174
+ ### Example Model
175
+
176
+ Add turntable [shard_key_name] to the model class:
177
+
178
+ ```ruby
179
+ class User < ActiveRecord::Base
180
+ turntable :user_cluster, :id
181
+ sequencer
182
+ has_one :status
183
+ end
184
+
185
+ class Status < ActiveRecord::Base
186
+ turntable :user_cluster, :user_id
187
+ sequencer
188
+ belongs_to :user
189
+ end
190
+ ```
191
+
192
+ ## Usage
193
+
194
+ ### Creating
195
+
196
+ ```
197
+ > User.create(name: "hoge")
198
+ (0.0ms) [Shard: user_seq_1] BEGIN
199
+ (0.3ms) [Shard: user_seq_1] UPDATE `users_id_seq` SET id=LAST_INSERT_ID(id+1)
200
+ (0.8ms) [Shard: user_seq_1] COMMIT
201
+ (0.1ms) [Shard: user_seq_1] SELECT LAST_INSERT_ID()
202
+ (0.1ms) [Shard: user_shard_1] BEGIN
203
+ [ActiveRecord::Turntable] Sending method: insert, sql: #<Arel::InsertManager:0x007f8503685b48>, shards: ["user_shard_1"]
204
+ SQL (0.8ms) [Shard: user_shard_1] INSERT INTO `users` (`created_at`, `id`, `name`, `updated_at`) VALUES ('2012-04-10 03:59:42', 2, 'hoge', '2012-04-10 03:59:42')
205
+ (0.4ms) [Shard: user_shard_1] COMMIT
206
+ => #<User id: 2, name: "hoge", created_at: "2012-04-10 03:59:42", updated_at: "2012-04-10 03:59:42">
207
+ ```
208
+
209
+ ### Retrieving
210
+
211
+ ```
212
+ > user = User.find(2)
213
+ [ActiveRecord::Turntable] Sending method: select_all, sql: #<Arel::SelectManager:0x007f850466e668>, shards: ["user_shard_1"]
214
+ User Load (0.3ms) [Shard: user_shard_1] SELECT `users`.* FROM `users` WHERE `users`.`id` = 2 LIMIT 1
215
+ => #<User id: 2, name: "hoge", created_at: "2012-04-10 03:59:42", updated_at: "2012-04-10 03:59:42">
216
+ ```
217
+
218
+ ### Updating
219
+
220
+ ```
221
+ > user.update_attributes(name: "hogefoo")
222
+ (0.1ms) [Shard: user_shard_1] BEGIN
223
+ [ActiveRecord::Turntable] Sending method: update, sql: UPDATE `users` SET `name` = 'hogefoo', `updated_at` = '2012-04-10 04:07:52' WHERE `users`.`id` = 2, shards: ["user_shard_1"]
224
+ (0.3ms) [Shard: user_shard_1] UPDATE `users` SET `name` = 'hogefoo', `updated_at` = '2012-04-10 04:07:52' WHERE `users`.`id` = 2
225
+ (0.8ms) [Shard: user_shard_1] COMMIT
226
+ => true
227
+ ```
228
+
229
+ ### Delete
230
+
231
+ ```
232
+ > user.destroy
233
+ (0.2ms) [Shard: user_shard_1] BEGIN
234
+ [ActiveRecord::Turntable] Sending method: delete, sql: #<Arel::DeleteManager:0x007f8503677ea8>, shards: ["user_shard_1"]
235
+ SQL (0.3ms) [Shard: user_shard_1] DELETE FROM `users` WHERE `users`.`id` = 2
236
+ (1.7ms) [Shard: user_shard_1] COMMIT
237
+ => #<User id: 2, name: "hogefoo", created_at: "2012-04-10 03:59:42", updated_at: "2012-04-10 04:07:52">
238
+ ```
239
+
240
+ ### Counting
241
+
242
+ ```
243
+ > User.count
244
+ [ActiveRecord::Turntable] Sending method: select_value, sql: #<Arel::SelectManager:0x007f9e82ccebb0>, shards: ["user_shard_1", "user_shard_2", "user_shard_3"]
245
+ (0.8ms) [Shard: user_shard_1] SELECT COUNT(*) FROM `users`
246
+ (0.3ms) [Shard: user_shard_2] SELECT COUNT(*) FROM `users`
247
+ (0.2ms) [Shard: user_shard_3] SELECT COUNT(*) FROM `users`
248
+ => 1
249
+ ```
250
+
251
+ ## Sequencer
252
+
253
+ Sequencer provides generating global IDs.
254
+
255
+ Turntable has follow 2 sequencers currently:
256
+
257
+ * :mysql - Use database table to generate ids.
258
+ * :barrage - Use [barrage](https://github.com/drecom/barrage) gem to generate ids
259
+
260
+ ### Mysql example
261
+
262
+ First, add configuration to turntable.yml and database.yml
263
+
264
+ * database.yml
265
+
266
+ ```yaml
267
+ development:
268
+ ...
269
+ seq: # <-- sequence database definition
270
+ user_seq_1:
271
+ <<: *spec
272
+ database: sample_app_user_seq_development
273
+ ```
274
+
275
+ * turntable.yml
276
+
277
+ ```yaml
278
+ development:
279
+ clusters:
280
+ user_cluster: # <-- cluster name
281
+ ....
282
+ seq:
283
+ user_seq: # <-- sequencer name
284
+ seq_type: mysql # <-- sequencer type
285
+ connection: user_seq_1 # <-- sequencer database connection
286
+ ```
287
+
288
+ Add below to the migration:
289
+
290
+ ```ruby
291
+ create_sequence_for(:users) # <-- this line creates sequence table named `users_id_seq`
292
+ ```
293
+
294
+ Next, add sequencer definition to the model:
295
+
296
+ ```ruby
297
+ class User < ActiveRecord::Base
298
+ turntable :id
299
+ sequencer :users_seq # <-- this line enables sequencer module
300
+ has_one :status
301
+ end
302
+ ```
303
+
304
+ ### Barrage example
305
+
306
+ First, add barrage gem to your Gemfile:
307
+
308
+ ```ruby
309
+ gem 'barrage'
310
+ ```
311
+
312
+ Then, add configuration to turntable.yml:
313
+
314
+ * turntable.yml
315
+
316
+ ```yaml
317
+ development:
318
+ clusters:
319
+ user_cluster: # <-- cluster name
320
+ ....
321
+ seq:
322
+ barrage_seq: # <-- sequencer name
323
+ seq_type: barrage # <-- sequencer type
324
+ options: # <-- options passed to barrage
325
+ generators:
326
+ - name: msec
327
+ length: 39 # MAX 17.4 years from start_at
328
+ start_at: 1396278000000 # 2014/04/01 00:00:00 JST
329
+ - name: redis_worker_id
330
+ length: 16
331
+ ttl: 300
332
+ redis:
333
+ host: '127.0.0.1'
334
+ - name: sequence
335
+ length: 9
336
+ ```
337
+
338
+ Next, add sequencer definition to the model:
339
+
340
+ ```ruby
341
+ class User < ActiveRecord::Base
342
+ turntable :id
343
+ sequencer :barrage_seq # <-- this line enables sequencer module
344
+ has_one :status
345
+ end
346
+ ```
347
+
348
+ ## Transactions
349
+ Turntable has some transaction support methods.
350
+
351
+ ### shards_transaction
352
+
353
+ Pass AR::Base instances, `shards_transaction` method suitable shards
354
+
355
+ ```ruby
356
+ user = User.find(2)
357
+ user3 = User.create(name: "hoge3")
358
+
359
+ User.shards_transaction([user, user3]) do
360
+ user.name = "hogehoge"
361
+ user3.name = "hogehoge3"
362
+ user.save!
363
+ user3.save!
364
+ end
365
+ ```
366
+
367
+ ### cluster_transaction
368
+
369
+ transaction helper to execute transaction to all shards in the cluster:
370
+
371
+ ```ruby
372
+ User.user_cluster_transaction do
373
+ # Transaction is opened all shards in "user_cluster"
374
+ end
375
+ ```
376
+
377
+ ### Migration
378
+
379
+ If you specify cluster or shard, migration will be executed to the cluster(or shard) and master database.
380
+
381
+ Default, migrations will be executed to all databases.
382
+
383
+ to specify cluster:
384
+
385
+ ```ruby
386
+ class CreateUsers < ActiveRecord::Migration
387
+ clusters :user_cluster
388
+ ....
389
+ end
390
+ ```
391
+
392
+ to specify shard:
393
+
394
+ ```ruby
395
+ class CreateUsers < ActiveRecord::Migration
396
+ shards :user_shard_01
397
+ ....
398
+ end
399
+ ```
400
+
401
+ ## Limitations
402
+
403
+ * Queries includes "ORDER BY", "GROUP BY" and "LIMIT" clauses cannot be distributed.
404
+ * "has many through" and "habtm" relationships may causes wrong results. ex) `User-Friend-User` relation
405
+
406
+
407
+ ## TIPS
408
+
409
+ ### Send query to a specific shard.
410
+
411
+ Use `with_shard` method:
412
+
413
+ ```ruby
414
+ AR::Base.connection.with_shard(shard1) do
415
+ # something queries to shard1
416
+ end
417
+ ```
418
+
419
+ To access shard objects, use below:
420
+
421
+ * AR::Base.connection.shards # \\{shard_name => shard_obj,....}
422
+ * AR::Base#turntable_shard # Returns current object's shard
423
+ * AR::Base.connection.select_shard(shard_key_value) #=> shard
424
+
425
+ ### Send query to all shards
426
+
427
+ Use with_all method:
428
+
429
+ ```ruby
430
+ User.connection.with_all do
431
+ User.order("created_at DESC").limit(3).all
432
+ end
433
+ ```
434
+
435
+ ### Connection Management
436
+
437
+ Rails's ConnectionManagement middleware keeps ActiveRecord's connection during the process is alive, but Turntable keeps more connections.
438
+ This may cause flooding max connections on your database. So, we made a middleware that disconnects on each request.
439
+
440
+ if you use turntable's ConnectionManagement middleware, add below line to your initializer.
441
+
442
+ ```ruby
443
+ app.middleware.swap ActiveRecord::ConnectionAdapters::ConnectionManagement, ActiveRecord::Turntable::Rack::ConnectionManagement
444
+ ```
445
+
446
+ ### Performance Exception
447
+
448
+ To notice queries causing performance problem, Turntable has follow options.
449
+
450
+ * raise\_on\_not\_specified\_shard\_query - raises on queries execute on all shards
451
+ * raise\_on\_not\_specified\_shard\_update - raises on updates executed on all shards
452
+
453
+
454
+ Add to turntable.yml:
455
+
456
+ ```yaml
457
+ development:
458
+ ....
459
+ raise_on_not_specified_shard_query: true
460
+ raise_on_not_specified_shard_update: true
461
+ ```
462
+
463
+ ## Thanks
464
+
465
+ ConnectionProxy, Distributed Migration implementation is inspired by Octopus and DataFabric.
466
+
467
+ ## License
468
+
469
+ activerecord-turntable is released under the MIT license:
470
+
471
+ Copyright (c) 2012 Drecom Co.,Ltd.
472
+
473
+ Permission is hereby granted, free of charge, to any person obtaining
474
+ a copy of this software and associated documentation files (the
475
+ "Software"), to deal in the Software without restriction, including
476
+ without limitation the rights to use, copy, modify, merge, publish,
477
+ distribute, sublicense, and/or sell copies of the Software, and to
478
+ permit persons to whom the Software is furnished to do so, subject to
479
+ the following conditions:
480
+
481
+ The above copyright notice and this permission notice shall be
482
+ included in all copies or substantial portions of the Software.
483
+
484
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
485
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
486
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
487
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
488
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
489
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
490
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.