nightcrawler 0.0.1 → 0.0.2
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/.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
|