tarantool 0.3.0.7 → 0.4.2.1
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/Gemfile +2 -3
- data/README.md +90 -30
- data/Rakefile +6 -1
- data/lib/tarantool.rb +93 -18
- data/lib/tarantool/base_record.rb +97 -10
- data/lib/tarantool/block_db.rb +104 -6
- data/lib/tarantool/callback_db.rb +7 -0
- data/lib/tarantool/core-ext.rb +24 -8
- data/lib/tarantool/em_db.rb +189 -20
- data/lib/tarantool/exceptions.rb +4 -0
- data/lib/tarantool/fiber_db.rb +15 -1
- data/lib/tarantool/light_record.rb +17 -0
- data/lib/tarantool/query.rb +15 -9
- data/lib/tarantool/record/select.rb +21 -3
- data/lib/tarantool/request.rb +130 -43
- data/lib/tarantool/response.rb +70 -7
- data/lib/tarantool/serializers.rb +26 -5
- data/lib/tarantool/serializers/ber_array.rb +14 -0
- data/lib/tarantool/shards_support.rb +204 -0
- data/lib/tarantool/space_array.rb +38 -13
- data/lib/tarantool/space_hash.rb +49 -27
- data/lib/tarantool/util.rb +96 -10
- data/lib/tarantool/version.rb +2 -1
- data/test/helper.rb +154 -4
- data/test/{tarant/init.lua → init.lua} +0 -0
- data/test/run_all.rb +2 -2
- data/test/shared_record.rb +59 -0
- data/test/shared_replicated_shard.rb +1018 -0
- data/test/shared_reshard.rb +380 -0
- data/test/tarantool.cfg +2 -0
- data/test/test_light_record.rb +2 -0
- data/test/test_light_record_callback.rb +92 -0
- data/test/test_query_block.rb +1 -0
- data/test/test_query_fiber.rb +1 -0
- data/test/test_reshard_block.rb +7 -0
- data/test/test_reshard_fiber.rb +11 -0
- data/test/test_shard_replication_block.rb +7 -0
- data/test/test_shard_replication_fiber.rb +11 -0
- data/test/test_space_array_block.rb +1 -0
- data/test/test_space_array_callback.rb +50 -121
- data/test/test_space_array_callback_nodef.rb +39 -96
- data/test/test_space_array_fiber.rb +1 -0
- data/test/test_space_hash_block.rb +1 -0
- data/test/test_space_hash_fiber.rb +1 -0
- metadata +54 -17
- data/lib/tarantool/record.rb +0 -164
- data/test/box.pid +0 -1
- data/test/tarantool.log +0 -6
- data/test/tarantool_repl.cfg +0 -53
- data/test/test_record.rb +0 -88
- data/test/test_record_composite_pk.rb +0 -77
File without changes
|
data/test/run_all.rb
CHANGED
@@ -1,3 +1,3 @@
|
|
1
|
-
Dir.glob(File.expand_path('../
|
2
|
-
require f
|
1
|
+
Dir.glob(File.expand_path('../test*.rb', __FILE__)) do |f|
|
2
|
+
require f unless f =~ /shard/
|
3
3
|
end
|
data/test/shared_record.rb
CHANGED
@@ -471,4 +471,63 @@ shared_examples_for :record do
|
|
471
471
|
u.info['hobbies'].must_equal ['mufa', 'tuka']
|
472
472
|
end
|
473
473
|
end
|
474
|
+
|
475
|
+
describe "composite primary key" do
|
476
|
+
let(:address_class) do
|
477
|
+
Class.new(base_class) do
|
478
|
+
set_tarantool DB
|
479
|
+
set_space_no 2
|
480
|
+
|
481
|
+
def self.name # For naming
|
482
|
+
"Adress"
|
483
|
+
end
|
484
|
+
|
485
|
+
field :city, :string
|
486
|
+
field :street, :string
|
487
|
+
field :index, :integer
|
488
|
+
field :name, :string
|
489
|
+
field :citizens, :integer, default: 1
|
490
|
+
index :city, :street, primary: true
|
491
|
+
index :index
|
492
|
+
end
|
493
|
+
end
|
494
|
+
|
495
|
+
before do
|
496
|
+
address_class.create city: "Moscow", street: "Leningradskii", index: 123, name: "Pedro"
|
497
|
+
address_class.create city: "Moscow", street: "Mohovaya", index: 123, name: "Pedro"
|
498
|
+
end
|
499
|
+
|
500
|
+
it "should return objects by second index" do
|
501
|
+
a1 = address_class.where(index: 123)
|
502
|
+
a1.all.size.must_equal 2
|
503
|
+
end
|
504
|
+
|
505
|
+
it "should work with increment" do
|
506
|
+
a1 = address_class.where(city: "Moscow", street: "Leningradskii").first
|
507
|
+
citizens = a1.citizens
|
508
|
+
a1.increment :citizens
|
509
|
+
a1.reload.citizens.must_equal(citizens+1)
|
510
|
+
end
|
511
|
+
|
512
|
+
it "should return all objects" do
|
513
|
+
a1 = address_class.where city: "Moscow"
|
514
|
+
a1.all.size.must_equal 2
|
515
|
+
a1.all.map(&:street).must_equal ["Leningradskii", "Mohovaya"]
|
516
|
+
end
|
517
|
+
|
518
|
+
it "should return right object" do
|
519
|
+
a1 = address_class.where city: "Moscow", street: "Leningradskii"
|
520
|
+
a1.first.street.must_equal "Leningradskii"
|
521
|
+
end
|
522
|
+
|
523
|
+
it "should destroy object" do
|
524
|
+
a1 = address_class.where city: "Moscow", street: "Leningradskii"
|
525
|
+
a1.first.destroy
|
526
|
+
end
|
527
|
+
|
528
|
+
it "should return id" do
|
529
|
+
a1 = address_class.where city: "Moscow", street: "Leningradskii"
|
530
|
+
a1.first.id.must_equal ["Moscow", "Leningradskii"]
|
531
|
+
end
|
532
|
+
end
|
474
533
|
end
|
@@ -0,0 +1,1018 @@
|
|
1
|
+
require File.expand_path('../helper.rb', __FILE__)
|
2
|
+
require 'tarantool/light_record'
|
3
|
+
|
4
|
+
shared_examples_for 'replication and shards' do
|
5
|
+
before { TConf.reset_and_up_all }
|
6
|
+
after { TConf.clear_all }
|
7
|
+
|
8
|
+
let(:t_both) {
|
9
|
+
Tarantool.new(type: tarantool_type,
|
10
|
+
servers: [
|
11
|
+
[ TConf.conf(:master1), TConf.conf(:slave1) ],
|
12
|
+
[ TConf.conf(:master2), TConf.conf(:slave2) ]
|
13
|
+
],
|
14
|
+
replica_strategy: replica_strategy
|
15
|
+
)
|
16
|
+
}
|
17
|
+
|
18
|
+
let(:t_first) {
|
19
|
+
Tarantool.new(type: tarantool_type,
|
20
|
+
servers: [ TConf.conf(:master1), TConf.conf(:slave1) ],
|
21
|
+
replica_strategy: replica_strategy
|
22
|
+
)
|
23
|
+
}
|
24
|
+
|
25
|
+
let(:t_second) {
|
26
|
+
Tarantool.new(type: tarantool_type,
|
27
|
+
servers: [ TConf.conf(:master2), TConf.conf(:slave2) ],
|
28
|
+
replica_strategy: replica_strategy
|
29
|
+
)
|
30
|
+
}
|
31
|
+
let(:replica_strategy) { :master_first }
|
32
|
+
|
33
|
+
HSPACE1_ = {
|
34
|
+
fields: {id: :int, name: :str, val: :int},
|
35
|
+
keys: :id
|
36
|
+
}
|
37
|
+
|
38
|
+
let(:space0_array_both) {
|
39
|
+
t_both.space(0, SPACE0[:types], keys: SPACE0[:keys],
|
40
|
+
shard_fields: shard_fields_array0, shard_proc: shard_proc0)
|
41
|
+
}
|
42
|
+
let(:space0_array_first) { t_first.space(0, SPACE0[:types], keys: SPACE0[:keys]) }
|
43
|
+
let(:space0_array_second) { t_second.space(0, SPACE0[:types], keys: SPACE0[:keys]) }
|
44
|
+
let(:space0_hash_both) {
|
45
|
+
t_both.space_hash(0, HSPACE0[:fields], keys: HSPACE0[:keys],
|
46
|
+
shard_fields: shard_fields_hash0, shard_proc: shard_proc0)
|
47
|
+
}
|
48
|
+
let(:space0_hash_first) { t_first.space_hash(0, HSPACE0[:fields], keys: HSPACE0[:keys]) }
|
49
|
+
let(:space0_hash_second) { t_second.space_hash(0, HSPACE0[:fields], keys: HSPACE0[:keys]) }
|
50
|
+
|
51
|
+
let(:space1_array_both) {
|
52
|
+
t_both.space(1, SPACE1[:types], keys: SPACE1[:keys],
|
53
|
+
shard_fields: shard_fields_array1, shard_proc: shard_proc1)
|
54
|
+
}
|
55
|
+
let(:space1_array_first) { t_first.space(1, SPACE1[:types], keys: SPACE1[:keys]) }
|
56
|
+
let(:space1_array_second) { t_second.space(1, SPACE1[:types], keys: SPACE1[:keys]) }
|
57
|
+
let(:space1_hash_both) {
|
58
|
+
t_both.space_hash(1, HSPACE1_[:fields], keys: HSPACE1_[:keys],
|
59
|
+
shard_fields: shard_fields_hash1, shard_proc: shard_proc1)
|
60
|
+
}
|
61
|
+
let(:space1_hash_first) { t_first.space_hash(1, HSPACE1_[:fields], keys: HSPACE1_[:keys]) }
|
62
|
+
let(:space1_hash_second) { t_second.space_hash(1, HSPACE1_[:fields], keys: HSPACE1_[:keys]) }
|
63
|
+
|
64
|
+
let(:space2_array_both) {
|
65
|
+
t_both.space(2, SPACE2[:types], keys: SPACE2[:keys],
|
66
|
+
shard_fields: shard_fields_array2, shard_proc: shard_proc2)
|
67
|
+
}
|
68
|
+
let(:space2_array_first) { t_first.space(2, SPACE2[:types], keys: SPACE2[:keys]) }
|
69
|
+
let(:space2_array_second) { t_second.space(2, SPACE2[:types], keys: SPACE2[:keys]) }
|
70
|
+
let(:space2_hash_both) {
|
71
|
+
t_both.space_hash(2, HSPACE2[:fields], keys: HSPACE2[:keys],
|
72
|
+
shard_fields: shard_fields_hash2, shard_proc: shard_proc2)
|
73
|
+
}
|
74
|
+
let(:space2_hash_first) { t_first.space_hash(2, HSPACE2[:fields], keys: HSPACE2[:keys]) }
|
75
|
+
let(:space2_hash_second) { t_second.space_hash(2, HSPACE2[:fields], keys: HSPACE2[:keys]) }
|
76
|
+
|
77
|
+
let(:shard_fields_array0) { nil }
|
78
|
+
let(:shard_fields_array1) { nil }
|
79
|
+
let(:shard_fields_array2) { nil }
|
80
|
+
let(:shard_fields_hash0) { nil }
|
81
|
+
let(:shard_fields_hash1) { nil }
|
82
|
+
let(:shard_fields_hash2) { nil }
|
83
|
+
let(:shard_proc0) { nil }
|
84
|
+
let(:shard_proc1) { nil }
|
85
|
+
let(:shard_proc2) { nil }
|
86
|
+
|
87
|
+
shared_examples_for "array space with simple shard" do
|
88
|
+
let(:space_both) { space1_array_both }
|
89
|
+
let(:space_first) { space1_array_first }
|
90
|
+
let(:space_second){ space1_array_second }
|
91
|
+
before {
|
92
|
+
blockrun {
|
93
|
+
100.times{|i|
|
94
|
+
space_both.insert([i, "#{i+1}", i+2])
|
95
|
+
}
|
96
|
+
}
|
97
|
+
}
|
98
|
+
it "should spread distribution over" do
|
99
|
+
all_pks = 100.times.map{|i| [i]}
|
100
|
+
results = blockrun{[
|
101
|
+
100.times.map{|i| space_both.by_pk(i) },
|
102
|
+
space_first.all_by_pks(all_pks),
|
103
|
+
space_second.all_by_pks(all_pks),
|
104
|
+
space_both.all_by_pks(all_pks),
|
105
|
+
]}
|
106
|
+
results[1].size.must_equal 50
|
107
|
+
results[2].size.must_equal 50
|
108
|
+
results[1].include?([50, '51', 52]).must_equal results[2].include?([51, '52', 53])
|
109
|
+
(results[1] + results[2]).sort.must_equal results[0]
|
110
|
+
results[3].sort.must_equal results[0]
|
111
|
+
end
|
112
|
+
|
113
|
+
it "should delete" do
|
114
|
+
results = blockrun{[
|
115
|
+
space_both.delete(50, return_tuple: true),
|
116
|
+
space_both.by_pk(50),
|
117
|
+
space_both.delete(51, return_tuple: true),
|
118
|
+
space_both.by_pk(51),
|
119
|
+
]}
|
120
|
+
results[0].must_equal [50, '51', 52]
|
121
|
+
results[1].must_be_nil
|
122
|
+
results[2].must_equal [51, '52', 53]
|
123
|
+
results[3].must_be_nil
|
124
|
+
end
|
125
|
+
|
126
|
+
it "should update" do
|
127
|
+
results = blockrun{[
|
128
|
+
space_both.update(50, {1 => '--'}, return_tuple: true),
|
129
|
+
space_both.update(51, [[1, :set, '++']], return_tuple: true),
|
130
|
+
]}
|
131
|
+
results[0].must_equal [50, '--', 52]
|
132
|
+
results[1].must_equal [51, '++', 53]
|
133
|
+
end
|
134
|
+
end
|
135
|
+
|
136
|
+
describe "array space with modulo shard" do
|
137
|
+
let(:shard_proc1) { :modulo }
|
138
|
+
it_behaves_like "array space with simple shard"
|
139
|
+
end
|
140
|
+
|
141
|
+
shared_examples_for "hash space with simple shard" do
|
142
|
+
let(:space_both) { space1_hash_both }
|
143
|
+
let(:space_first) { space1_hash_first }
|
144
|
+
let(:space_second){ space1_hash_second }
|
145
|
+
before {
|
146
|
+
blockrun {
|
147
|
+
100.times{|i|
|
148
|
+
space_both.insert({id: i, name: "#{i+1}", val: i+2})
|
149
|
+
}
|
150
|
+
}
|
151
|
+
}
|
152
|
+
it "should spread distribution over" do
|
153
|
+
all_pks = 100.times.map{|i| [i]}
|
154
|
+
results = blockrun{[
|
155
|
+
100.times.map{|i| space_both.by_pk(i) },
|
156
|
+
space_first.all_by_pks(all_pks),
|
157
|
+
space_second.all_by_pks(all_pks),
|
158
|
+
space_both.all_by_pks(all_pks),
|
159
|
+
]}
|
160
|
+
results[1].size.must_equal 50
|
161
|
+
results[2].size.must_equal 50
|
162
|
+
results[1].include?({id:50, name:'51', val:52}).must_equal results[2].include?({id:51, name:'52', val:53})
|
163
|
+
(results[1] + results[2]).sort_by{|v| v[:id]}.must_equal results[0]
|
164
|
+
results[3].sort_by{|v| v[:id]}.must_equal results[0]
|
165
|
+
end
|
166
|
+
|
167
|
+
it "should delete" do
|
168
|
+
results = blockrun{[
|
169
|
+
space_both.delete(50, return_tuple: true),
|
170
|
+
space_both.by_pk(50),
|
171
|
+
space_both.delete(51, return_tuple: true),
|
172
|
+
space_both.by_pk(51),
|
173
|
+
]}
|
174
|
+
results[0].must_equal({id: 50, name:'51', val:52})
|
175
|
+
results[1].must_be_nil
|
176
|
+
results[2].must_equal({id: 51, name:'52', val:53})
|
177
|
+
results[3].must_be_nil
|
178
|
+
end
|
179
|
+
|
180
|
+
it "should update" do
|
181
|
+
results = blockrun{[
|
182
|
+
space_both.update(50, {name: '--'}, return_tuple: true),
|
183
|
+
space_both.update(51, [[:name, :set, '++']], return_tuple: true),
|
184
|
+
]}
|
185
|
+
results[0].must_equal({id: 50, name: '--', val: 52})
|
186
|
+
results[1].must_equal({id: 51, name: '++', val: 53})
|
187
|
+
end
|
188
|
+
end
|
189
|
+
|
190
|
+
describe "hash space with modulo shard" do
|
191
|
+
let(:shard_proc1) { :modulo }
|
192
|
+
it_behaves_like "hash space with simple shard"
|
193
|
+
end
|
194
|
+
|
195
|
+
shared_examples_for "array space shard with composit pk" do
|
196
|
+
def iii(pk) [*pk, pk.hash & 0xffffff] end
|
197
|
+
let(:space_both){ space2_array_both }
|
198
|
+
let(:space_first){ space2_array_first }
|
199
|
+
let(:space_second){ space2_array_second }
|
200
|
+
let(:pks) {
|
201
|
+
(1..10).to_a.product((1..10).to_a).map{|i,j| [i.to_s, j.to_s]}
|
202
|
+
}
|
203
|
+
let(:pk_first) {
|
204
|
+
pks.detect{|pk| space_both._detect_shards_for_key(pk, 0) == 0}
|
205
|
+
}
|
206
|
+
let(:pk_second) {
|
207
|
+
pks.detect{|pk| space_both._detect_shards_for_key(pk, 0) == 1}
|
208
|
+
}
|
209
|
+
before {
|
210
|
+
blockrun{
|
211
|
+
pks.each{|pk| space_both.insert(iii(pk))}
|
212
|
+
}
|
213
|
+
}
|
214
|
+
|
215
|
+
it "should spread distribution over" do
|
216
|
+
results = blockrun{[
|
217
|
+
pks.map{|pk| space_both.by_pk(pk)},
|
218
|
+
pks.flat_map{|pk| space_first.all_by_pks([pk])},
|
219
|
+
pks.flat_map{|pk| space_second.all_by_pks([pk])},
|
220
|
+
space_first.by_pk(pk_first),
|
221
|
+
space_second.by_pk(pk_second),
|
222
|
+
space_first.by_pk(pk_second),
|
223
|
+
space_second.by_pk(pk_first),
|
224
|
+
]}
|
225
|
+
results[0].compact.size.must_equal pks.size
|
226
|
+
(results[1] + results[2]).sort.must_equal results[0].sort
|
227
|
+
results[1].size.must_be_close_to pks.size/2, pks.size/5
|
228
|
+
results[2].size.must_be_close_to pks.size/2, pks.size/5
|
229
|
+
results[3].must_equal iii(pk_first)
|
230
|
+
results[4].must_equal iii(pk_second)
|
231
|
+
results[5].must_equal nil
|
232
|
+
results[6].must_equal nil
|
233
|
+
end
|
234
|
+
|
235
|
+
it "should delete" do
|
236
|
+
results = blockrun{[
|
237
|
+
space_both.delete(pk_first, return_tuple:true),
|
238
|
+
space_both.by_pk(pk_first),
|
239
|
+
space_both.delete(pk_second, return_tuple:true),
|
240
|
+
space_both.by_pk(pk_second),
|
241
|
+
]}
|
242
|
+
results[0].must_equal iii(pk_first)
|
243
|
+
results[1].must_be_nil
|
244
|
+
results[2].must_equal iii(pk_second)
|
245
|
+
results[3].must_be_nil
|
246
|
+
end
|
247
|
+
|
248
|
+
it "should update" do
|
249
|
+
results = blockrun{[
|
250
|
+
space_both.update(pk_first, {2=> [:+, 1]}, return_tuple:true),
|
251
|
+
space_both.update(pk_second, {2=> [:+, 1]}, return_tuple:true),
|
252
|
+
]}
|
253
|
+
results[0][2].must_equal iii(pk_first)[2]+1
|
254
|
+
results[1][2].must_equal iii(pk_second)[2]+1
|
255
|
+
end
|
256
|
+
|
257
|
+
it "should search by keys half" do
|
258
|
+
results = blockrun{[
|
259
|
+
pks.map{|pk| space_both.by_pk(pk)},
|
260
|
+
(1..10).flat_map{|i| space_both.select(0, [i.to_s])},
|
261
|
+
space_both.select(0, (1..10).map{|i| [i.to_s]})
|
262
|
+
]}
|
263
|
+
results[1].sort.must_equal results[0].sort
|
264
|
+
results[2].sort.must_equal results[0].sort
|
265
|
+
end
|
266
|
+
end
|
267
|
+
|
268
|
+
describe "array space default shard with composit pk" do
|
269
|
+
it_behaves_like "array space shard with composit pk"
|
270
|
+
end
|
271
|
+
|
272
|
+
describe "array space custom shard with composit pk" do
|
273
|
+
class ShardProc2
|
274
|
+
attr :count
|
275
|
+
def initialize
|
276
|
+
@count = 0
|
277
|
+
end
|
278
|
+
def call(shard_values, shards_count, this)
|
279
|
+
@count += 1
|
280
|
+
shard_values[0] && shard_values[0].to_i % shards_count
|
281
|
+
end
|
282
|
+
end
|
283
|
+
let(:shard_proc2){ ShardProc2.new }
|
284
|
+
|
285
|
+
it_behaves_like "array space shard with composit pk"
|
286
|
+
|
287
|
+
it "should call custom shard proc" do
|
288
|
+
shard_proc2.count.must_equal pks.size
|
289
|
+
end
|
290
|
+
end
|
291
|
+
|
292
|
+
shared_examples_for "hash space shard with composit pk" do
|
293
|
+
def iii(pk) {first: pk[0], second: pk[1], third: pk.hash & 0xffffff} end
|
294
|
+
let(:space_both){ space2_hash_both }
|
295
|
+
let(:space_first){ space2_hash_first }
|
296
|
+
let(:space_second){ space2_hash_second }
|
297
|
+
let(:pks) {
|
298
|
+
(1..10).to_a.product((1..10).to_a).map{|i,j| [i.to_s, j.to_s]}
|
299
|
+
}
|
300
|
+
let(:pk_first) {
|
301
|
+
pks.detect{|pk| space_both._detect_shards_for_key(pk, 0) == 0}
|
302
|
+
}
|
303
|
+
let(:pk_second) {
|
304
|
+
pks.detect{|pk| space_both._detect_shards_for_key(pk, 0) == 1}
|
305
|
+
}
|
306
|
+
before {
|
307
|
+
blockrun{
|
308
|
+
pks.each{|pk| space_both.insert(iii(pk))}
|
309
|
+
}
|
310
|
+
}
|
311
|
+
|
312
|
+
it "should spread distribution over" do
|
313
|
+
results = blockrun{[
|
314
|
+
pks.map{|pk| space_both.by_pk(pk)},
|
315
|
+
pks.flat_map{|pk| space_first.all_by_pks([pk])},
|
316
|
+
pks.flat_map{|pk| space_second.all_by_pks([pk])},
|
317
|
+
space_first.by_pk(pk_first),
|
318
|
+
space_second.by_pk(pk_second),
|
319
|
+
space_first.by_pk(pk_second),
|
320
|
+
space_second.by_pk(pk_first),
|
321
|
+
]}
|
322
|
+
results[0].compact.size.must_equal pks.size
|
323
|
+
(results[1] + results[2]).sort_by(&:values).must_equal results[0].sort_by(&:values)
|
324
|
+
results[1].size.must_be_close_to pks.size/2, pks.size/5
|
325
|
+
results[2].size.must_be_close_to pks.size/2, pks.size/5
|
326
|
+
results[3].must_equal iii(pk_first)
|
327
|
+
results[4].must_equal iii(pk_second)
|
328
|
+
results[5].must_equal nil
|
329
|
+
results[6].must_equal nil
|
330
|
+
end
|
331
|
+
|
332
|
+
it "should delete" do
|
333
|
+
results = blockrun{[
|
334
|
+
space_both.delete(pk_first, return_tuple:true),
|
335
|
+
space_both.by_pk(pk_first),
|
336
|
+
space_both.delete(pk_second, return_tuple:true),
|
337
|
+
space_both.by_pk(pk_second),
|
338
|
+
]}
|
339
|
+
results[0].must_equal iii(pk_first)
|
340
|
+
results[1].must_be_nil
|
341
|
+
results[2].must_equal iii(pk_second)
|
342
|
+
results[3].must_be_nil
|
343
|
+
end
|
344
|
+
|
345
|
+
it "should update" do
|
346
|
+
results = blockrun{[
|
347
|
+
space_both.update(pk_first, {third: [:+, 1]}, return_tuple:true),
|
348
|
+
space_both.update(pk_second, {third: [:+, 1]}, return_tuple:true),
|
349
|
+
]}
|
350
|
+
results[0][:third].must_equal iii(pk_first)[:third]+1
|
351
|
+
results[1][:third].must_equal iii(pk_second)[:third]+1
|
352
|
+
end
|
353
|
+
|
354
|
+
it "should search by keys half" do
|
355
|
+
results = blockrun{[
|
356
|
+
pks.map{|pk| space_both.by_pk(pk)},
|
357
|
+
(1..10).flat_map{|i| space_both.select({first: i.to_s})},
|
358
|
+
space_both.select((1..10).map{|i| {first: i.to_s}})
|
359
|
+
]}
|
360
|
+
results[1].sort_by(&:values).must_equal results[0].sort_by(&:values)
|
361
|
+
results[2].sort_by(&:values).must_equal results[0].sort_by(&:values)
|
362
|
+
end
|
363
|
+
end
|
364
|
+
|
365
|
+
describe "hash space default shard with composit pk" do
|
366
|
+
it_behaves_like "hash space shard with composit pk"
|
367
|
+
end
|
368
|
+
|
369
|
+
describe "array space with shard on not pk" do
|
370
|
+
let(:shard_proc0) { :modulo }
|
371
|
+
let(:shard_fields_array0) { [3] }
|
372
|
+
let(:space_both){ space0_array_both }
|
373
|
+
let(:space_first){ space0_array_first }
|
374
|
+
let(:space_second){ space0_array_second }
|
375
|
+
let(:pks){ 100.times.to_a }
|
376
|
+
|
377
|
+
before {
|
378
|
+
blockrun{
|
379
|
+
pks.each{|i| space_both.insert([i, i+1, i+2, i/10]) }
|
380
|
+
}
|
381
|
+
}
|
382
|
+
|
383
|
+
it "should spread distribution over" do
|
384
|
+
results = blockrun{[
|
385
|
+
pks.flat_map{|i| space_both.all_by_pks([i])},
|
386
|
+
pks.flat_map{|i| space_first.all_by_pks([i])},
|
387
|
+
pks.flat_map{|i| space_second.all_by_pks([i])},
|
388
|
+
(0..9).flat_map{|i| space_both.select(2, i)},
|
389
|
+
(0..9).flat_map{|i| space_first.select([3], i)},
|
390
|
+
(0..9).flat_map{|i| space_second.select(2, i)},
|
391
|
+
space_both.all_by_pks(pks.map{|pk| [pk]}),
|
392
|
+
space_both.select(2, (0..9).map{|i| [i]})
|
393
|
+
]}
|
394
|
+
results[0].size.must_equal pks.size
|
395
|
+
results[1].size.must_equal pks.size/2
|
396
|
+
results[2].size.must_equal pks.size/2
|
397
|
+
(results[1]+results[2]).sort_by{|v|v[0].to_i}.must_equal results[0]
|
398
|
+
|
399
|
+
results[3].size.must_equal pks.size
|
400
|
+
results[3].sort_by{|v|v[0].to_i}.must_equal results[0]
|
401
|
+
results[4].size.must_equal pks.size/2
|
402
|
+
results[5].size.must_equal pks.size/2
|
403
|
+
(results[4]+results[5]).sort_by{|v|v[0].to_i}.must_equal results[0]
|
404
|
+
|
405
|
+
results[4].include?(['21','22','23',2]).must_equal(
|
406
|
+
results[5].include?(['31','32','33',3]))
|
407
|
+
|
408
|
+
results[6].sort_by{|v|v[0].to_i}.must_equal results[0]
|
409
|
+
results[7].sort_by{|v|v[0].to_i}.must_equal results[0]
|
410
|
+
end
|
411
|
+
|
412
|
+
it "should delete" do
|
413
|
+
results = blockrun{[
|
414
|
+
space_both.delete('21', return_tuple:true),
|
415
|
+
space_both.by_pk('21'),
|
416
|
+
space_both.delete('31', return_tuple:true),
|
417
|
+
space_both.by_pk('31')
|
418
|
+
]}
|
419
|
+
results[0].must_equal ['21','22','23',2]
|
420
|
+
results[1].must_be_nil
|
421
|
+
results[2].must_equal ['31','32','33',3]
|
422
|
+
results[3].must_be_nil
|
423
|
+
end
|
424
|
+
|
425
|
+
it "should update" do
|
426
|
+
results = blockrun{[
|
427
|
+
space_both.update('21', {1=> [:splice,3,0,'!']}, return_tuple:true),
|
428
|
+
space_both.update('31', {1=> [:splice,3,0,'!']}, return_tuple:true),
|
429
|
+
]}
|
430
|
+
results[0].must_equal ['21','22!','23',2]
|
431
|
+
results[1].must_equal ['31','32!','33',3]
|
432
|
+
end
|
433
|
+
end
|
434
|
+
|
435
|
+
describe "hash space with shard on not pk" do
|
436
|
+
let(:shard_proc0) { :modulo }
|
437
|
+
let(:shard_fields_hash0) { [:score] }
|
438
|
+
let(:space_both){ space0_hash_both }
|
439
|
+
let(:space_first){ space0_hash_first }
|
440
|
+
let(:space_second){ space0_hash_second }
|
441
|
+
let(:pks){ 100.times.to_a }
|
442
|
+
|
443
|
+
before {
|
444
|
+
blockrun{
|
445
|
+
pks.each{|i|
|
446
|
+
space_both.insert(name: i, surname: i+1, email: i+2, score: i/10)
|
447
|
+
}
|
448
|
+
}
|
449
|
+
}
|
450
|
+
let(:twenty_one){ {name:'21',surname:'22',email:'23',score: 2} }
|
451
|
+
let(:thirty_one){ {name:'31',surname:'32',email:'33',score: 3} }
|
452
|
+
|
453
|
+
it "should spread distribution over" do
|
454
|
+
results = blockrun{[
|
455
|
+
pks.flat_map{|i| space_both.all_by_pks([i])},
|
456
|
+
pks.flat_map{|i| space_first.all_by_pks([i])},
|
457
|
+
pks.flat_map{|i| space_second.all_by_pks([i])},
|
458
|
+
(0..9).flat_map{|i| space_both.select(score: i)},
|
459
|
+
(0..9).flat_map{|i| space_first.select(score: i)},
|
460
|
+
(0..9).flat_map{|i| space_second.select(score: i)},
|
461
|
+
space_both.all_by_pks(pks.map{|pk| [pk]}),
|
462
|
+
space_both.select(score: (0..9).to_a)
|
463
|
+
]}
|
464
|
+
results[0].size.must_equal pks.size
|
465
|
+
results[1].size.must_equal pks.size/2
|
466
|
+
results[2].size.must_equal pks.size/2
|
467
|
+
(results[1]+results[2]).sort_by{|v|v[:name].to_i}.must_equal results[0]
|
468
|
+
|
469
|
+
results[3].size.must_equal pks.size
|
470
|
+
results[3].sort_by{|v|v[:name].to_i}.must_equal results[0]
|
471
|
+
results[4].size.must_equal pks.size/2
|
472
|
+
results[5].size.must_equal pks.size/2
|
473
|
+
(results[4]+results[5]).sort_by{|v|v[:name].to_i}.must_equal results[0]
|
474
|
+
|
475
|
+
results[4].include?(twenty_one).must_equal results[5].include?(thirty_one)
|
476
|
+
|
477
|
+
results[6].sort_by{|v|v[:name].to_i}.must_equal results[0]
|
478
|
+
results[7].sort_by{|v|v[:name].to_i}.must_equal results[0]
|
479
|
+
end
|
480
|
+
|
481
|
+
it "should delete" do
|
482
|
+
results = blockrun{[
|
483
|
+
space_both.delete('21', return_tuple:true),
|
484
|
+
space_both.by_pk('21'),
|
485
|
+
space_both.delete('31', return_tuple:true),
|
486
|
+
space_both.by_pk('31')
|
487
|
+
]}
|
488
|
+
results[0].must_equal twenty_one
|
489
|
+
results[1].must_be_nil
|
490
|
+
results[2].must_equal thirty_one
|
491
|
+
results[3].must_be_nil
|
492
|
+
end
|
493
|
+
|
494
|
+
it "should update" do
|
495
|
+
results = blockrun{[
|
496
|
+
space_both.update('21', {surname: [:splice,3,0,'!']}, return_tuple:true),
|
497
|
+
space_both.update('31', {surname: [:splice,3,0,'!']}, return_tuple:true),
|
498
|
+
]}
|
499
|
+
results[0].must_equal twenty_one.merge(surname: '22!')
|
500
|
+
results[1].must_equal thirty_one.merge(surname: '32!')
|
501
|
+
end
|
502
|
+
end
|
503
|
+
|
504
|
+
shared_examples_for "space test call" do
|
505
|
+
before {
|
506
|
+
blockrun {
|
507
|
+
space_both.insert(one)
|
508
|
+
space_both.insert(two)
|
509
|
+
}
|
510
|
+
}
|
511
|
+
|
512
|
+
it "should call on both" do
|
513
|
+
result = blockrun{ space_both.call('box.select_range', [0, 100]) }
|
514
|
+
result.sort_by{|t| get_id(t)}.must_equal [one, two]
|
515
|
+
end
|
516
|
+
|
517
|
+
it "should call on specified" do
|
518
|
+
results = blockrun{[
|
519
|
+
space_both.call('box.select_range', [0, 100], shard_key: 1),
|
520
|
+
space_both.call('box.select_range', [0, 100], shard_keys: [2])
|
521
|
+
]}
|
522
|
+
results[0].must_equal [one]
|
523
|
+
results[1].must_equal [two]
|
524
|
+
end
|
525
|
+
end
|
526
|
+
|
527
|
+
describe "space array test call" do
|
528
|
+
let(:space_both){ space1_array_both }
|
529
|
+
let(:space_first){ space1_array_first }
|
530
|
+
let(:space_second){ space1_array_second }
|
531
|
+
|
532
|
+
let(:one) { [1, 'a', 1] }
|
533
|
+
let(:two) { [2, 'b', 2] }
|
534
|
+
def get_id(tuple) tuple[0] end
|
535
|
+
|
536
|
+
it_behaves_like "space test call"
|
537
|
+
end
|
538
|
+
|
539
|
+
describe "space hash test call" do
|
540
|
+
let(:space_both){ space1_hash_both }
|
541
|
+
let(:space_first){ space1_hash_first }
|
542
|
+
let(:space_second){ space1_hash_second }
|
543
|
+
|
544
|
+
let(:one) { {id: 1, name: 'a', val: 1} }
|
545
|
+
let(:two) { {id: 2, name: 'b', val: 2} }
|
546
|
+
def get_id(tuple) tuple[:id] end
|
547
|
+
|
548
|
+
it_behaves_like "space test call"
|
549
|
+
end
|
550
|
+
|
551
|
+
shared_examples_for "explicit shard number" do
|
552
|
+
before {
|
553
|
+
blockrun {
|
554
|
+
space_both.shard(0).insert(one)
|
555
|
+
space_both.shard(1).insert(two)
|
556
|
+
}
|
557
|
+
}
|
558
|
+
|
559
|
+
it "should not find under implicit shard" do
|
560
|
+
blockrun{[
|
561
|
+
space_both.by_pk(1),
|
562
|
+
space_both.by_pk(2),
|
563
|
+
]}.must_equal [nil, nil]
|
564
|
+
end
|
565
|
+
|
566
|
+
it "should not find under explicit but not same shard" do
|
567
|
+
blockrun{[
|
568
|
+
space_both.shard(1).by_pk(1),
|
569
|
+
space_both.shard(0).by_pk(2),
|
570
|
+
]}.must_equal [nil, nil]
|
571
|
+
end
|
572
|
+
|
573
|
+
it "should find under explict shard" do
|
574
|
+
blockrun{[
|
575
|
+
space_both.shard(0).by_pk(1),
|
576
|
+
space_both.shard(1).by_pk(2)
|
577
|
+
]}.must_equal [one, two]
|
578
|
+
end
|
579
|
+
|
580
|
+
it "should call on both" do
|
581
|
+
result = blockrun{ space_both.call('box.select_range', [0, 100]) }
|
582
|
+
result.sort_by{|t| get_id(t)}.must_equal [one, two]
|
583
|
+
end
|
584
|
+
|
585
|
+
it "should call on specified" do
|
586
|
+
results = blockrun{[
|
587
|
+
space_both.shard(1).call('box.select_range', [0, 100]),
|
588
|
+
space_both.shard(0).call('box.select_range', [0, 100])
|
589
|
+
]}
|
590
|
+
results[0].must_equal [two]
|
591
|
+
results[1].must_equal [one]
|
592
|
+
end
|
593
|
+
end
|
594
|
+
|
595
|
+
describe "space array explicit shard number" do
|
596
|
+
let(:space_both){ space1_array_both }
|
597
|
+
let(:space_first){ space1_array_first }
|
598
|
+
let(:space_second){ space1_array_second }
|
599
|
+
|
600
|
+
let(:one) { [1, 'a', 1] }
|
601
|
+
let(:two) { [2, 'b', 2] }
|
602
|
+
def get_id(tuple) tuple[0] end
|
603
|
+
|
604
|
+
it_behaves_like "explicit shard number"
|
605
|
+
end
|
606
|
+
|
607
|
+
describe "space hash explicit shard number" do
|
608
|
+
let(:space_both){ space1_hash_both }
|
609
|
+
let(:space_first){ space1_hash_first }
|
610
|
+
let(:space_second){ space1_hash_second }
|
611
|
+
|
612
|
+
let(:one) { {id: 1, name: 'a', val: 1} }
|
613
|
+
let(:two) { {id: 2, name: 'b', val: 2} }
|
614
|
+
def get_id(tuple) tuple[:id] end
|
615
|
+
|
616
|
+
it_behaves_like "explicit shard number"
|
617
|
+
end
|
618
|
+
|
619
|
+
shared_examples_for "replication tests" do
|
620
|
+
before{
|
621
|
+
blockrun{
|
622
|
+
space.insert(record1)
|
623
|
+
space.insert(record2)
|
624
|
+
}
|
625
|
+
sleep 0.12
|
626
|
+
stop_masters
|
627
|
+
}
|
628
|
+
|
629
|
+
it "should read from slave" do
|
630
|
+
results = blockrun{[
|
631
|
+
space.by_pk(2),
|
632
|
+
space.by_pk(1),
|
633
|
+
space.call('box.select_range', [0, 100])
|
634
|
+
]}
|
635
|
+
results[1].must_equal record1
|
636
|
+
results[0].must_equal record2
|
637
|
+
results[2].sort_by{|v| get_id(v)}.must_equal [record1, record2]
|
638
|
+
end
|
639
|
+
|
640
|
+
it "should raise exception when slave had not become master" do
|
641
|
+
blockrun{
|
642
|
+
[1,2].each do |i|
|
643
|
+
proc{
|
644
|
+
p space.delete(i)
|
645
|
+
}.must_raise Tarantool::NoMasterError
|
646
|
+
proc{
|
647
|
+
space.update(i, update_op)
|
648
|
+
}.must_raise Tarantool::NoMasterError
|
649
|
+
proc{
|
650
|
+
space.call('box.delete', [0, i])
|
651
|
+
}.must_raise Tarantool::NoMasterError
|
652
|
+
end
|
653
|
+
}
|
654
|
+
end
|
655
|
+
|
656
|
+
it "should perform write when slave became master" do
|
657
|
+
make_masters
|
658
|
+
results = blockrun{[
|
659
|
+
space.call('box.select_range', [0, 100]),
|
660
|
+
space.update(1, update_op, return_tuple: true),
|
661
|
+
space.delete(1, return_tuple: true),
|
662
|
+
space.update(2, update_op, return_tuple: true),
|
663
|
+
space.delete(2, return_tuple: true),
|
664
|
+
]}
|
665
|
+
results[0].sort_by{|v| get_id(v)}.must_equal [record1, record2]
|
666
|
+
results[1].must_equal updated1
|
667
|
+
results[2].must_equal updated1
|
668
|
+
results[3].must_equal updated2
|
669
|
+
results[4].must_equal updated2
|
670
|
+
end
|
671
|
+
|
672
|
+
it "should perform read from previous masters when slaves (which became masters) fails" do
|
673
|
+
make_masters
|
674
|
+
make_slaves
|
675
|
+
sleep 0.15
|
676
|
+
stop_masters :slaves
|
677
|
+
|
678
|
+
results = blockrun{[
|
679
|
+
space.by_pk(1),
|
680
|
+
space.by_pk(2),
|
681
|
+
space.call('box.select_range', [0, 100])
|
682
|
+
]}
|
683
|
+
results[0].must_equal record1
|
684
|
+
results[1].must_equal record2
|
685
|
+
results[2].sort_by{|v| get_id(v)}.must_equal [record1, record2]
|
686
|
+
end
|
687
|
+
|
688
|
+
it "should perform write to previous masters, which become masters again when slaves fails" do
|
689
|
+
make_masters
|
690
|
+
make_slaves
|
691
|
+
sleep 0.15
|
692
|
+
stop_masters :slaves
|
693
|
+
make_masters :masters
|
694
|
+
|
695
|
+
results = blockrun{[
|
696
|
+
space.call('box.select_range', [0, 100]),
|
697
|
+
space.update(1, update_op, return_tuple: true),
|
698
|
+
space.delete(1, return_tuple: true),
|
699
|
+
space.update(2, update_op, return_tuple: true),
|
700
|
+
space.delete(2, return_tuple: true),
|
701
|
+
]}
|
702
|
+
results[0].sort_by{|v| get_id(v)}.must_equal [record1, record2]
|
703
|
+
results[1].must_equal updated1
|
704
|
+
results[2].must_equal updated1
|
705
|
+
results[3].must_equal updated2
|
706
|
+
results[4].must_equal updated2
|
707
|
+
end
|
708
|
+
|
709
|
+
it "should fail when masters and slaves both down" do
|
710
|
+
stop_masters(:slaves)
|
711
|
+
blockrun{
|
712
|
+
proc {
|
713
|
+
space.by_pk(1)
|
714
|
+
}.must_raise ::Tarantool::ConnectionError
|
715
|
+
}
|
716
|
+
end
|
717
|
+
end
|
718
|
+
|
719
|
+
shared_examples_for "replication tests 2" do
|
720
|
+
def prepare_replica
|
721
|
+
space.insert(record1)
|
722
|
+
space.insert(record2)
|
723
|
+
bsleep 0.12
|
724
|
+
stop_masters
|
725
|
+
end
|
726
|
+
|
727
|
+
it "should read from slave" do
|
728
|
+
results = blockrun{
|
729
|
+
prepare_replica
|
730
|
+
[
|
731
|
+
space.by_pk(2),
|
732
|
+
space.by_pk(1),
|
733
|
+
space.call('box.select_range', [0, 100])
|
734
|
+
]}
|
735
|
+
results[1].must_equal record1
|
736
|
+
results[0].must_equal record2
|
737
|
+
results[2].sort_by{|v| get_id(v)}.must_equal [record1, record2]
|
738
|
+
end
|
739
|
+
|
740
|
+
it "should raise exception when slave had not become master" do
|
741
|
+
blockrun{
|
742
|
+
prepare_replica
|
743
|
+
[1,2].each do |i|
|
744
|
+
proc{
|
745
|
+
p space.delete(i)
|
746
|
+
}.must_raise Tarantool::NoMasterError
|
747
|
+
proc{
|
748
|
+
space.update(i, update_op)
|
749
|
+
}.must_raise Tarantool::NoMasterError
|
750
|
+
proc{
|
751
|
+
space.call('box.delete', [0, i])
|
752
|
+
}.must_raise Tarantool::NoMasterError
|
753
|
+
end
|
754
|
+
}
|
755
|
+
end
|
756
|
+
|
757
|
+
it "should perform write when slave became master" do
|
758
|
+
results = blockrun{
|
759
|
+
prepare_replica
|
760
|
+
make_masters
|
761
|
+
[
|
762
|
+
space.call('box.select_range', [0, 100]),
|
763
|
+
space.update(1, update_op, return_tuple: true),
|
764
|
+
space.delete(1, return_tuple: true),
|
765
|
+
space.update(2, update_op, return_tuple: true),
|
766
|
+
space.delete(2, return_tuple: true),
|
767
|
+
]}
|
768
|
+
results[0].sort_by{|v| get_id(v)}.must_equal [record1, record2]
|
769
|
+
results[1].must_equal updated1
|
770
|
+
results[2].must_equal updated1
|
771
|
+
results[3].must_equal updated2
|
772
|
+
results[4].must_equal updated2
|
773
|
+
end
|
774
|
+
|
775
|
+
it "should perform read from previous masters when slaves (which became masters) fails" do
|
776
|
+
results = blockrun{
|
777
|
+
prepare_replica
|
778
|
+
make_masters
|
779
|
+
make_slaves
|
780
|
+
bsleep 0.15
|
781
|
+
stop_masters :slaves
|
782
|
+
[
|
783
|
+
space.by_pk(1),
|
784
|
+
space.by_pk(2),
|
785
|
+
space.call('box.select_range', [0, 100])
|
786
|
+
]}
|
787
|
+
results[0].must_equal record1
|
788
|
+
results[1].must_equal record2
|
789
|
+
results[2].sort_by{|v| get_id(v)}.must_equal [record1, record2]
|
790
|
+
end
|
791
|
+
|
792
|
+
it "should perform write to previous masters, which become masters again when slaves fails" do
|
793
|
+
|
794
|
+
results = blockrun{
|
795
|
+
prepare_replica
|
796
|
+
make_masters
|
797
|
+
make_slaves
|
798
|
+
bsleep 0.15
|
799
|
+
stop_masters :slaves
|
800
|
+
make_masters :masters
|
801
|
+
[
|
802
|
+
space.call('box.select_range', [0, 100]),
|
803
|
+
space.update(1, update_op, return_tuple: true),
|
804
|
+
space.delete(1, return_tuple: true),
|
805
|
+
space.update(2, update_op, return_tuple: true),
|
806
|
+
space.delete(2, return_tuple: true),
|
807
|
+
]}
|
808
|
+
results[0].sort_by{|v| get_id(v)}.must_equal [record1, record2]
|
809
|
+
results[1].must_equal updated1
|
810
|
+
results[2].must_equal updated1
|
811
|
+
results[3].must_equal updated2
|
812
|
+
results[4].must_equal updated2
|
813
|
+
end
|
814
|
+
|
815
|
+
|
816
|
+
it "should fail when masters and slaves both down" do
|
817
|
+
blockrun{
|
818
|
+
prepare_replica
|
819
|
+
stop_masters(:slaves)
|
820
|
+
proc {
|
821
|
+
space.by_pk(1)
|
822
|
+
}.must_raise ::Tarantool::ConnectionError
|
823
|
+
}
|
824
|
+
end
|
825
|
+
end
|
826
|
+
|
827
|
+
shared_examples_for "space array replication" do
|
828
|
+
let(:record1){ [1, 'a', 1] }
|
829
|
+
let(:record2){ [2, 'a', 2] }
|
830
|
+
let(:update_op){ {1 => 'b'} }
|
831
|
+
let(:updated1){ [1, 'b', 1] }
|
832
|
+
let(:updated2){ [2, 'b', 2] }
|
833
|
+
def get_id(v) v[0] end
|
834
|
+
end
|
835
|
+
|
836
|
+
shared_examples_for "space hash replication" do
|
837
|
+
let(:record1){ {id: 1, name: 'a', val: 1} }
|
838
|
+
let(:record2){ {id: 2, name: 'a', val: 2} }
|
839
|
+
let(:update_op){ {name: 'b'} }
|
840
|
+
let(:updated1){ {id: 1, name: 'b', val: 1} }
|
841
|
+
let(:updated2){ {id: 2, name: 'b', val: 2} }
|
842
|
+
def get_id(v) v[:id] end
|
843
|
+
end
|
844
|
+
|
845
|
+
shared_examples_for "single replication" do
|
846
|
+
def stop_masters(which=:masters)
|
847
|
+
if which == :masters
|
848
|
+
TConf.stop(:master1)
|
849
|
+
else
|
850
|
+
TConf.stop(:slave1)
|
851
|
+
end
|
852
|
+
end
|
853
|
+
def make_masters(which=:slaves)
|
854
|
+
if which == :slaves
|
855
|
+
TConf.promote_to_master(:slave1)
|
856
|
+
else
|
857
|
+
TConf.promote_to_master(:master1)
|
858
|
+
end
|
859
|
+
end
|
860
|
+
def make_slaves
|
861
|
+
TConf.promote_to_slave(:master1, :slave1)
|
862
|
+
end
|
863
|
+
end
|
864
|
+
|
865
|
+
shared_examples_for "shard and replication" do
|
866
|
+
def stop_masters(which=:masters)
|
867
|
+
if which == :masters
|
868
|
+
TConf.stop(:master1)
|
869
|
+
TConf.stop(:master2)
|
870
|
+
else
|
871
|
+
TConf.stop(:slave1)
|
872
|
+
TConf.stop(:slave2)
|
873
|
+
end
|
874
|
+
end
|
875
|
+
def make_masters(which=:slaves)
|
876
|
+
if which == :slaves
|
877
|
+
TConf.promote_to_master(:slave1)
|
878
|
+
TConf.promote_to_master(:slave2)
|
879
|
+
else
|
880
|
+
TConf.promote_to_master(:master1)
|
881
|
+
TConf.promote_to_master(:master2)
|
882
|
+
end
|
883
|
+
end
|
884
|
+
def make_slaves
|
885
|
+
TConf.promote_to_slave(:master1, :slave1)
|
886
|
+
TConf.promote_to_slave(:master2, :slave2)
|
887
|
+
end
|
888
|
+
end
|
889
|
+
|
890
|
+
describe "space array single replication" do
|
891
|
+
let(:space){ space1_array_first }
|
892
|
+
|
893
|
+
it_behaves_like "space array replication"
|
894
|
+
it_behaves_like "single replication"
|
895
|
+
it_behaves_like "replication tests"
|
896
|
+
end
|
897
|
+
|
898
|
+
describe "space hash single replication" do
|
899
|
+
let(:space){ space1_hash_first }
|
900
|
+
|
901
|
+
it_behaves_like "space hash replication"
|
902
|
+
it_behaves_like "single replication"
|
903
|
+
it_behaves_like "replication tests"
|
904
|
+
end
|
905
|
+
|
906
|
+
describe "space hash shard and replication" do
|
907
|
+
let(:space){ space1_hash_both }
|
908
|
+
|
909
|
+
it_behaves_like "space hash replication"
|
910
|
+
it_behaves_like "shard and replication"
|
911
|
+
it_behaves_like "replication tests"
|
912
|
+
end
|
913
|
+
|
914
|
+
describe "space array shard and replication" do
|
915
|
+
let(:space){ space1_array_both }
|
916
|
+
|
917
|
+
it_behaves_like "space array replication"
|
918
|
+
it_behaves_like "shard and replication"
|
919
|
+
it_behaves_like "replication tests"
|
920
|
+
end
|
921
|
+
|
922
|
+
describe "space array single replication 2" do
|
923
|
+
let(:space){ space1_array_first }
|
924
|
+
|
925
|
+
it_behaves_like "space array replication"
|
926
|
+
it_behaves_like "single replication"
|
927
|
+
it_behaves_like "replication tests 2"
|
928
|
+
end
|
929
|
+
|
930
|
+
describe "space hash single replication 2" do
|
931
|
+
let(:space){ space1_hash_first }
|
932
|
+
|
933
|
+
it_behaves_like "space hash replication"
|
934
|
+
it_behaves_like "single replication"
|
935
|
+
it_behaves_like "replication tests 2"
|
936
|
+
end
|
937
|
+
|
938
|
+
describe "space hash shard and replication 2" do
|
939
|
+
let(:space){ space1_hash_both }
|
940
|
+
|
941
|
+
it_behaves_like "space hash replication"
|
942
|
+
it_behaves_like "shard and replication"
|
943
|
+
it_behaves_like "replication tests 2"
|
944
|
+
end
|
945
|
+
|
946
|
+
describe "space array shard and replication 2" do
|
947
|
+
let(:space){ space1_array_both }
|
948
|
+
|
949
|
+
it_behaves_like "space array replication"
|
950
|
+
it_behaves_like "shard and replication"
|
951
|
+
it_behaves_like "replication tests 2"
|
952
|
+
end
|
953
|
+
|
954
|
+
describe "record api" do
|
955
|
+
let(:klass) do
|
956
|
+
t_both = t_both()
|
957
|
+
Class.new(Tarantool::LightRecord) do
|
958
|
+
set_tarantool t_both
|
959
|
+
set_space_no 0
|
960
|
+
|
961
|
+
field :name, :str
|
962
|
+
field :surname, :str
|
963
|
+
field :email, :str
|
964
|
+
field :score, :int
|
965
|
+
|
966
|
+
index :surname, :email
|
967
|
+
index :score
|
968
|
+
|
969
|
+
shard_fields :score
|
970
|
+
shard_proc do |fields, shard_num|
|
971
|
+
fields[0] && ((fields[0].to_i + 1) / 2 + 1) % shard_num
|
972
|
+
end
|
973
|
+
end
|
974
|
+
end
|
975
|
+
|
976
|
+
before do
|
977
|
+
blockrun {
|
978
|
+
klass.create(name: 'a', surname: 'a', email: 'a', score: 1)
|
979
|
+
klass.create(name: 'b', surname: 'b', email: 'b', score: 2)
|
980
|
+
klass.create(name: 'c', surname: 'c', email: 'c', score: 3)
|
981
|
+
klass.create(name: 'd', surname: 'd', email: 'd', score: 4)
|
982
|
+
}
|
983
|
+
end
|
984
|
+
|
985
|
+
it "should spread distribution" do
|
986
|
+
results = blockrun {[
|
987
|
+
space0_hash_first.all_by_pks(%w{a b c d}),
|
988
|
+
space0_hash_second.all_by_pks(%w{a b c d})
|
989
|
+
]}
|
990
|
+
results[0].map{|v| v[:name]}.sort.must_equal %w{a b}
|
991
|
+
results[0].map{|v| v[:score]}.sort.must_equal [1, 2]
|
992
|
+
results[1].map{|v| v[:name]}.sort.must_equal %w{c d}
|
993
|
+
results[1].map{|v| v[:score]}.sort.must_equal [3, 4]
|
994
|
+
end
|
995
|
+
|
996
|
+
it "should find by pk" do
|
997
|
+
results = blockrun {[
|
998
|
+
klass.find('a'),
|
999
|
+
klass.find('b'),
|
1000
|
+
klass.find('c'),
|
1001
|
+
klass.find('d')
|
1002
|
+
]}
|
1003
|
+
results.map(&:name).must_equal %w{a b c d}
|
1004
|
+
results.map(&:score).must_equal [1,2,3,4]
|
1005
|
+
end
|
1006
|
+
|
1007
|
+
it "should find by shard key" do
|
1008
|
+
results = blockrun {[
|
1009
|
+
klass.first(score: 1),
|
1010
|
+
klass.first(score: 2),
|
1011
|
+
klass.first(score: 3),
|
1012
|
+
klass.first(score: 4),
|
1013
|
+
]}
|
1014
|
+
results.map(&:name).must_equal %w{a b c d}
|
1015
|
+
results.map(&:score).must_equal [1,2,3,4]
|
1016
|
+
end
|
1017
|
+
end
|
1018
|
+
end
|