sequel-schema-sharding 0.11.1 → 0.12.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/Gemfile +0 -3
- data/README.md +15 -1
- data/lib/sequel/plugins/dataset_shard_id.rb +21 -0
- data/lib/sequel/schema-sharding/connection_manager.rb +2 -5
- data/lib/sequel/schema-sharding/extensions/postgres/dataset.rb +16 -0
- data/lib/sequel/schema-sharding/extensions/sequel_ext.rb +12 -8
- data/lib/sequel/schema-sharding/finder.rb +4 -3
- data/lib/sequel/schema-sharding/model.rb +10 -0
- data/lib/sequel/schema-sharding/version.rb +1 -1
- data/lib/sequel/tasks/test.rake +9 -0
- data/sequel-schema-sharding.gemspec +1 -1
- data/spec/schema-sharding/finder_spec.rb +1 -0
- data/spec/schema-sharding/model_spec.rb +21 -4
- metadata +24 -22
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: e9d2a8dea7d18fc038c01e427503dc01aa6b24f0
|
4
|
+
data.tar.gz: 8033c5b54ac76371fe1f0f3074f75d10d33e14fe
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 56f54ffdeb952181d59b683e74312c3387bc0edeb037c11b2d658d5a653b0e5eef867d44139c56c2ce6f6179d4d0275c1c70efe7abfd7696cfcbe837605547cb
|
7
|
+
data.tar.gz: 3d980233dd79fd9eac01f56550885d3a02589f1b0b95718dbad2d47aa5d5750b7225f134819a871759e3d9a3f4e4805f4e496ca82b4ee35688f63e9fc76c4ab4
|
data/Gemfile
CHANGED
data/README.md
CHANGED
@@ -1,7 +1,9 @@
|
|
1
1
|
sequel-schema-sharding
|
2
2
|
======================
|
3
3
|
|
4
|
+
[![Gem Version](https://badge.fury.io/rb/sequel-schema-sharding.png)](http://badge.fury.io/rb/sequel-schema-sharding)
|
4
5
|
[![Build Status](https://travis-ci.org/wanelo/sequel-schema-sharding.png?branch=master)](https://travis-ci.org/wanelo/sequel-schema-sharding)
|
6
|
+
[![Code Climate](https://codeclimate.com/github/wanelo/sequel-schema-sharding.png)](https://codeclimate.com/github/wanelo/sequel-schema-sharding)
|
5
7
|
|
6
8
|
Horizontally shard PostgreSQL tables with the Sequel gem, where each shard
|
7
9
|
lives in its own PostgreSQL schema.
|
@@ -201,7 +203,7 @@ queries do not try to reconnect to a downed master.
|
|
201
203
|
|
202
204
|
```bash
|
203
205
|
> bundle install
|
204
|
-
> bundle exec rake sequel:db:
|
206
|
+
> bundle exec rake sequel:db:test:reset
|
205
207
|
> bundle exec rspec
|
206
208
|
```
|
207
209
|
|
@@ -325,6 +327,18 @@ mapping, but any attempt to read a record inserted via the old mapping
|
|
325
327
|
will pick the wrong shard and return an empty set. DON'T EVER DO THIS.
|
326
328
|
It's really embarrassing.
|
327
329
|
|
330
|
+
### Any problems with other services?
|
331
|
+
|
332
|
+
When integrating with NewRelic, *do not* enable the SQL query plan
|
333
|
+
instrumentation. It can grab a connection that your application is also
|
334
|
+
trying to use... libpq is thread safe, so long as two threads do not
|
335
|
+
try to manipulate the same PGonn object
|
336
|
+
(http://www.postgresql.org/docs/9.3/static/libpq-threading.html).
|
337
|
+
If you see errors such as `PG::UnableToSend: insufficient data in "T" message`
|
338
|
+
or `PG::UnableToSend: extraneous data in "T" message`, this can indicate that
|
339
|
+
multiple threads are accessing the same connection, and data (or random bytes)
|
340
|
+
may have been transposed between queries.
|
341
|
+
|
328
342
|
|
329
343
|
## Contributing
|
330
344
|
|
@@ -0,0 +1,21 @@
|
|
1
|
+
module Sequel
|
2
|
+
module Plugins
|
3
|
+
module DatasetShardId
|
4
|
+
module InstanceMethods
|
5
|
+
def shard_number
|
6
|
+
@values[:shard_number] if @values
|
7
|
+
end
|
8
|
+
end
|
9
|
+
|
10
|
+
module DatasetMethods
|
11
|
+
def shard_number
|
12
|
+
@shard_number
|
13
|
+
end
|
14
|
+
|
15
|
+
def shard_number=(id)
|
16
|
+
@shard_number=id
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
@@ -77,16 +77,13 @@ module Sequel
|
|
77
77
|
def replica_hash_for(config)
|
78
78
|
return {} if config['replicas'].nil?
|
79
79
|
size = config['replicas'].size
|
80
|
-
i = rand(size)
|
81
80
|
{
|
82
81
|
:servers => {
|
83
82
|
:read_only => ->(db) do
|
84
|
-
choice =
|
83
|
+
choice = rand(size)
|
85
84
|
probe = Sequel::SchemaSharding::DTraceProvider.provider.replica_hash_for
|
86
85
|
probe.fire(choice, size) if probe.enabled?
|
87
|
-
sequel_connection_config_for(config['replicas'][choice])
|
88
|
-
i += 1
|
89
|
-
end
|
86
|
+
sequel_connection_config_for(config['replicas'][choice])
|
90
87
|
end
|
91
88
|
}
|
92
89
|
}
|
@@ -0,0 +1,16 @@
|
|
1
|
+
module Sequel
|
2
|
+
module Postgres
|
3
|
+
class Dataset
|
4
|
+
alias_method :adapter_fetch_rows, :fetch_rows
|
5
|
+
|
6
|
+
def fetch_rows(sql, &block)
|
7
|
+
adapter_fetch_rows(sql) do |r|
|
8
|
+
if self.respond_to?(:shard_number)
|
9
|
+
r[:shard_number] = self.shard_number
|
10
|
+
end
|
11
|
+
block.call r
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
@@ -1,16 +1,20 @@
|
|
1
1
|
module Sequel
|
2
|
-
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
2
|
+
class Database
|
3
|
+
class << self
|
4
|
+
alias_method :sequel_adapter_class, :adapter_class
|
5
|
+
|
6
|
+
def adapter_class(scheme)
|
7
|
+
klass = sequel_adapter_class(scheme)
|
8
|
+
|
9
|
+
begin
|
10
|
+
require "sequel/schema-sharding/extensions/#{scheme}/dataset"
|
11
|
+
rescue LoadError => e
|
9
12
|
end
|
13
|
+
|
14
|
+
klass
|
10
15
|
end
|
11
16
|
end
|
12
17
|
end
|
13
18
|
end
|
14
19
|
|
15
|
-
Sequel::Model.plugin Sequel::SchemaSharding::Extensions::SequelExt
|
16
20
|
Sequel::Model.plugin :validation_helpers
|
@@ -4,11 +4,12 @@ module Sequel
|
|
4
4
|
module SchemaSharding
|
5
5
|
class Finder
|
6
6
|
class Result
|
7
|
-
attr_reader :connection, :schema
|
7
|
+
attr_reader :connection, :schema, :shard_number
|
8
8
|
|
9
|
-
def initialize(connection, schema)
|
9
|
+
def initialize(connection, schema, shard_number)
|
10
10
|
@connection = connection
|
11
11
|
@schema = schema
|
12
|
+
@shard_number = shard_number
|
12
13
|
end
|
13
14
|
end
|
14
15
|
|
@@ -21,7 +22,7 @@ module Sequel
|
|
21
22
|
conn = Sequel::SchemaSharding.connection_manager[physical_shard]
|
22
23
|
schema = Sequel::SchemaSharding.connection_manager.schema_for(table_name, shard_number)
|
23
24
|
|
24
|
-
Result.new(conn, schema)
|
25
|
+
Result.new(conn, schema, shard_number)
|
25
26
|
end
|
26
27
|
|
27
28
|
private
|
@@ -28,6 +28,7 @@ module Sequel
|
|
28
28
|
|
29
29
|
def self.included(base)
|
30
30
|
base.extend(ClassMethods)
|
31
|
+
base.plugin(:dataset_shard_id)
|
31
32
|
end
|
32
33
|
|
33
34
|
module ClassMethods
|
@@ -53,6 +54,7 @@ module Sequel
|
|
53
54
|
ds = result.connection[schema_and_table(result)]
|
54
55
|
ds.row_proc = self
|
55
56
|
dataset_method_modules.each { |m| ds.instance_eval { extend(m) } }
|
57
|
+
ds.shard_number = result.shard_number
|
56
58
|
ds.model = self
|
57
59
|
ds
|
58
60
|
end
|
@@ -71,6 +73,14 @@ module Sequel
|
|
71
73
|
def schema_and_table(result)
|
72
74
|
:"#{result.schema}__#{self.implicit_table_name}"
|
73
75
|
end
|
76
|
+
|
77
|
+
def create(values = {}, &block)
|
78
|
+
sharded_column_value = values[sharded_column]
|
79
|
+
shard_number = result_for(sharded_column_value).shard_number
|
80
|
+
super.tap do |m|
|
81
|
+
m.values[:shard_number] = shard_number
|
82
|
+
end
|
83
|
+
end
|
74
84
|
end
|
75
85
|
|
76
86
|
# The database connection that has the logical shard.
|
data/lib/sequel/tasks/test.rake
CHANGED
@@ -20,5 +20,14 @@ namespace :sequel do
|
|
20
20
|
manager = Sequel::SchemaSharding::DatabaseManager.new
|
21
21
|
manager.drop_databases
|
22
22
|
end
|
23
|
+
|
24
|
+
namespace :test do
|
25
|
+
desc 'Reset test database'
|
26
|
+
task :reset do
|
27
|
+
ENV['RACK_ENV'] = 'test'
|
28
|
+
Rake::Task['sequel:db:drop'].invoke
|
29
|
+
Rake::Task['sequel:db:create'].invoke
|
30
|
+
end
|
31
|
+
end
|
23
32
|
end
|
24
33
|
end
|
@@ -21,7 +21,7 @@ Gem::Specification.new do |spec|
|
|
21
21
|
spec.add_dependency 'sequel'
|
22
22
|
spec.add_dependency 'pg'
|
23
23
|
spec.add_dependency 'sequel-replica-failover'
|
24
|
-
spec.add_dependency 'ruby-usdt'
|
24
|
+
spec.add_dependency 'ruby-usdt', '>= 0.2.1'
|
25
25
|
|
26
26
|
spec.add_development_dependency 'bundler', '~> 1.3'
|
27
27
|
spec.add_development_dependency 'rake'
|
@@ -9,6 +9,7 @@ describe Sequel::SchemaSharding::Finder do
|
|
9
9
|
result = Sequel::SchemaSharding::Finder.instance.lookup('boof', 60)
|
10
10
|
expect(result.connection).to be_a(Sequel::Postgres::Database)
|
11
11
|
expect(result.schema).to eq('sequel_logical_boof_01')
|
12
|
+
expect(result.shard_number).to eq(1)
|
12
13
|
end
|
13
14
|
|
14
15
|
xit 'is fast' do
|
@@ -25,8 +25,8 @@ describe Sequel::SchemaSharding, 'Model' do
|
|
25
25
|
klass
|
26
26
|
end
|
27
27
|
|
28
|
-
describe '
|
29
|
-
it '
|
28
|
+
describe 'reading from database' do
|
29
|
+
it 'can return a valid record' do
|
30
30
|
artist = model.create(artist_id: 14, name: 'Paul')
|
31
31
|
expect(artist.id).to_not be_nil
|
32
32
|
read_back_artist = model.by_id(14).first
|
@@ -36,15 +36,28 @@ describe Sequel::SchemaSharding, 'Model' do
|
|
36
36
|
read_back_artist = model.by_id(14).first
|
37
37
|
expect(read_back_artist).to be_nil
|
38
38
|
end
|
39
|
+
|
40
|
+
it 'includes shard number on model instances' do
|
41
|
+
shard_number = model.shard_for(456).shard_number
|
42
|
+
|
43
|
+
model.create(artist_id: 456, name: 'Randy')
|
44
|
+
record = model.by_id(456).first
|
45
|
+
expect(record.shard_number).to eq(shard_number)
|
46
|
+
end
|
39
47
|
end
|
40
48
|
|
41
|
-
describe '
|
42
|
-
it '
|
49
|
+
describe 'writing to database' do
|
50
|
+
it 'can create a valid record' do
|
43
51
|
artist = model.create(artist_id: 234, name: 'Paul')
|
44
52
|
expect(artist).to be_a(model)
|
45
53
|
expect(artist.name).to eql('Paul')
|
46
54
|
artist.destroy
|
47
55
|
end
|
56
|
+
|
57
|
+
it 'includes shard number on model instances' do
|
58
|
+
shard_number = model.shard_for(5432).shard_number
|
59
|
+
expect(model.create(artist_id: 5432, name: 'WOW').shard_number).to eq(shard_number)
|
60
|
+
end
|
48
61
|
end
|
49
62
|
|
50
63
|
describe '#shard_for' do
|
@@ -58,6 +71,10 @@ describe Sequel::SchemaSharding, 'Model' do
|
|
58
71
|
expect(dataset.db.opts[:database]).to eq('sequel_test_shard2')
|
59
72
|
expect(dataset.first_source).to eq(:sequel_logical_artists_17__artists)
|
60
73
|
end
|
74
|
+
|
75
|
+
it 'includes shard_number on dataset' do
|
76
|
+
expect(dataset.shard_number).to eq(17)
|
77
|
+
end
|
61
78
|
end
|
62
79
|
|
63
80
|
describe '#read_only_shard_for' do
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: sequel-schema-sharding
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.12.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Paul Henry
|
@@ -10,90 +10,90 @@ authors:
|
|
10
10
|
autorequire:
|
11
11
|
bindir: bin
|
12
12
|
cert_chain: []
|
13
|
-
date:
|
13
|
+
date: 2014-01-24 00:00:00.000000000 Z
|
14
14
|
dependencies:
|
15
15
|
- !ruby/object:Gem::Dependency
|
16
16
|
name: sequel
|
17
17
|
requirement: !ruby/object:Gem::Requirement
|
18
18
|
requirements:
|
19
|
-
- -
|
19
|
+
- - ">="
|
20
20
|
- !ruby/object:Gem::Version
|
21
21
|
version: '0'
|
22
22
|
type: :runtime
|
23
23
|
prerelease: false
|
24
24
|
version_requirements: !ruby/object:Gem::Requirement
|
25
25
|
requirements:
|
26
|
-
- -
|
26
|
+
- - ">="
|
27
27
|
- !ruby/object:Gem::Version
|
28
28
|
version: '0'
|
29
29
|
- !ruby/object:Gem::Dependency
|
30
30
|
name: pg
|
31
31
|
requirement: !ruby/object:Gem::Requirement
|
32
32
|
requirements:
|
33
|
-
- -
|
33
|
+
- - ">="
|
34
34
|
- !ruby/object:Gem::Version
|
35
35
|
version: '0'
|
36
36
|
type: :runtime
|
37
37
|
prerelease: false
|
38
38
|
version_requirements: !ruby/object:Gem::Requirement
|
39
39
|
requirements:
|
40
|
-
- -
|
40
|
+
- - ">="
|
41
41
|
- !ruby/object:Gem::Version
|
42
42
|
version: '0'
|
43
43
|
- !ruby/object:Gem::Dependency
|
44
44
|
name: sequel-replica-failover
|
45
45
|
requirement: !ruby/object:Gem::Requirement
|
46
46
|
requirements:
|
47
|
-
- -
|
47
|
+
- - ">="
|
48
48
|
- !ruby/object:Gem::Version
|
49
49
|
version: '0'
|
50
50
|
type: :runtime
|
51
51
|
prerelease: false
|
52
52
|
version_requirements: !ruby/object:Gem::Requirement
|
53
53
|
requirements:
|
54
|
-
- -
|
54
|
+
- - ">="
|
55
55
|
- !ruby/object:Gem::Version
|
56
56
|
version: '0'
|
57
57
|
- !ruby/object:Gem::Dependency
|
58
58
|
name: ruby-usdt
|
59
59
|
requirement: !ruby/object:Gem::Requirement
|
60
60
|
requirements:
|
61
|
-
- -
|
61
|
+
- - ">="
|
62
62
|
- !ruby/object:Gem::Version
|
63
|
-
version:
|
63
|
+
version: 0.2.1
|
64
64
|
type: :runtime
|
65
65
|
prerelease: false
|
66
66
|
version_requirements: !ruby/object:Gem::Requirement
|
67
67
|
requirements:
|
68
|
-
- -
|
68
|
+
- - ">="
|
69
69
|
- !ruby/object:Gem::Version
|
70
|
-
version:
|
70
|
+
version: 0.2.1
|
71
71
|
- !ruby/object:Gem::Dependency
|
72
72
|
name: bundler
|
73
73
|
requirement: !ruby/object:Gem::Requirement
|
74
74
|
requirements:
|
75
|
-
- - ~>
|
75
|
+
- - "~>"
|
76
76
|
- !ruby/object:Gem::Version
|
77
77
|
version: '1.3'
|
78
78
|
type: :development
|
79
79
|
prerelease: false
|
80
80
|
version_requirements: !ruby/object:Gem::Requirement
|
81
81
|
requirements:
|
82
|
-
- - ~>
|
82
|
+
- - "~>"
|
83
83
|
- !ruby/object:Gem::Version
|
84
84
|
version: '1.3'
|
85
85
|
- !ruby/object:Gem::Dependency
|
86
86
|
name: rake
|
87
87
|
requirement: !ruby/object:Gem::Requirement
|
88
88
|
requirements:
|
89
|
-
- -
|
89
|
+
- - ">="
|
90
90
|
- !ruby/object:Gem::Version
|
91
91
|
version: '0'
|
92
92
|
type: :development
|
93
93
|
prerelease: false
|
94
94
|
version_requirements: !ruby/object:Gem::Requirement
|
95
95
|
requirements:
|
96
|
-
- -
|
96
|
+
- - ">="
|
97
97
|
- !ruby/object:Gem::Version
|
98
98
|
version: '0'
|
99
99
|
description: ''
|
@@ -103,9 +103,9 @@ executables: []
|
|
103
103
|
extensions: []
|
104
104
|
extra_rdoc_files: []
|
105
105
|
files:
|
106
|
-
- .gitignore
|
107
|
-
- .rspec
|
108
|
-
- .travis.yml
|
106
|
+
- ".gitignore"
|
107
|
+
- ".rspec"
|
108
|
+
- ".travis.yml"
|
109
109
|
- CONTRIBUTORS.md
|
110
110
|
- Gemfile
|
111
111
|
- Guardfile
|
@@ -118,6 +118,7 @@ files:
|
|
118
118
|
- examples/sharding.rb
|
119
119
|
- examples/sharding.yml
|
120
120
|
- lib/sequel-schema-sharding.rb
|
121
|
+
- lib/sequel/plugins/dataset_shard_id.rb
|
121
122
|
- lib/sequel/schema-sharding.rb
|
122
123
|
- lib/sequel/schema-sharding/configuration.rb
|
123
124
|
- lib/sequel/schema-sharding/connection_manager.rb
|
@@ -125,6 +126,7 @@ files:
|
|
125
126
|
- lib/sequel/schema-sharding/database_manager/schema_iterator.rb
|
126
127
|
- lib/sequel/schema-sharding/dtrace_provider.rb
|
127
128
|
- lib/sequel/schema-sharding/extensions/migrations_ext.rb
|
129
|
+
- lib/sequel/schema-sharding/extensions/postgres/dataset.rb
|
128
130
|
- lib/sequel/schema-sharding/extensions/sequel_ext.rb
|
129
131
|
- lib/sequel/schema-sharding/finder.rb
|
130
132
|
- lib/sequel/schema-sharding/logger_proxy.rb
|
@@ -158,17 +160,17 @@ require_paths:
|
|
158
160
|
- lib
|
159
161
|
required_ruby_version: !ruby/object:Gem::Requirement
|
160
162
|
requirements:
|
161
|
-
- -
|
163
|
+
- - ">="
|
162
164
|
- !ruby/object:Gem::Version
|
163
165
|
version: '0'
|
164
166
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
165
167
|
requirements:
|
166
|
-
- -
|
168
|
+
- - ">="
|
167
169
|
- !ruby/object:Gem::Version
|
168
170
|
version: '0'
|
169
171
|
requirements: []
|
170
172
|
rubyforge_project:
|
171
|
-
rubygems_version: 2.0
|
173
|
+
rubygems_version: 2.2.0
|
172
174
|
signing_key:
|
173
175
|
specification_version: 4
|
174
176
|
summary: Create horizontally sharded Sequel models with Postgres
|