ar-octopus 0.0.29 → 0.0.30
Sign up to get free protection for your applications and to get access to all the features.
- data/README.mkdn +4 -1
- data/Rakefile +1 -1
- data/TODO.txt +6 -0
- data/ar-octopus.gemspec +3 -7
- data/lib/octopus/model.rb +1 -1
- data/lib/octopus/proxy.rb +20 -8
- data/spec/config/shards.yml +3 -4
- data/spec/octopus/controller_spec.rb +2 -4
- data/spec/octopus/migration_spec.rb +1 -4
- data/spec/octopus/model_spec.rb +12 -0
- data/spec/octopus/replication_specs.rb +32 -78
- metadata +5 -9
- data/VERSION +0 -1
- data/doc/api.textile +0 -98
- data/doc/features.textile +0 -74
- data/doc/libraries.textile +0 -70
- data/doc/shards.yml +0 -76
data/README.mkdn
CHANGED
@@ -18,6 +18,9 @@ When using replication, all writes queries will be sent to master, and read quer
|
|
18
18
|
### Sharding
|
19
19
|
When using sharding, you need to specify what shard the query will be sent. Octopus support selecting the shard inside a controller, or manually in each object. More could be found at <a href="http://wiki.github.com/tchandy/octopus/sharding"> Wiki</a>
|
20
20
|
|
21
|
+
### Replication + Sharding
|
22
|
+
Replication + Sharding isn't supported yet. This is in our todo, and will be done ASAP. if you need, feel free to fork and implement it.
|
23
|
+
|
21
24
|
## Install
|
22
25
|
|
23
26
|
### Rails 2.x
|
@@ -115,7 +118,7 @@ If you want to send a specified action, or all actions from a controller, to a s
|
|
115
118
|
</pre>
|
116
119
|
|
117
120
|
To see the complete list of features and syntax, please check out our <a href="http://wiki.github.com/tchandy/octopus/"> Wiki</a>
|
118
|
-
Wanna see sample rails applications using octopus features? please check it out: <a href="http://github.com/tchandy/octopus_sharding_example">Sharding Example</a> and <a href="http://github.com/tchandy/octopus_replication_example">Replication Example</a>
|
121
|
+
Wanna see sample rails applications using octopus features? please check it out: <a href="http://github.com/tchandy/octopus_sharding_example">Sharding Example</a> and <a href="http://github.com/tchandy/octopus_replication_example">Replication Example</a>. Also, we have a example that shows how to use Octopus without Rails: <a href="http://github.com/tchandy/octopus_sinatra"> Octopus + Sinatra Example</a>.
|
119
122
|
|
120
123
|
|
121
124
|
## Contributing with Octopus
|
data/Rakefile
CHANGED
@@ -37,7 +37,7 @@ begin
|
|
37
37
|
gem.add_development_dependency "jeweler", ">= 1.4"
|
38
38
|
gem.add_development_dependency "actionpack", ">= 2.3"
|
39
39
|
gem.add_dependency('activerecord', '>= 2.3')
|
40
|
-
gem.version = "0.0.
|
40
|
+
gem.version = "0.0.30"
|
41
41
|
end
|
42
42
|
Jeweler::GemcutterTasks.new
|
43
43
|
rescue LoadError
|
data/TODO.txt
ADDED
@@ -0,0 +1,6 @@
|
|
1
|
+
- Find the best way to move data along shards.
|
2
|
+
- Improve documentation.
|
3
|
+
- Automatic configuration file for shards.
|
4
|
+
- Support specifics cases of sharding/replication.
|
5
|
+
- Basic algorithm to do load balancing between slaves server (handling servers down)
|
6
|
+
- Support replication + sharding
|
data/ar-octopus.gemspec
CHANGED
@@ -5,11 +5,11 @@
|
|
5
5
|
|
6
6
|
Gem::Specification.new do |s|
|
7
7
|
s.name = %q{ar-octopus}
|
8
|
-
s.version = "0.0.
|
8
|
+
s.version = "0.0.30"
|
9
9
|
|
10
10
|
s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
|
11
11
|
s.authors = ["Thiago Pradi", "Mike Perham"]
|
12
|
-
s.date = %q{2010-
|
12
|
+
s.date = %q{2010-08-14}
|
13
13
|
s.description = %q{This gem allows you to use sharded databases with ActiveRecord. this also provides a interface for replication and for running migrations with multiples shards.}
|
14
14
|
s.email = %q{tchandy@gmail.com}
|
15
15
|
s.extra_rdoc_files = [
|
@@ -22,12 +22,8 @@ Gem::Specification.new do |s|
|
|
22
22
|
"Gemfile.lock",
|
23
23
|
"README.mkdn",
|
24
24
|
"Rakefile",
|
25
|
-
"
|
25
|
+
"TODO.txt",
|
26
26
|
"ar-octopus.gemspec",
|
27
|
-
"doc/api.textile",
|
28
|
-
"doc/features.textile",
|
29
|
-
"doc/libraries.textile",
|
30
|
-
"doc/shards.yml",
|
31
27
|
"init.rb",
|
32
28
|
"lib/octopus.rb",
|
33
29
|
"lib/octopus/association.rb",
|
data/lib/octopus/model.rb
CHANGED
data/lib/octopus/proxy.rb
CHANGED
@@ -37,7 +37,11 @@ class Octopus::Proxy
|
|
37
37
|
|
38
38
|
def initialize_replication(config)
|
39
39
|
@replicated = true
|
40
|
-
|
40
|
+
if config.has_key?("fully_replicated")
|
41
|
+
@fully_replicated = config["fully_replicated"]
|
42
|
+
else
|
43
|
+
@fully_replicated = true
|
44
|
+
end
|
41
45
|
@slaves_list = @shards.keys.map {|sym| sym.to_s}.sort
|
42
46
|
@slaves_list.delete('master')
|
43
47
|
end
|
@@ -105,6 +109,16 @@ class Octopus::Proxy
|
|
105
109
|
ActiveRecord::Base.using(shard).connection.initialize_schema_migrations_table
|
106
110
|
end
|
107
111
|
end
|
112
|
+
|
113
|
+
def transaction(options = {}, &block)
|
114
|
+
if @replicated && (current_model.read_inheritable_attribute(:replicated) || @fully_replicated)
|
115
|
+
self.run_queries_on_shard(:master) do
|
116
|
+
select_connection.transaction(options, &block)
|
117
|
+
end
|
118
|
+
else
|
119
|
+
select_connection.transaction(options, &block)
|
120
|
+
end
|
121
|
+
end
|
108
122
|
|
109
123
|
def method_missing(method, *args, &block)
|
110
124
|
if should_clean_connection?(method)
|
@@ -142,16 +156,14 @@ class Octopus::Proxy
|
|
142
156
|
|
143
157
|
def send_queries_to_selected_slave(method, *args, &block)
|
144
158
|
old_shard = self.current_shard
|
145
|
-
|
146
|
-
if current_model.read_inheritable_attribute(:replicated) || @
|
147
|
-
|
148
|
-
|
149
|
-
@slaves_list << self.current_shard
|
150
|
-
end
|
159
|
+
|
160
|
+
if current_model.read_inheritable_attribute(:replicated) || @fully_replicated
|
161
|
+
self.current_shard = @slaves_list.shift.to_sym
|
162
|
+
@slaves_list << self.current_shard
|
151
163
|
else
|
152
164
|
self.current_shard = :master
|
153
165
|
end
|
154
|
-
|
166
|
+
|
155
167
|
sql = select_connection().send(method, *args, &block)
|
156
168
|
self.current_shard = old_shard
|
157
169
|
@using_enabled = nil
|
data/spec/config/shards.yml
CHANGED
@@ -12,7 +12,6 @@ octopus:
|
|
12
12
|
database: octopus_shard1
|
13
13
|
encoding: unicode
|
14
14
|
|
15
|
-
#TODO - FIX THIS
|
16
15
|
sqlite_shard:
|
17
16
|
adapter: sqlite3
|
18
17
|
database: /tmp/database.sqlite3
|
@@ -62,6 +61,8 @@ production_raise_error:
|
|
62
61
|
|
63
62
|
production_replicated:
|
64
63
|
replicated: true
|
64
|
+
fully_replicated: false
|
65
|
+
|
65
66
|
shards:
|
66
67
|
slave1:
|
67
68
|
adapter: mysql
|
@@ -81,9 +82,8 @@ production_replicated:
|
|
81
82
|
database: octopus_shard5
|
82
83
|
|
83
84
|
|
84
|
-
|
85
|
+
production_fully_replicated:
|
85
86
|
replicated: true
|
86
|
-
entire_replicated: true
|
87
87
|
shards:
|
88
88
|
slave1:
|
89
89
|
adapter: mysql
|
@@ -128,7 +128,6 @@ octopus_rails:
|
|
128
128
|
host: localhost
|
129
129
|
database: octopus_shard5
|
130
130
|
|
131
|
-
|
132
131
|
not_entire_sharded:
|
133
132
|
entire_sharded: false
|
134
133
|
shards:
|
@@ -78,7 +78,6 @@ describe Octopus::Migration do
|
|
78
78
|
it "should run writes on master when you use replication" do
|
79
79
|
using_enviroment :production_replicated do
|
80
80
|
migrating_to_version 10 do
|
81
|
-
Cat.using(:master).find_by_name("Replication").should_not be_nil
|
82
81
|
Cat.find_by_name("Replication").should be_nil
|
83
82
|
end
|
84
83
|
end
|
@@ -88,10 +87,8 @@ describe Octopus::Migration do
|
|
88
87
|
using_enviroment :production_replicated do
|
89
88
|
migrating_to_version 11 do
|
90
89
|
[:slave4, :slave1, :slave2, :slave3].each do |sym|
|
91
|
-
Cat.
|
90
|
+
Cat.find_by_name("Slaves").should_not be_nil
|
92
91
|
end
|
93
|
-
|
94
|
-
Cat.using(:master).find_by_name("Slaves").should be_nil
|
95
92
|
end
|
96
93
|
end
|
97
94
|
end
|
data/spec/octopus/model_spec.rb
CHANGED
@@ -36,6 +36,18 @@ describe Octopus::Model do
|
|
36
36
|
User.create!(:name => 'oi')
|
37
37
|
User.count.should == 1
|
38
38
|
end
|
39
|
+
|
40
|
+
it "should ensure that the connection will be cleaned" do
|
41
|
+
ActiveRecord::Base.connection.current_shard.should == :master
|
42
|
+
begin
|
43
|
+
Octopus.using(:canada) do
|
44
|
+
raise "Some Exception"
|
45
|
+
end
|
46
|
+
rescue
|
47
|
+
end
|
48
|
+
|
49
|
+
ActiveRecord::Base.connection.current_shard.should == :master
|
50
|
+
end
|
39
51
|
|
40
52
|
it "should allow creating more than one user" do
|
41
53
|
User.using(:canada).create([{ :name => 'America User 1' }, { :name => 'America User 2' }])
|
@@ -1,100 +1,54 @@
|
|
1
1
|
require File.expand_path(File.dirname(__FILE__) + '/../spec_helper')
|
2
2
|
|
3
3
|
describe "when the database is replicated" do
|
4
|
-
before(:each) do
|
5
|
-
Octopus.stub!(:env).and_return("production_replicated")
|
6
|
-
clean_connection_proxy()
|
7
|
-
end
|
8
|
-
|
9
4
|
it "should send all writes/reads queries to master when you have a non replicated model" do
|
10
|
-
|
11
|
-
|
12
|
-
|
5
|
+
using_enviroment :production_replicated do
|
6
|
+
u = User.create!(:name => "Replicated")
|
7
|
+
User.count.should == 1
|
8
|
+
User.find(u.id).should == u
|
9
|
+
end
|
13
10
|
end
|
14
|
-
|
11
|
+
|
15
12
|
it "should send all writes queries to master" do
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
it "should allow to send some queries to a selected slave" do
|
24
|
-
Cat.using(:slave4).create!([{:name => "Slave Cat 1"}, {:name => "Slave Cat 2"}])
|
25
|
-
Cat.using(:slave4).count.should == 2
|
13
|
+
using_enviroment :production_replicated do
|
14
|
+
Cat.create!(:name => "Slave Cat")
|
15
|
+
Cat.find_by_name("Slave Cat").should be_nil
|
16
|
+
Client.create!(:name => "Slave Client")
|
17
|
+
Client.find_by_name("Slave Client").should_not be_nil
|
18
|
+
end
|
26
19
|
end
|
27
20
|
|
28
|
-
it "should
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
Cat.using(sym).create!(:name => "Replicated_#{sym}")
|
21
|
+
it "should allow to create multiple models on the master" do
|
22
|
+
using_enviroment :production_replicated do
|
23
|
+
Cat.create!([{:name => "Slave Cat 1"}, {:name => "Slave Cat 2"}])
|
24
|
+
Cat.find_by_name("Slave Cat 1").should be_nil
|
25
|
+
Cat.find_by_name("Slave Cat 2").should be_nil
|
34
26
|
end
|
27
|
+
end
|
35
28
|
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
Cat.
|
40
|
-
|
41
|
-
|
42
|
-
Client.find(:first).should_not be_nil
|
43
|
-
Cat.find(:first).name.should == "Replicated_slave4"
|
44
|
-
Client.find(:first).should_not be_nil
|
45
|
-
Cat.find(:first).name.should == "Replicated_slave1"
|
46
|
-
Client.find(:first).should_not be_nil
|
47
|
-
|
48
|
-
[:slave4, :slave1, :slave2, :slave3].each do |sym|
|
49
|
-
Cat.using(sym).find_by_name("master").should be_false
|
50
|
-
end
|
29
|
+
it "should send the count query to a slave" do
|
30
|
+
pending()
|
31
|
+
# using_enviroment :production_replicated do
|
32
|
+
# Cat.create!(:name => "Slave Cat")
|
33
|
+
# Cat.count.should == 0
|
34
|
+
# end
|
51
35
|
end
|
52
36
|
end
|
53
37
|
|
54
38
|
|
55
39
|
describe "when the database is replicated and the entire application is replicated" do
|
56
40
|
before(:each) do
|
57
|
-
Octopus.stub!(:env).and_return("
|
41
|
+
Octopus.stub!(:env).and_return("production_fully_replicated")
|
58
42
|
clean_connection_proxy()
|
59
43
|
end
|
60
|
-
|
61
|
-
it "should send all writes queries to master" do
|
62
|
-
u = Client.using(:slave4).create!(:name => "Slave Client")
|
63
|
-
u1 = Client.using(:slave4).first
|
64
|
-
u1.name = "Client"
|
65
|
-
u1.save()
|
66
|
-
Client.using(:slave4).first.name.should == "Slave Client"
|
67
|
-
end
|
68
|
-
|
69
|
-
it "should send read queries to slaves,to all models, using a round robin algorithm" do
|
70
|
-
u = Cat.create!(:name => "master")
|
71
|
-
c = Client.create!(:name => "client_master")
|
72
|
-
|
73
|
-
[:slave4, :slave1, :slave2, :slave3].each do |sym|
|
74
|
-
Cat.using(sym).create!(:name => "Replicated_#{sym}")
|
75
|
-
end
|
76
|
-
|
77
|
-
[:slave4, :slave1, :slave2, :slave3].each do |sym|
|
78
|
-
Client.using(sym).create!(:name => "Replicated_#{sym}")
|
79
|
-
end
|
80
|
-
|
81
|
-
Client.find(:first).name.should == "Replicated_slave1"
|
82
|
-
Cat.find(:first).name.should == "Replicated_slave2"
|
83
|
-
Client.find(:first).name.should == "Replicated_slave3"
|
84
|
-
Cat.find(:first).name.should == "Replicated_slave4"
|
85
|
-
Cat.find(:first).name.should == "Replicated_slave1"
|
86
|
-
Client.find(:first).name.should == "Replicated_slave2"
|
87
|
-
Cat.find(:first).name.should == "Replicated_slave3"
|
88
|
-
Client.find(:first).name.should == "Replicated_slave4"
|
89
|
-
|
90
44
|
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
Client.
|
45
|
+
it "should send all writes queries to master" do
|
46
|
+
using_enviroment :production_fully_replicated do
|
47
|
+
Cat.create!(:name => "Slave Cat")
|
48
|
+
Cat.find_by_name("Slave Cat").should be_nil
|
49
|
+
Client.create!(:name => "Slave Client")
|
50
|
+
Client.find_by_name("Slave Client").should be_nil
|
97
51
|
end
|
98
|
-
end
|
52
|
+
end
|
99
53
|
end
|
100
54
|
|
metadata
CHANGED
@@ -1,13 +1,13 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: ar-octopus
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
hash:
|
4
|
+
hash: 35
|
5
5
|
prerelease: false
|
6
6
|
segments:
|
7
7
|
- 0
|
8
8
|
- 0
|
9
|
-
-
|
10
|
-
version: 0.0.
|
9
|
+
- 30
|
10
|
+
version: 0.0.30
|
11
11
|
platform: ruby
|
12
12
|
authors:
|
13
13
|
- Thiago Pradi
|
@@ -16,7 +16,7 @@ autorequire:
|
|
16
16
|
bindir: bin
|
17
17
|
cert_chain: []
|
18
18
|
|
19
|
-
date: 2010-
|
19
|
+
date: 2010-08-14 00:00:00 -03:00
|
20
20
|
default_executable:
|
21
21
|
dependencies:
|
22
22
|
- !ruby/object:Gem::Dependency
|
@@ -145,12 +145,8 @@ files:
|
|
145
145
|
- Gemfile.lock
|
146
146
|
- README.mkdn
|
147
147
|
- Rakefile
|
148
|
-
-
|
148
|
+
- TODO.txt
|
149
149
|
- ar-octopus.gemspec
|
150
|
-
- doc/api.textile
|
151
|
-
- doc/features.textile
|
152
|
-
- doc/libraries.textile
|
153
|
-
- doc/shards.yml
|
154
150
|
- init.rb
|
155
151
|
- lib/octopus.rb
|
156
152
|
- lib/octopus/association.rb
|
data/VERSION
DELETED
@@ -1 +0,0 @@
|
|
1
|
-
0.0.1
|
data/doc/api.textile
DELETED
@@ -1,98 +0,0 @@
|
|
1
|
-
h1. Api Design
|
2
|
-
|
3
|
-
* The API design should be simple as possible, so they will just have one method to change the shards, the method using, which will be different in the context that him get called. In controller, they will send all queries in the action or controller to a specified shard. So, if you want to get all of your application sharded, you need to do a before_filter in the application controller.
|
4
|
-
* The other method will be sharded_by, which selects the shard using a attribute of the model. This method is just accessible from class method on ActiveRecord::Base.
|
5
|
-
* The using method also could be called from models, as a scope, or receiving a block, that will run all the methods inside a specific shard.
|
6
|
-
* Also, the using method could be called from migrations. For default, migrations should run only in the master database, if you want to run in a different database, you should specify where you wish that migration run. like this:
|
7
|
-
<pre>
|
8
|
-
# This will run in all, master and slaves
|
9
|
-
class AddNameFieldToUser < ActiveRecord::Migration
|
10
|
-
using(:all)
|
11
|
-
#or
|
12
|
-
using_all
|
13
|
-
|
14
|
-
def self.up
|
15
|
-
add_column :users, :name, :string
|
16
|
-
end
|
17
|
-
|
18
|
-
def self.down
|
19
|
-
remove_column :users, :name, :string
|
20
|
-
end
|
21
|
-
end
|
22
|
-
|
23
|
-
|
24
|
-
# This will run just in all slaves
|
25
|
-
class AddNameFieldToUser < ActiveRecord::Migration
|
26
|
-
using(:slaves)
|
27
|
-
or
|
28
|
-
using_slaves()
|
29
|
-
|
30
|
-
def self.up
|
31
|
-
add_column :users, :name, :string
|
32
|
-
end
|
33
|
-
|
34
|
-
def self.down
|
35
|
-
remove_column :users, :name, :string
|
36
|
-
end
|
37
|
-
end
|
38
|
-
|
39
|
-
# This will run in all shards, slaves or not
|
40
|
-
class AddNameFieldToUser < ActiveRecord::Migration
|
41
|
-
using(:shards)
|
42
|
-
|
43
|
-
def self.up
|
44
|
-
add_column :users, :name, :string
|
45
|
-
end
|
46
|
-
|
47
|
-
def self.down
|
48
|
-
remove_column :users, :name, :string
|
49
|
-
end
|
50
|
-
end
|
51
|
-
|
52
|
-
# This will run in specific shard
|
53
|
-
class AddNameFieldToUser < ActiveRecord::Migration
|
54
|
-
using(:canada)
|
55
|
-
|
56
|
-
def self.up
|
57
|
-
add_column :users, :name, :string
|
58
|
-
end
|
59
|
-
|
60
|
-
def self.down
|
61
|
-
remove_column :users, :name, :string
|
62
|
-
end
|
63
|
-
end
|
64
|
-
|
65
|
-
# This will run in the specified shards, this accepts two or more shards.
|
66
|
-
class AddNameFieldToUser < ActiveRecord::Migration
|
67
|
-
using(:canada, :brazil)
|
68
|
-
|
69
|
-
def self.up
|
70
|
-
add_column :users, :name, :string
|
71
|
-
end
|
72
|
-
|
73
|
-
def self.down
|
74
|
-
remove_column :users, :name, :string
|
75
|
-
end
|
76
|
-
end
|
77
|
-
</pre>
|
78
|
-
* The default behavior on finds will be: if you find a object from a shard, like:
|
79
|
-
|
80
|
-
# This will find all users in brazil shard and iterate over it, saving all of them in the brazil shard
|
81
|
-
<pre>
|
82
|
-
User.using(:brazil).find(:all).each do |user|
|
83
|
-
user.name = "Brazil"
|
84
|
-
user.save()
|
85
|
-
end
|
86
|
-
</pre>
|
87
|
-
|
88
|
-
# This will find all users in brazil shard and iterate over it, saving all of them in the canada shard
|
89
|
-
<pre>
|
90
|
-
User.using(:brazil).find(:all).each do |user|
|
91
|
-
user.name = "Brazil"
|
92
|
-
user.using(:canada).save()
|
93
|
-
end
|
94
|
-
</pre>
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
|
data/doc/features.textile
DELETED
@@ -1,74 +0,0 @@
|
|
1
|
-
h1. Octopus Features List:
|
2
|
-
|
3
|
-
* Support for replicated databases, with one master and multiple slaves. Multi-master support could be added later. The writes will be sended to master and the reads to slave, but this could be modified.
|
4
|
-
* Support database sharding, with a nice and clean syntax like:
|
5
|
-
<pre>
|
6
|
-
User.using(:awesome_shard).all() or User.using_awesome_shard.all()
|
7
|
-
|
8
|
-
class User < ActiveRecord::Base
|
9
|
-
sharded_by :code
|
10
|
-
|
11
|
-
def awesome_method
|
12
|
-
#All queries inside the block will go to awesome_shard
|
13
|
-
using(:awesome_shard) do
|
14
|
-
Foo.all
|
15
|
-
Bar.all
|
16
|
-
end
|
17
|
-
end
|
18
|
-
end
|
19
|
-
|
20
|
-
class ApplicationController < ActionController::Base
|
21
|
-
around_filter :select_shard
|
22
|
-
|
23
|
-
def select_shard(&block)
|
24
|
-
using(current_user.city) do
|
25
|
-
yield
|
26
|
-
end
|
27
|
-
end
|
28
|
-
end
|
29
|
-
</pre>
|
30
|
-
* Running migrations between shards, example:
|
31
|
-
<pre>
|
32
|
-
class MyAwesomeMigration < ActiveRecord::Migration
|
33
|
-
using :awesome_shard
|
34
|
-
|
35
|
-
def self.up
|
36
|
-
create_table :users do |t|
|
37
|
-
t.string :name
|
38
|
-
end
|
39
|
-
end
|
40
|
-
|
41
|
-
def self.down
|
42
|
-
drop_table :users
|
43
|
-
end
|
44
|
-
end
|
45
|
-
</pre>
|
46
|
-
* The sharding config will be separated from the database.yml file, given a more cleaner and nice configuration file.
|
47
|
-
* An initial generator will be integrated in to the project, to help people using octopus, this will generate the config file: db/shards.yml, and a initializer to help the configuration. The example of this configuration is on this directory.
|
48
|
-
* Like the others implementation, the sharding will be selected using a Proxy class for ActiveRecord, where the class will require the connection, and the query will be executed in the selected shard.
|
49
|
-
* After this essentials feature, I will add a task to capistrano that allows you to generate the configuration and start the replication with one line command, reading the db/shards.yml configuration and running commands on the servers.
|
50
|
-
* In replication, all slaves will be trated as shards, but you will have to specify what should be used as slave. For default, replication will balance your read queries between the slaves, and your writes queries will goes to master. if you don't want this feature, just set the load_balancing to false, and specify what queries you want to goes to slaves, and what slave. with replication, you will have methods like:
|
51
|
-
<pre>
|
52
|
-
# This sends the queries to a random shard (support just read queries, not writes)
|
53
|
-
User.using_slaves().all()
|
54
|
-
# This sends all queries to master(read/write)
|
55
|
-
User.using_master().all()
|
56
|
-
# Same thing, but all users queries will be sent to master
|
57
|
-
class User < ActiveRecord::Base
|
58
|
-
using_master()
|
59
|
-
end
|
60
|
-
# Same thing, but all read queries will be sent to slaves
|
61
|
-
class User < ActiveRecord::Base
|
62
|
-
using_slaves()
|
63
|
-
end
|
64
|
-
|
65
|
-
#or
|
66
|
-
|
67
|
-
class User < ActiveRecord::Base
|
68
|
-
using(:master)
|
69
|
-
end
|
70
|
-
|
71
|
-
#Tip: you cannot name a shard as master or slaves, they are reserved words used for replication.
|
72
|
-
</pre>
|
73
|
-
|
74
|
-
* If you have multiple slaves, the load balancing will be did using round robin algorithm, sending the queries to the databases available. (This isn't the better algorithm, but it's easy to implement and works)
|
data/doc/libraries.textile
DELETED
@@ -1,70 +0,0 @@
|
|
1
|
-
h1. Masochism
|
2
|
-
|
3
|
-
p. Features:
|
4
|
-
* Support Multiple Database Support
|
5
|
-
* The config is stored in database.yml
|
6
|
-
* You could have a master and multiples slaves, but you couldn't change on the fly the shard. Ex: User.using(:awesome_shard)
|
7
|
-
|
8
|
-
p. Pros:
|
9
|
-
* Easy to use
|
10
|
-
* Test Coverage
|
11
|
-
|
12
|
-
p. Cons:
|
13
|
-
* Outdated (Lastest commit in January 12, 2009)
|
14
|
-
* Don't support running migrations on different shards.
|
15
|
-
* Don't support changing the shard on the fly
|
16
|
-
|
17
|
-
|
18
|
-
h1. DataFabric
|
19
|
-
|
20
|
-
p. Features:
|
21
|
-
* Support Multiple Database Support
|
22
|
-
* The config is stored in database.yml
|
23
|
-
* You could have data that are just sharded, not replicated.
|
24
|
-
* Support on the fly sharding selecting, with blocks
|
25
|
-
|
26
|
-
p. Pros:
|
27
|
-
* Easy to use and config
|
28
|
-
* Test Coverage
|
29
|
-
* Support just sharded, not replicated data
|
30
|
-
|
31
|
-
p. Cons:
|
32
|
-
* Don't support running migrations between shards.
|
33
|
-
* Don't support changing the sharding on the model, example: User.using(:awesome_shard)
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
h1. DbCharmer
|
38
|
-
|
39
|
-
p. Features:
|
40
|
-
* Support Multiple Database Support
|
41
|
-
* The config is stored in database.yml
|
42
|
-
* You could have a master and multiples slaves
|
43
|
-
* You could change the shard on the fly, with this syntax: User.switch_connection_to(:awesome_shard)
|
44
|
-
* You could run migrations over shards
|
45
|
-
* You could specify configurations of shards using ruby code
|
46
|
-
|
47
|
-
|
48
|
-
p. Pros:
|
49
|
-
* Support replication and sharding
|
50
|
-
* Support migrations between shards
|
51
|
-
* Supports on the Fly changing on the model
|
52
|
-
|
53
|
-
p. Cons:
|
54
|
-
* Didn't have test coverage in the plugin project, the tests are in another project.
|
55
|
-
* Weird and complicated syntax.
|
56
|
-
* Code are much more complicated than in the others project.
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
h1. DataMapper Sharding
|
61
|
-
|
62
|
-
p. Features:
|
63
|
-
* Support Multiple Database Support
|
64
|
-
* Syntax: DataMapper.setup(:external, 'mysql://someother_host/dm_core_test'); repository(:external) { Person.first }
|
65
|
-
|
66
|
-
h1. Multi-DB (http://github.com/schoefmax/multi_db)
|
67
|
-
|
68
|
-
p. Features:
|
69
|
-
* Support replication, with multiple slaves
|
70
|
-
* Load balancing between slaves
|
data/doc/shards.yml
DELETED
@@ -1,76 +0,0 @@
|
|
1
|
-
# The master database is the database settend in config/database.yml
|
2
|
-
# This supports both sharding and replication, you could select the shards, and for default just replicated
|
3
|
-
# database will be used, the master database is in database.yml and the slave will only be awesome_slave.
|
4
|
-
# Setting Load balancing for default will be true, octopus will send all your read queries to slaves, and writes queries to
|
5
|
-
# master. if you just want some queries to send to slaves, set it to false, and use the sintax User.using(:slave).
|
6
|
-
production:
|
7
|
-
replicated: true
|
8
|
-
|
9
|
-
shards:
|
10
|
-
awesome_slave:
|
11
|
-
adapter: mysql
|
12
|
-
username: user
|
13
|
-
password: pass
|
14
|
-
database: awesome_slave
|
15
|
-
host: 192.321.321.21
|
16
|
-
|
17
|
-
not_slave:
|
18
|
-
adapter: mysql
|
19
|
-
username: user
|
20
|
-
password: pass
|
21
|
-
database: awesome_slave
|
22
|
-
host: 192.321.321.18
|
23
|
-
|
24
|
-
# This is another example, not replicated.
|
25
|
-
production:
|
26
|
-
shards:
|
27
|
-
us:
|
28
|
-
adapter: mysql
|
29
|
-
username: user
|
30
|
-
password: pass
|
31
|
-
database: shard1
|
32
|
-
host: 192.321.321.19
|
33
|
-
canada:
|
34
|
-
adapter: mysql
|
35
|
-
username: user
|
36
|
-
password: pass
|
37
|
-
database: shard1
|
38
|
-
host: 192.321.321.17
|
39
|
-
brazil:
|
40
|
-
adapter: mysql
|
41
|
-
username: user
|
42
|
-
password: pass
|
43
|
-
database: shard1
|
44
|
-
host: 192.321.321.90
|
45
|
-
|
46
|
-
# This is a example using a group of shards:
|
47
|
-
production:
|
48
|
-
shards:
|
49
|
-
history_shards:
|
50
|
-
aug2009:
|
51
|
-
adapter: mysql
|
52
|
-
username: user
|
53
|
-
password: pass
|
54
|
-
database: aug2009
|
55
|
-
host: 192.321.321.21
|
56
|
-
oct2009:
|
57
|
-
adapter: mysql
|
58
|
-
username: user
|
59
|
-
password: pass
|
60
|
-
database: oct2009
|
61
|
-
host: 192.321.321.18
|
62
|
-
|
63
|
-
country_shards:
|
64
|
-
brazil:
|
65
|
-
adapter: mysql
|
66
|
-
username: user
|
67
|
-
password: pass
|
68
|
-
database: brazil
|
69
|
-
host: 192.321.321.21
|
70
|
-
slave: true
|
71
|
-
canada:
|
72
|
-
adapter: mysql
|
73
|
-
username: user
|
74
|
-
password: pass
|
75
|
-
database: canada
|
76
|
-
host: 192.321.321.18
|