nightcrawler 0.0.1 → 0.0.2
Sign up to get free protection for your applications and to get access to all the features.
- data/.gitignore +1 -0
- data/lib/nightcrawler/mosaic.rb +20 -0
- data/lib/nightcrawler/version.rb +1 -1
- data/spec/mosaic_spec.rb +101 -0
- data/spec/shard_builder_spec.rb +88 -0
- data/spec/support/shard_builder.rb +47 -0
- metadata +9 -3
data/.gitignore
CHANGED
@@ -0,0 +1,20 @@
|
|
1
|
+
## each instance controls the complete set of similar shards
|
2
|
+
#class Mosaic
|
3
|
+
|
4
|
+
## a parent namespace for all our sharded models
|
5
|
+
#module ShardPool; end
|
6
|
+
|
7
|
+
## name should be unique across mosaics
|
8
|
+
#def initialize(name, keys_configs, &block=nil)
|
9
|
+
#@name = name
|
10
|
+
#@keys_configs = keys_configs
|
11
|
+
#@block = block
|
12
|
+
#@shards = {}
|
13
|
+
#@keys_configs.each do |k,v|
|
14
|
+
#@shard_klass[k] =
|
15
|
+
#end
|
16
|
+
|
17
|
+
|
18
|
+
#end
|
19
|
+
|
20
|
+
|
data/lib/nightcrawler/version.rb
CHANGED
data/spec/mosaic_spec.rb
ADDED
@@ -0,0 +1,101 @@
|
|
1
|
+
#require 'spec_helper'
|
2
|
+
|
3
|
+
#describe Mosaic do
|
4
|
+
#before(:each) do
|
5
|
+
#@builder = ShardBuilder.new(3)
|
6
|
+
#shard_configs = (1..3).inject({}) {|h,i| h.merge "shard#{i}".to_sym => @builder.config(i)}
|
7
|
+
#Mosaic.new(shard_configs) do
|
8
|
+
#class Parent < Shard; end
|
9
|
+
#class Child < Shard; end
|
10
|
+
#end
|
11
|
+
#end
|
12
|
+
|
13
|
+
#it "should provide the correct model for the given shard" do
|
14
|
+
#(1..3).each do |i|
|
15
|
+
#Parent.shard("shard#{i}".to_sym).first.name.split(']')[0].should== "[shard ##{i}"
|
16
|
+
#end
|
17
|
+
#end
|
18
|
+
|
19
|
+
#it "should provide the correct model for the given shard" do
|
20
|
+
#Mosaic.shards.each do |shard|
|
21
|
+
#shard::Parent.first.name.split(']')[0].should== "[shard ##{shard.key}"
|
22
|
+
#end
|
23
|
+
#end
|
24
|
+
#end
|
25
|
+
|
26
|
+
#class Comment < ActiveRecord::Base
|
27
|
+
#include Nightcrawler::Sharding
|
28
|
+
#establish_connection :adapter => "sqlite3", :database => File.expand_path(File.join(File.dirname(__FILE__), "db", "master.db"))
|
29
|
+
|
30
|
+
#validates_presence_of :shard_key
|
31
|
+
|
32
|
+
#shard_by :shard_key
|
33
|
+
|
34
|
+
#def self.find_shard(value)
|
35
|
+
#"shard#{value}".to_sym
|
36
|
+
#end
|
37
|
+
|
38
|
+
#def self.shard_for(key)
|
39
|
+
#$shards[key]
|
40
|
+
#end
|
41
|
+
|
42
|
+
#end
|
43
|
+
|
44
|
+
|
45
|
+
#@shard_builder = TestShardBuilder.new(4)
|
46
|
+
|
47
|
+
#before(:each) do
|
48
|
+
#@shard_builder.build
|
49
|
+
#end
|
50
|
+
|
51
|
+
#after(:each) do
|
52
|
+
#@shard_builder.destroy
|
53
|
+
#end
|
54
|
+
|
55
|
+
|
56
|
+
|
57
|
+
#it "should do something" do
|
58
|
+
#Comment.shard(:shard1).should == CommentShard1
|
59
|
+
#end
|
60
|
+
#it "should create second shard" do
|
61
|
+
#Comment.shard(:shard2).should == CommentShard2
|
62
|
+
#end
|
63
|
+
|
64
|
+
#describe "creating a comment" do
|
65
|
+
#before(:all) do
|
66
|
+
#@comment = Comment.shard(:shard1).create!(:shard_key => 1, :comment => "comment number 1")
|
67
|
+
#end
|
68
|
+
#after(:all) do
|
69
|
+
#@comment.destroy
|
70
|
+
#end
|
71
|
+
#it "should exist on the first shard" do
|
72
|
+
#Comment.shard(:shard1).where(:comment => "comment number 1").first.should == @comment
|
73
|
+
#end
|
74
|
+
#it "should not exist on the second shard" do
|
75
|
+
#Comment.shard(:shard2).where(:comment => "comment number 1").should be_empty
|
76
|
+
#end
|
77
|
+
#end
|
78
|
+
|
79
|
+
#describe "search for a comment" do
|
80
|
+
#before(:all) do
|
81
|
+
#@comment1 = Comment.shard(:shard1).create!(:shard_key => 1, :comment => "blah")
|
82
|
+
#@comment2 = Comment.shard(:shard2).create!(:shard_key => 2, :comment => "blah")
|
83
|
+
#end
|
84
|
+
#after(:all) do
|
85
|
+
#@comment1.destroy rescue nil
|
86
|
+
#@comment2.destroy rescue nil
|
87
|
+
#end
|
88
|
+
#it "should lookup shard based on relation" do
|
89
|
+
#Comment.where(:shard_key => 1).where(:comment => "blah").size.should == 1
|
90
|
+
#Comment.where(:shard_key => 1).where(:comment => "blah").first.should be_a CommentShard1
|
91
|
+
#Comment.where(:shard_key => 2).size.should == 1
|
92
|
+
#Comment.where(:shard_key => 2).first.should be_a Comment
|
93
|
+
#Comment.where(:shard_key => 2).first.should be_a CommentShard2
|
94
|
+
#end
|
95
|
+
|
96
|
+
#it "should not do blind relation" do
|
97
|
+
#lambda { Comment.where(:comment => "blah").to_a }.should raise_exception
|
98
|
+
#end
|
99
|
+
#end
|
100
|
+
#end
|
101
|
+
|
@@ -0,0 +1,88 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
require 'active_record'
|
3
|
+
|
4
|
+
# ActiveRecord classes do not like to be inside anonymous modules-- if you want to anonymize, then you'll need a nested module!
|
5
|
+
|
6
|
+
|
7
|
+
describe ShardBuilder do
|
8
|
+
context "testing the shard builder" do
|
9
|
+
|
10
|
+
it "should raze the db dir as expected" do
|
11
|
+
builder = ShardBuilder.new
|
12
|
+
builder.raze
|
13
|
+
Dir.entries(File.expand_path("../db", __FILE__)).length.should == 2
|
14
|
+
FileUtils.touch File.expand_path("../db/blahblah", __FILE__)
|
15
|
+
FileUtils.touch File.expand_path("../db/yo.db", __FILE__)
|
16
|
+
Dir.entries(File.expand_path("../db", __FILE__)).length.should == 4
|
17
|
+
builder.raze
|
18
|
+
Dir.entries(File.expand_path("../db", __FILE__)).length.should == 2
|
19
|
+
end
|
20
|
+
|
21
|
+
it "should be able to connect to a single shard no problem" do
|
22
|
+
builder = ShardBuilder.new(1,3)
|
23
|
+
ActiveRecord::Base.establish_connection(builder.config(1))
|
24
|
+
module A
|
25
|
+
class Parent < ActiveRecord::Base; end
|
26
|
+
end
|
27
|
+
A::Parent.all.length.should == 3
|
28
|
+
A::Parent.all.each do |p|
|
29
|
+
p.name.should match(/^\[shard #1\]/)
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
# unfortunately this seems to require setting the table name!
|
34
|
+
it "should be able to leverage sandwich classes to hold the connections and still have relationships on a single shard" do
|
35
|
+
builder = ShardBuilder.new(1,3,5)
|
36
|
+
module B
|
37
|
+
class Shard < ActiveRecord::Base; end
|
38
|
+
class Parent < Shard; set_table_name "parents"; has_many :children end
|
39
|
+
class Child < Shard; set_table_name "children"; end
|
40
|
+
end
|
41
|
+
B::Shard.establish_connection(builder.config(1))
|
42
|
+
B::Parent.all.length.should == 3
|
43
|
+
B::Parent.all.each do |p|
|
44
|
+
p.name.should match(/^\[shard #1\]/)
|
45
|
+
p.children.length.should == 5
|
46
|
+
p.children.each do |c|
|
47
|
+
c.name.should match(/^\[shard #1\]/)
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
# unfortunately this seems to require setting the table name!
|
53
|
+
it "should be able to have relationships in each shard" do
|
54
|
+
builder = ShardBuilder.new(2,3,5)
|
55
|
+
module C1
|
56
|
+
class Shard < ActiveRecord::Base; end
|
57
|
+
class Parent < Shard; set_table_name "parents"; has_many :children end
|
58
|
+
class Child < Shard; set_table_name "children"; end
|
59
|
+
end
|
60
|
+
module C2
|
61
|
+
class Shard < ActiveRecord::Base; end
|
62
|
+
class Parent < Shard; set_table_name "parents"; has_many :children end
|
63
|
+
class Child < Shard; set_table_name "children"; end
|
64
|
+
end
|
65
|
+
C1::Shard.establish_connection(builder.config(1))
|
66
|
+
C2::Shard.establish_connection(builder.config(2))
|
67
|
+
C1::Parent.all.length.should == 3
|
68
|
+
C1::Parent.all.each do |p|
|
69
|
+
p.name.should match(/^\[shard #1\]/)
|
70
|
+
p.children.length.should == 5
|
71
|
+
p.children.each do |c|
|
72
|
+
c.name.should match(/^\[shard #1\]/)
|
73
|
+
end
|
74
|
+
end
|
75
|
+
C2::Parent.all.length.should == 3
|
76
|
+
C2::Parent.all.each do |p|
|
77
|
+
p.name.should match(/^\[shard #2\]/)
|
78
|
+
p.children.length.should == 5
|
79
|
+
p.children.each do |c|
|
80
|
+
c.name.should match(/^\[shard #2\]/)
|
81
|
+
end
|
82
|
+
end
|
83
|
+
end
|
84
|
+
|
85
|
+
end
|
86
|
+
end
|
87
|
+
|
88
|
+
|
@@ -0,0 +1,47 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
require 'active_record'
|
3
|
+
|
4
|
+
# a utility class used only for tests that builds out a number of shards with parent & child tables prepopulated with some test data
|
5
|
+
class ShardBuilder
|
6
|
+
class Child < ActiveRecord::Base; end;
|
7
|
+
class Parent < ActiveRecord::Base; end;
|
8
|
+
|
9
|
+
def initialize(shard_count=3, parent_count=3, child_count=5)
|
10
|
+
@shard_count = shard_count
|
11
|
+
@parent_count = parent_count
|
12
|
+
@child_count = child_count
|
13
|
+
raze
|
14
|
+
build
|
15
|
+
end
|
16
|
+
|
17
|
+
def build
|
18
|
+
(1..@shard_count).each do |i|
|
19
|
+
base = ActiveRecord::Base
|
20
|
+
base.establish_connection(config(i))
|
21
|
+
base.connection.create_table "children" do |t|
|
22
|
+
t.integer :parent_id
|
23
|
+
t.string :name
|
24
|
+
end
|
25
|
+
base.connection.create_table "parents" do |t|
|
26
|
+
t.integer :id
|
27
|
+
t.string :name
|
28
|
+
end
|
29
|
+
(1..@parent_count).each do |j|
|
30
|
+
(1..@child_count).each do |k|
|
31
|
+
Child.create :parent_id=>j, :name=>"[shard ##{i}] child(##{k}) for parent(##{j})"
|
32
|
+
end
|
33
|
+
Parent.create :name=>"[shard ##{i}] parent(#{j})"
|
34
|
+
end
|
35
|
+
base.remove_connection(base)
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
def raze
|
40
|
+
FileUtils.rm_rf File.expand_path("../../db", __FILE__)
|
41
|
+
FileUtils.mkdir_p File.expand_path("../../db", __FILE__)
|
42
|
+
end
|
43
|
+
|
44
|
+
def config(shard_index)
|
45
|
+
{:adapter => "sqlite3", :database => File.expand_path("../../db/shard#{shard_index}.db", __FILE__)}
|
46
|
+
end
|
47
|
+
end
|
metadata
CHANGED
@@ -2,7 +2,7 @@
|
|
2
2
|
name: nightcrawler
|
3
3
|
version: !ruby/object:Gem::Version
|
4
4
|
prerelease:
|
5
|
-
version: 0.0.
|
5
|
+
version: 0.0.2
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
8
8
|
- Joshua Lane, Michael Prior
|
@@ -10,7 +10,8 @@ autorequire:
|
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
12
|
|
13
|
-
date: 2011-05-
|
13
|
+
date: 2011-05-04 00:00:00 -04:00
|
14
|
+
default_executable:
|
14
15
|
dependencies:
|
15
16
|
- !ruby/object:Gem::Dependency
|
16
17
|
name: rspec
|
@@ -108,13 +109,18 @@ files:
|
|
108
109
|
- lib/nightcrawler.rb
|
109
110
|
- lib/nightcrawler/manager.rb
|
110
111
|
- lib/nightcrawler/migration.rb
|
112
|
+
- lib/nightcrawler/mosaic.rb
|
111
113
|
- lib/nightcrawler/relation.rb
|
112
114
|
- lib/nightcrawler/shard.rb
|
113
115
|
- lib/nightcrawler/shard_descriptor.rb
|
114
116
|
- lib/nightcrawler/version.rb
|
115
117
|
- nightcrawler.gemspec
|
116
118
|
- spec/manager_spec.rb
|
119
|
+
- spec/mosaic_spec.rb
|
120
|
+
- spec/shard_builder_spec.rb
|
117
121
|
- spec/spec_helper.rb
|
122
|
+
- spec/support/shard_builder.rb
|
123
|
+
has_rdoc: true
|
118
124
|
homepage: ""
|
119
125
|
licenses: []
|
120
126
|
|
@@ -138,7 +144,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
138
144
|
requirements: []
|
139
145
|
|
140
146
|
rubyforge_project: nightcrawler
|
141
|
-
rubygems_version: 1.
|
147
|
+
rubygems_version: 1.6.2
|
142
148
|
signing_key:
|
143
149
|
specification_version: 3
|
144
150
|
summary: Minimal sharding solution for AR
|