ar-octopus 0.0.5 → 0.0.6
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.
- data/Rakefile +1 -1
- data/ar-octopus.gemspec +2 -1
- data/lib/octopus.rb +1 -0
- data/lib/octopus/association.rb +26 -2
- data/lib/octopus/controller.rb +1 -1
- data/lib/octopus/model.rb +33 -76
- data/lib/octopus/persistence.rb +27 -0
- data/lib/octopus/proxy.rb +18 -35
- data/spec/database_connection.rb +0 -1
- data/spec/octopus/model_spec.rb +1 -1
- data/spec/octopus/proxy_spec.rb +5 -5
- data/spec/octopus/replication_specs.rb +1 -1
- data/spec/octopus_helper.rb +1 -1
- metadata +4 -3
data/Rakefile
CHANGED
@@ -29,7 +29,7 @@ begin
|
|
29
29
|
gem.authors = ["Thiago Pradi", "Mike Perham", "Amit Agarwal"]
|
30
30
|
gem.add_development_dependency "rspec", ">= 1.2.9"
|
31
31
|
gem.add_dependency('activerecord')
|
32
|
-
gem.version = "0.0.
|
32
|
+
gem.version = "0.0.6"
|
33
33
|
end
|
34
34
|
Jeweler::GemcutterTasks.new
|
35
35
|
rescue LoadError
|
data/ar-octopus.gemspec
CHANGED
@@ -5,7 +5,7 @@
|
|
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.6"
|
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", "Amit Agarwal"]
|
@@ -32,6 +32,7 @@ Gem::Specification.new do |s|
|
|
32
32
|
"lib/octopus/has_and_belongs_to_many_association.rb",
|
33
33
|
"lib/octopus/migration.rb",
|
34
34
|
"lib/octopus/model.rb",
|
35
|
+
"lib/octopus/persistence.rb",
|
35
36
|
"lib/octopus/proxy.rb",
|
36
37
|
"rails/init.rb",
|
37
38
|
"spec/config/shards.yml",
|
data/lib/octopus.rb
CHANGED
data/lib/octopus/association.rb
CHANGED
@@ -1,4 +1,28 @@
|
|
1
1
|
module Octopus::Association
|
2
|
+
def has_many(association_id, options = {}, &extension)
|
3
|
+
default_octopus_opts(options)
|
4
|
+
super(association_id, options, &extension)
|
5
|
+
end
|
6
|
+
|
7
|
+
def has_and_belongs_to_many(association_id, options = {}, &extension)
|
8
|
+
default_octopus_opts(options)
|
9
|
+
super(association_id, options, &extension)
|
10
|
+
end
|
11
|
+
|
12
|
+
def default_octopus_opts(options)
|
13
|
+
if options[:before_add].is_a?(Array)
|
14
|
+
options[:before_add] << :set_connection
|
15
|
+
else
|
16
|
+
options[:before_add] = :set_connection
|
17
|
+
end
|
18
|
+
|
19
|
+
if options[:before_remove].is_a?(Array)
|
20
|
+
options[:before_remove] << :set_connection
|
21
|
+
else
|
22
|
+
options[:before_remove] = :set_connection
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
2
26
|
def collection_reader_method(reflection, association_proxy_class)
|
3
27
|
define_method(reflection.name) do |*params|
|
4
28
|
force_reload = params.first unless params.empty?
|
@@ -60,13 +84,13 @@ module Octopus::Association
|
|
60
84
|
|
61
85
|
def association_accessor_methods(reflection, association_proxy_class)
|
62
86
|
define_method(reflection.name) do |*params|
|
63
|
-
force_reload = true
|
64
87
|
reload_connection()
|
88
|
+
force_reload = params.first unless params.empty?
|
65
89
|
association = association_instance_get(reflection.name)
|
66
90
|
|
67
91
|
if association.nil? || force_reload
|
68
92
|
association = association_proxy_class.new(self, reflection)
|
69
|
-
retval = have_a_valid_shard? ? reflection.klass.uncached { self.class.connection_proxy.
|
93
|
+
retval = have_a_valid_shard? ? reflection.klass.uncached { self.class.connection_proxy.run_queries_on_shard(self.current_shard) { association.reload } } : association.reload
|
70
94
|
if retval.nil? and association_proxy_class == ActiveRecord::Associations::BelongsToAssociation
|
71
95
|
association_instance_set(reflection.name, nil)
|
72
96
|
return nil
|
data/lib/octopus/controller.rb
CHANGED
data/lib/octopus/model.rb
CHANGED
@@ -1,34 +1,30 @@
|
|
1
1
|
module Octopus::Model
|
2
2
|
def self.extended(base)
|
3
3
|
base.send(:include, InstanceMethods)
|
4
|
+
base.extend(ClassMethods)
|
4
5
|
base.hijack_connection()
|
5
6
|
end
|
6
7
|
|
7
|
-
module
|
8
|
-
def
|
9
|
-
|
10
|
-
end
|
11
|
-
|
12
|
-
def update_attribute(name, value)
|
13
|
-
reload_connection()
|
14
|
-
super(name, value)
|
15
|
-
end
|
16
|
-
|
17
|
-
def update_attributes(attributes)
|
18
|
-
reload_connection()
|
19
|
-
super(attributes)
|
8
|
+
module SharedMethods
|
9
|
+
def clean_table_name
|
10
|
+
self.reset_table_name() if self != ActiveRecord::Base && self.respond_to?(:reset_table_name)
|
20
11
|
end
|
12
|
+
|
13
|
+
def using(shard, &block)
|
14
|
+
hijack_connection()
|
15
|
+
clean_table_name()
|
21
16
|
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
17
|
+
if block_given?
|
18
|
+
self.connection.run_queries_on_shard(shard, &block)
|
19
|
+
else
|
20
|
+
hijack_initializer()
|
21
|
+
self.connection.current_shard = shard
|
22
|
+
self.connection.using_enabled = true
|
26
23
|
|
27
|
-
|
28
|
-
|
29
|
-
super
|
24
|
+
return self
|
25
|
+
end
|
30
26
|
end
|
31
|
-
|
27
|
+
|
32
28
|
def hijack_initializer()
|
33
29
|
attr_accessor :current_shard
|
34
30
|
after_initialize :set_current_shard
|
@@ -36,12 +32,10 @@ module Octopus::Model
|
|
36
32
|
before_destroy :set_connection
|
37
33
|
|
38
34
|
def set_current_shard
|
39
|
-
if
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
self.current_shard = self.class.connection_proxy.last_current_shard
|
44
|
-
end
|
35
|
+
if new_record?
|
36
|
+
self.current_shard = self.class.connection_proxy.current_shard
|
37
|
+
else
|
38
|
+
self.current_shard = self.class.connection_proxy.last_current_shard
|
45
39
|
end
|
46
40
|
end
|
47
41
|
end
|
@@ -58,67 +52,30 @@ module Octopus::Model
|
|
58
52
|
end
|
59
53
|
end
|
60
54
|
end
|
55
|
+
end
|
61
56
|
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
if block_given?
|
67
|
-
self.connection_proxy.run_query_on_shard(shard, &block)
|
68
|
-
else
|
69
|
-
hijack_initializer()
|
70
|
-
self.connection_proxy.current_shard = shard
|
71
|
-
self.connection_proxy.using_enabled = true
|
72
|
-
|
73
|
-
return self
|
74
|
-
end
|
75
|
-
end
|
76
|
-
|
77
|
-
def have_a_valid_shard?
|
78
|
-
self.respond_to?(:current_shard) && self.current_shard != nil
|
79
|
-
end
|
80
|
-
|
57
|
+
module InstanceMethods
|
58
|
+
include SharedMethods
|
59
|
+
|
81
60
|
def set_connection(*args)
|
82
61
|
if(args.size == 1)
|
83
62
|
arg = args.first
|
84
63
|
arg.current_shard = self.current_shard if arg.respond_to?(:current_shard) && have_a_valid_shard?
|
85
64
|
end
|
86
65
|
|
87
|
-
self.
|
66
|
+
self.connection.current_shard = self.current_shard if have_a_valid_shard?
|
88
67
|
end
|
89
|
-
|
90
|
-
def
|
91
|
-
self.
|
68
|
+
|
69
|
+
def have_a_valid_shard?
|
70
|
+
self.respond_to?(:current_shard) && self.current_shard != nil
|
92
71
|
end
|
93
72
|
end
|
94
73
|
|
95
|
-
|
96
|
-
|
97
|
-
def replicated_model()
|
98
|
-
write_inheritable_attribute(:replicated, true)
|
99
|
-
end
|
100
|
-
|
101
|
-
def has_many(association_id, options = {}, &extension)
|
102
|
-
default_octopus_opts(options)
|
103
|
-
super(association_id, options, &extension)
|
104
|
-
end
|
105
|
-
|
106
|
-
def has_and_belongs_to_many(association_id, options = {}, &extension)
|
107
|
-
default_octopus_opts(options)
|
108
|
-
super(association_id, options, &extension)
|
109
|
-
end
|
110
|
-
|
111
|
-
def default_octopus_opts(options)
|
112
|
-
if options[:before_add].is_a?(Array)
|
113
|
-
options[:before_add] << :set_connection
|
114
|
-
else
|
115
|
-
options[:before_add] = :set_connection
|
116
|
-
end
|
74
|
+
module ClassMethods
|
75
|
+
include SharedMethods
|
117
76
|
|
118
|
-
|
119
|
-
|
120
|
-
else
|
121
|
-
options[:before_remove] = :set_connection
|
77
|
+
def replicated_model()
|
78
|
+
write_inheritable_attribute(:replicated, true)
|
122
79
|
end
|
123
80
|
end
|
124
81
|
end
|
@@ -0,0 +1,27 @@
|
|
1
|
+
module Octopus::Persistence
|
2
|
+
def reload_connection()
|
3
|
+
set_connection() if have_a_valid_shard?
|
4
|
+
end
|
5
|
+
|
6
|
+
def update_attribute(name, value)
|
7
|
+
reload_connection()
|
8
|
+
super(name, value)
|
9
|
+
end
|
10
|
+
|
11
|
+
def update_attributes(attributes)
|
12
|
+
reload_connection()
|
13
|
+
super(attributes)
|
14
|
+
end
|
15
|
+
|
16
|
+
def update_attributes!(attributes)
|
17
|
+
reload_connection()
|
18
|
+
super(attributes)
|
19
|
+
end
|
20
|
+
|
21
|
+
def reload
|
22
|
+
reload_connection()
|
23
|
+
super
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
ActiveRecord::Base.send(:include, Octopus::Persistence)
|
data/lib/octopus/proxy.rb
CHANGED
@@ -1,25 +1,17 @@
|
|
1
|
-
require "set"
|
2
|
-
|
3
1
|
class Octopus::Proxy
|
4
|
-
attr_accessor :
|
5
|
-
|
6
|
-
delegate :increment_open_transactions, :decrement_open_transactions, :to => :select_connection
|
2
|
+
attr_accessor :current_model, :current_shard, :current_group, :block, :using_enabled, :last_current_shard
|
7
3
|
|
8
4
|
def initialize(config)
|
5
|
+
initialize_shards(config)
|
6
|
+
initialize_replication() if config[Octopus.env()]["replicated"]
|
7
|
+
end
|
8
|
+
|
9
|
+
def initialize_shards(config)
|
9
10
|
@shards = {}
|
10
11
|
@groups = {}
|
11
|
-
@replicated = config[Octopus.env()]["replicated"]
|
12
12
|
@shards[:master] = ActiveRecord::Base.connection_pool()
|
13
13
|
@current_shard = :master
|
14
14
|
|
15
|
-
initialize_shards(config)
|
16
|
-
|
17
|
-
if @replicated
|
18
|
-
initialize_replication()
|
19
|
-
end
|
20
|
-
end
|
21
|
-
|
22
|
-
def initialize_shards(config)
|
23
15
|
config[Octopus.env()]["shards"].each do |key, value|
|
24
16
|
if value.has_key?("adapter")
|
25
17
|
initialize_adapter(value['adapter'])
|
@@ -38,11 +30,10 @@ class Octopus::Proxy
|
|
38
30
|
end
|
39
31
|
|
40
32
|
def initialize_replication()
|
41
|
-
@
|
42
|
-
@slaves_list.
|
43
|
-
@slaves_list
|
33
|
+
@replicated = true
|
34
|
+
@slaves_list = @shards.keys.map {|sym| sym.to_s}.sort
|
35
|
+
@slaves_list.delete('master')
|
44
36
|
end
|
45
|
-
|
46
37
|
|
47
38
|
def current_shard=(shard_symbol)
|
48
39
|
if shard_symbol.is_a?(Array)
|
@@ -58,18 +49,14 @@ class Octopus::Proxy
|
|
58
49
|
if group_symbol.is_a?(Array)
|
59
50
|
group_symbol.each {|symbol| raise "Nonexistent Group Name: #{symbol}" if @groups[symbol].nil? }
|
60
51
|
else
|
61
|
-
raise "Nonexistent Group Name: #{group_symbol}" if @groups[group_symbol].nil?
|
52
|
+
raise "Nonexistent Group Name: #{group_symbol}" if @groups[group_symbol].nil?
|
62
53
|
end
|
63
54
|
|
64
55
|
@current_group = group_symbol
|
65
56
|
end
|
66
57
|
|
67
58
|
def current_model=(model)
|
68
|
-
|
69
|
-
@current_model = model.class
|
70
|
-
else
|
71
|
-
@current_model = model
|
72
|
-
end
|
59
|
+
@current_model = model.is_a?(ActiveRecord::Base) ? model.class : model
|
73
60
|
end
|
74
61
|
|
75
62
|
def select_connection()
|
@@ -77,11 +64,7 @@ class Octopus::Proxy
|
|
77
64
|
end
|
78
65
|
|
79
66
|
def shard_name
|
80
|
-
|
81
|
-
current_shard.first
|
82
|
-
else
|
83
|
-
current_shard
|
84
|
-
end
|
67
|
+
current_shard.is_a?(Array) ? current_shard.first : current_shard
|
85
68
|
end
|
86
69
|
|
87
70
|
def add_transaction_record(record)
|
@@ -97,7 +80,7 @@ class Octopus::Proxy
|
|
97
80
|
self.send_transaction_to_multiple_groups(options, &block)
|
98
81
|
elsif should_send_queries_to_a_group_of_shards?
|
99
82
|
self.send_transaction_to_multiple_shards(@groups[current_group], options, &block)
|
100
|
-
|
83
|
+
@current_group = nil
|
101
84
|
else
|
102
85
|
select_connection.transaction(options, &block)
|
103
86
|
end
|
@@ -123,10 +106,11 @@ class Octopus::Proxy
|
|
123
106
|
end
|
124
107
|
end
|
125
108
|
|
126
|
-
def
|
109
|
+
def run_queries_on_shard(shard, &block)
|
127
110
|
older_shard = self.current_shard
|
128
111
|
self.block = true
|
129
112
|
self.current_shard = shard
|
113
|
+
|
130
114
|
begin
|
131
115
|
yield
|
132
116
|
ensure
|
@@ -142,7 +126,6 @@ class Octopus::Proxy
|
|
142
126
|
|
143
127
|
def initialize_adapter(adapter)
|
144
128
|
begin
|
145
|
-
require 'rubygems'
|
146
129
|
gem "activerecord-#{adapter}-adapter"
|
147
130
|
require "active_record/connection_adapters/#{adapter}_adapter"
|
148
131
|
rescue LoadError
|
@@ -155,7 +138,7 @@ class Octopus::Proxy
|
|
155
138
|
end
|
156
139
|
|
157
140
|
def should_clean_connection?(method)
|
158
|
-
method.to_s =~ /insert|select/ && !should_send_queries_to_multiple_shards? && !self.current_group &&
|
141
|
+
method.to_s =~ /insert|select/ && !should_send_queries_to_multiple_shards? && !self.current_group && !@replicated
|
159
142
|
end
|
160
143
|
|
161
144
|
def should_send_queries_to_multiple_shards?
|
@@ -199,8 +182,8 @@ class Octopus::Proxy
|
|
199
182
|
|
200
183
|
if current_model.read_inheritable_attribute(:replicated)
|
201
184
|
if !using_enabled
|
202
|
-
self.current_shard = slaves_list.shift.to_sym
|
203
|
-
slaves_list << self.current_shard
|
185
|
+
self.current_shard = @slaves_list.shift.to_sym
|
186
|
+
@slaves_list << self.current_shard
|
204
187
|
end
|
205
188
|
else
|
206
189
|
self.current_shard = :master
|
data/spec/database_connection.rb
CHANGED
data/spec/octopus/model_spec.rb
CHANGED
@@ -145,7 +145,7 @@ describe Octopus::Model do
|
|
145
145
|
describe "#replicated_model method" do
|
146
146
|
it "should be replicated" do
|
147
147
|
using_enviroment :production_replicated do
|
148
|
-
ActiveRecord::Base.connection_proxy.replicated.should be_true
|
148
|
+
ActiveRecord::Base.connection_proxy.instance_variable_get(:@replicated).should be_true
|
149
149
|
end
|
150
150
|
end
|
151
151
|
|
data/spec/octopus/proxy_spec.rb
CHANGED
@@ -7,15 +7,15 @@ describe Octopus::Proxy do
|
|
7
7
|
|
8
8
|
describe "creating a new instance" do
|
9
9
|
it "should initialize all shards and groups" do
|
10
|
-
@proxy.shards.keys.to_set.should == [:postgresql_shard, :alone_shard, :aug2011, :canada, :brazil, :aug2009, :russia, :aug2010, :master].to_set
|
11
|
-
@proxy.groups.should == {:country_shards=>[:canada, :brazil, :russia], :history_shards=>[:aug2009, :aug2010, :aug2011]}
|
10
|
+
@proxy.instance_variable_get(:@shards).keys.to_set.should == [:postgresql_shard, :alone_shard, :aug2011, :canada, :brazil, :aug2009, :russia, :aug2010, :master].to_set
|
11
|
+
@proxy.instance_variable_get(:@groups).should == {:country_shards=>[:canada, :brazil, :russia], :history_shards=>[:aug2009, :aug2010, :aug2011]}
|
12
12
|
end
|
13
13
|
|
14
14
|
it "should initialize the block attribute as false" do
|
15
15
|
@proxy.block.should be_false
|
16
16
|
end
|
17
17
|
it "should initialize replicated attribute as false" do
|
18
|
-
@proxy.replicated.should be_false
|
18
|
+
@proxy.instance_variable_get(:@replicated).should be_false
|
19
19
|
end
|
20
20
|
|
21
21
|
describe "should raise error if you have duplicated shard names" do
|
@@ -48,12 +48,12 @@ describe Octopus::Proxy do
|
|
48
48
|
|
49
49
|
describe "should return the connection based on shard_name" do
|
50
50
|
it "when current_shard is empty" do
|
51
|
-
@proxy.select_connection().should == @proxy.shards[:master].connection()
|
51
|
+
@proxy.select_connection().should == @proxy.instance_variable_get(:@shards)[:master].connection()
|
52
52
|
end
|
53
53
|
|
54
54
|
it "when current_shard is a single shard" do
|
55
55
|
@proxy.current_shard = :canada
|
56
|
-
@proxy.select_connection().should == @proxy.shards[:canada].connection()
|
56
|
+
@proxy.select_connection().should == @proxy.instance_variable_get(:@shards)[:canada].connection()
|
57
57
|
end
|
58
58
|
end
|
59
59
|
end
|
@@ -10,7 +10,7 @@ describe "when the database is replicated" do
|
|
10
10
|
end
|
11
11
|
|
12
12
|
it "should initialize the list of shards" do
|
13
|
-
@proxy.slaves_list.should == ["slave1", "slave2", "slave3", "slave4"]
|
13
|
+
@proxy.instance_variable_get(:@slaves_list).should == ["slave1", "slave2", "slave3", "slave4"]
|
14
14
|
end
|
15
15
|
|
16
16
|
it "should send all writes/reads queries to master when you have a replicated model" do
|
data/spec/octopus_helper.rb
CHANGED
@@ -1,5 +1,5 @@
|
|
1
1
|
def clean_all_shards()
|
2
|
-
ActiveRecord::Base.using(:master).connection.shards.keys.each do |shard_symbol|
|
2
|
+
ActiveRecord::Base.using(:master).connection.instance_variable_get(:@shards).keys.each do |shard_symbol|
|
3
3
|
['schema_migrations', 'users', 'clients', 'cats', 'items', 'keyboards', 'computers', 'permissions_roles', 'roles', 'permissions', 'assignments', 'projects', 'programmers'].each do |tables|
|
4
4
|
ActiveRecord::Base.using(shard_symbol).connection.execute("DELETE FROM #{tables};")
|
5
5
|
end
|
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: 19
|
5
5
|
prerelease: false
|
6
6
|
segments:
|
7
7
|
- 0
|
8
8
|
- 0
|
9
|
-
-
|
10
|
-
version: 0.0.
|
9
|
+
- 6
|
10
|
+
version: 0.0.6
|
11
11
|
platform: ruby
|
12
12
|
authors:
|
13
13
|
- Thiago Pradi
|
@@ -75,6 +75,7 @@ files:
|
|
75
75
|
- lib/octopus/has_and_belongs_to_many_association.rb
|
76
76
|
- lib/octopus/migration.rb
|
77
77
|
- lib/octopus/model.rb
|
78
|
+
- lib/octopus/persistence.rb
|
78
79
|
- lib/octopus/proxy.rb
|
79
80
|
- rails/init.rb
|
80
81
|
- spec/config/shards.yml
|