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
@@ -0,0 +1,380 @@
|
|
1
|
+
require File.expand_path('../helper.rb', __FILE__)
|
2
|
+
require 'tarantool/light_record'
|
3
|
+
|
4
|
+
shared_examples_for 'resharding' do
|
5
|
+
before { TConf.reset_and_up_masters }
|
6
|
+
#after { TConf.clear_masters }
|
7
|
+
|
8
|
+
let(:t_before) {
|
9
|
+
Tarantool.new(type: tarantool_type,
|
10
|
+
servers: [ [TConf.conf(:master1)], [TConf.conf(:master2)] ],
|
11
|
+
previous_shards_count: 1,
|
12
|
+
insert_to_previous_shard: true)
|
13
|
+
}
|
14
|
+
|
15
|
+
let(:t_after) {
|
16
|
+
Tarantool.new(type: tarantool_type,
|
17
|
+
servers: [ [TConf.conf(:master1)], [TConf.conf(:master2)] ],
|
18
|
+
previous_shards_count: 1)
|
19
|
+
}
|
20
|
+
|
21
|
+
let(:t_past) {
|
22
|
+
Tarantool.new(type: tarantool_type,
|
23
|
+
servers: [ [TConf.conf(:master1)], [TConf.conf(:master2)] ])
|
24
|
+
}
|
25
|
+
|
26
|
+
let(:t_after) {
|
27
|
+
Tarantool.new(type: tarantool_type,
|
28
|
+
servers: [ [TConf.conf(:master1)], [TConf.conf(:master2)] ],
|
29
|
+
previous_shards_count: 1)
|
30
|
+
}
|
31
|
+
|
32
|
+
let(:t_first) {
|
33
|
+
Tarantool.new(type: tarantool_type, servers: TConf.conf(:master1))
|
34
|
+
}
|
35
|
+
|
36
|
+
let(:t_second) {
|
37
|
+
Tarantool.new(type: tarantool_type, servers: TConf.conf(:master2))
|
38
|
+
}
|
39
|
+
|
40
|
+
HSPACE1_ = {
|
41
|
+
fields: {id: :int, name: :str, val: :int},
|
42
|
+
keys: :id
|
43
|
+
}
|
44
|
+
|
45
|
+
let(:space_array_before) {
|
46
|
+
t_before.space(1, SPACE1[:types], keys: SPACE1[:keys], shard_fields: [0], shard_proc: :modulo)
|
47
|
+
}
|
48
|
+
let(:space_array_after) {
|
49
|
+
t_after.space(1, SPACE1[:types], keys: SPACE1[:keys], shard_fields: [0], shard_proc: :modulo)
|
50
|
+
}
|
51
|
+
let(:space_array_past) {
|
52
|
+
t_past.space(1, SPACE1[:types], keys: SPACE1[:keys], shard_fields: [0], shard_proc: :modulo)
|
53
|
+
}
|
54
|
+
let(:space_array_first) { t_first.space(1, SPACE1[:types], keys: SPACE1[:keys]) }
|
55
|
+
let(:space_array_second) { t_second.space(1, SPACE1[:types], keys: SPACE1[:keys]) }
|
56
|
+
|
57
|
+
let(:space_hash_before) {
|
58
|
+
t_before.space(1, HSPACE1_[:fields], keys: HSPACE1_[:keys], shard_fields: [:id], shard_proc: :modulo)
|
59
|
+
}
|
60
|
+
let(:space_hash_after) {
|
61
|
+
t_after.space(1, HSPACE1_[:fields], keys: HSPACE1_[:keys], shard_fields: [:id], shard_proc: :modulo)
|
62
|
+
}
|
63
|
+
let(:space_hash_past) {
|
64
|
+
t_past.space(1, HSPACE1_[:fields], keys: HSPACE1_[:keys], shard_fields: [:id], shard_proc: :modulo)
|
65
|
+
}
|
66
|
+
let(:space_hash_first) { t_first.space(1, HSPACE1_[:fields], keys: HSPACE1_[:keys]) }
|
67
|
+
let(:space_hash_second) { t_second.space(1, HSPACE1_[:fields], keys: HSPACE1_[:keys]) }
|
68
|
+
|
69
|
+
shared_examples_for "first step reshard" do
|
70
|
+
before do
|
71
|
+
blockrun {
|
72
|
+
space_first.insert(one)
|
73
|
+
space_first.insert(two)
|
74
|
+
space_second.insert(five)
|
75
|
+
}
|
76
|
+
end
|
77
|
+
|
78
|
+
it "should read from both shards" do
|
79
|
+
results = blockrun {[
|
80
|
+
space_before.by_pk(1),
|
81
|
+
space_before.by_pk(2),
|
82
|
+
space_before.by_pk(5),
|
83
|
+
space_before.all_by_pks([1, 2, 5])
|
84
|
+
]}
|
85
|
+
results[0].must_equal one
|
86
|
+
results[1].must_equal two
|
87
|
+
results[2].must_equal five
|
88
|
+
results[3].sort_by(&get_id).must_equal [one, two, five]
|
89
|
+
end
|
90
|
+
|
91
|
+
it "should insert into old shard" do
|
92
|
+
results = blockrun {
|
93
|
+
space_before.insert(three)
|
94
|
+
space_before.insert(four)
|
95
|
+
[space_first.by_pk(3),
|
96
|
+
space_first.by_pk(4),
|
97
|
+
space_second.by_pk(3),
|
98
|
+
space_second.by_pk(4)
|
99
|
+
]
|
100
|
+
}
|
101
|
+
results[0].must_equal three
|
102
|
+
results[1].must_equal four
|
103
|
+
results[2].must_be_nil
|
104
|
+
results[3].must_be_nil
|
105
|
+
end
|
106
|
+
|
107
|
+
it "should update into both shards" do
|
108
|
+
results = blockrun {[
|
109
|
+
space_before.update(1, increment),
|
110
|
+
space_before.update(5, increment),
|
111
|
+
space_first.by_pk(1),
|
112
|
+
space_second.by_pk(5)
|
113
|
+
]}
|
114
|
+
results[0].must_equal 1
|
115
|
+
results[1].must_equal 1
|
116
|
+
results[2].must_equal incremented(one)
|
117
|
+
results[3].must_equal incremented(five)
|
118
|
+
end
|
119
|
+
|
120
|
+
it "should replace into both shards" do
|
121
|
+
results = blockrun {[
|
122
|
+
space_before.replace(incremented(one)),
|
123
|
+
space_before.replace(incremented(five)),
|
124
|
+
space_first.by_pk(1),
|
125
|
+
space_second.by_pk(5)
|
126
|
+
]}
|
127
|
+
results[0].must_equal 1
|
128
|
+
results[1].must_equal 1
|
129
|
+
results[2].must_equal incremented(one)
|
130
|
+
results[3].must_equal incremented(five)
|
131
|
+
end
|
132
|
+
|
133
|
+
it "should delete from both shards" do
|
134
|
+
results = blockrun {[
|
135
|
+
space_before.delete(1, return_tuple: true),
|
136
|
+
space_before.delete(5, return_tuple: true),
|
137
|
+
space_first.by_pk(1),
|
138
|
+
space_second.by_pk(5)
|
139
|
+
]}
|
140
|
+
results[0].must_equal one
|
141
|
+
results[1].must_equal five
|
142
|
+
results[2].must_equal nil
|
143
|
+
results[3].must_equal nil
|
144
|
+
end
|
145
|
+
end
|
146
|
+
|
147
|
+
shared_examples_for "second step reshard" do
|
148
|
+
before do
|
149
|
+
blockrun {
|
150
|
+
space_first.insert(one)
|
151
|
+
space_first.insert(two)
|
152
|
+
space_second.insert(five)
|
153
|
+
}
|
154
|
+
end
|
155
|
+
|
156
|
+
it "should read from both shards" do
|
157
|
+
results = blockrun {[
|
158
|
+
space_after.by_pk(1),
|
159
|
+
space_after.by_pk(2),
|
160
|
+
space_after.by_pk(5),
|
161
|
+
space_after.all_by_pks([1, 2, 5])
|
162
|
+
]}
|
163
|
+
results[0].must_equal one
|
164
|
+
results[1].must_equal two
|
165
|
+
results[2].must_equal five
|
166
|
+
results[3].sort_by(&get_id).must_equal [one, two, five]
|
167
|
+
end
|
168
|
+
|
169
|
+
it "should insert into right shard" do
|
170
|
+
results = blockrun {
|
171
|
+
space_after.insert(three)
|
172
|
+
space_after.insert(four)
|
173
|
+
[space_first.by_pk(3),
|
174
|
+
space_first.by_pk(4),
|
175
|
+
space_second.by_pk(3),
|
176
|
+
space_second.by_pk(4)
|
177
|
+
]
|
178
|
+
}
|
179
|
+
results[0].must_be_nil
|
180
|
+
results[1].must_equal four
|
181
|
+
results[2].must_equal three
|
182
|
+
results[3].must_be_nil
|
183
|
+
end
|
184
|
+
|
185
|
+
it "should update into both shards" do
|
186
|
+
results = blockrun {[
|
187
|
+
space_after.update(1, increment),
|
188
|
+
space_after.update(5, increment),
|
189
|
+
space_first.by_pk(1),
|
190
|
+
space_second.by_pk(5)
|
191
|
+
]}
|
192
|
+
results[0].must_equal 1
|
193
|
+
results[1].must_equal 1
|
194
|
+
results[2].must_equal incremented(one)
|
195
|
+
results[3].must_equal incremented(five)
|
196
|
+
end
|
197
|
+
|
198
|
+
it "should replace into both shards" do
|
199
|
+
results = blockrun {[
|
200
|
+
space_after.replace(incremented(one)),
|
201
|
+
space_after.replace(incremented(five)),
|
202
|
+
space_first.by_pk(1),
|
203
|
+
space_second.by_pk(5)
|
204
|
+
]}
|
205
|
+
results[0].must_equal 1
|
206
|
+
results[1].must_equal 1
|
207
|
+
results[2].must_equal incremented(one)
|
208
|
+
results[3].must_equal incremented(five)
|
209
|
+
end
|
210
|
+
|
211
|
+
it "should delete from both shards" do
|
212
|
+
results = blockrun {[
|
213
|
+
space_after.delete(1, return_tuple: true),
|
214
|
+
space_after.delete(5, return_tuple: true),
|
215
|
+
space_first.by_pk(1),
|
216
|
+
space_second.by_pk(5)
|
217
|
+
]}
|
218
|
+
results[0].must_equal one
|
219
|
+
results[1].must_equal five
|
220
|
+
results[2].must_equal nil
|
221
|
+
results[3].must_equal nil
|
222
|
+
end
|
223
|
+
end
|
224
|
+
|
225
|
+
shared_examples_for "after reshard" do
|
226
|
+
before do
|
227
|
+
blockrun {
|
228
|
+
space_first.insert(one)
|
229
|
+
space_first.insert(two)
|
230
|
+
space_second.insert(five)
|
231
|
+
}
|
232
|
+
end
|
233
|
+
|
234
|
+
it "should read only from right shards" do
|
235
|
+
results = blockrun {[
|
236
|
+
space_past.by_pk(1),
|
237
|
+
space_past.by_pk(2),
|
238
|
+
space_past.by_pk(5)
|
239
|
+
]}
|
240
|
+
results[0].must_be_nil
|
241
|
+
results[1].must_equal two
|
242
|
+
results[2].must_equal five
|
243
|
+
end
|
244
|
+
|
245
|
+
it "still reads from both shards when query many keys" do
|
246
|
+
results = blockrun {
|
247
|
+
space_past.all_by_pks([1, 2, 5])
|
248
|
+
}
|
249
|
+
# that is cause we do not separate by shards when there is many keys
|
250
|
+
results.sort_by(&get_id).must_equal [one, two, five]
|
251
|
+
end
|
252
|
+
|
253
|
+
it "should insert into right shard" do
|
254
|
+
results = blockrun {
|
255
|
+
space_past.insert(three)
|
256
|
+
space_past.insert(four)
|
257
|
+
[space_first.by_pk(3),
|
258
|
+
space_first.by_pk(4),
|
259
|
+
space_second.by_pk(3),
|
260
|
+
space_second.by_pk(4)
|
261
|
+
]
|
262
|
+
}
|
263
|
+
results[0].must_be_nil
|
264
|
+
results[1].must_equal four
|
265
|
+
results[2].must_equal three
|
266
|
+
results[3].must_be_nil
|
267
|
+
end
|
268
|
+
|
269
|
+
it "should try update only right shards" do
|
270
|
+
results = blockrun {[
|
271
|
+
space_past.update(1, increment),
|
272
|
+
space_past.update(5, increment),
|
273
|
+
space_first.by_pk(1),
|
274
|
+
space_second.by_pk(5)
|
275
|
+
]}
|
276
|
+
results[0].must_equal 0
|
277
|
+
results[1].must_equal 1
|
278
|
+
results[2].must_equal one
|
279
|
+
results[3].must_equal incremented(five)
|
280
|
+
end
|
281
|
+
|
282
|
+
it "should replace only right shards" do
|
283
|
+
results = blockrun {
|
284
|
+
proc {
|
285
|
+
space_past.replace(incremented(one))
|
286
|
+
}.must_raise ::Tarantool::TupleDoesntExists
|
287
|
+
[
|
288
|
+
space_past.replace(incremented(five)),
|
289
|
+
space_second.by_pk(5)
|
290
|
+
]}
|
291
|
+
results[0].must_equal 1
|
292
|
+
results[1].must_equal incremented(five)
|
293
|
+
end
|
294
|
+
|
295
|
+
it "should delete from right shards" do
|
296
|
+
results = blockrun {[
|
297
|
+
space_past.delete(1, return_tuple: true),
|
298
|
+
space_past.delete(5, return_tuple: true),
|
299
|
+
space_first.by_pk(1),
|
300
|
+
space_second.by_pk(5)
|
301
|
+
]}
|
302
|
+
results[0].must_equal nil
|
303
|
+
results[1].must_equal five
|
304
|
+
results[2].must_equal one
|
305
|
+
results[3].must_equal nil
|
306
|
+
end
|
307
|
+
end
|
308
|
+
|
309
|
+
describe "array space reshard" do
|
310
|
+
let(:space_before) { space_array_before }
|
311
|
+
let(:space_after) { space_array_after }
|
312
|
+
let(:space_past) { space_array_past }
|
313
|
+
let(:space_first) { space_array_first }
|
314
|
+
let(:space_second) { space_array_second }
|
315
|
+
|
316
|
+
let(:one) { [1, '1', 1] }
|
317
|
+
let(:two) { [2, '2', 2] }
|
318
|
+
let(:three) { [3, '3', 3] }
|
319
|
+
let(:four) { [4, '4', 4] }
|
320
|
+
let(:five) { [5, '5', 5] }
|
321
|
+
let(:increment) { {2 => [:+, 1] } }
|
322
|
+
def incremented(tuple)
|
323
|
+
tuple.dup.tap{|t| t[2]+=1}
|
324
|
+
end
|
325
|
+
def get_id
|
326
|
+
proc{|tuple| tuple[0]}
|
327
|
+
end
|
328
|
+
|
329
|
+
describe "first step" do
|
330
|
+
it_behaves_like "first step reshard"
|
331
|
+
end
|
332
|
+
|
333
|
+
describe "second step" do
|
334
|
+
it_behaves_like "second step reshard"
|
335
|
+
end
|
336
|
+
|
337
|
+
describe "after reshard" do
|
338
|
+
it_behaves_like "after reshard"
|
339
|
+
end
|
340
|
+
end
|
341
|
+
|
342
|
+
describe "hash space reshard" do
|
343
|
+
let(:space_before) { space_hash_before }
|
344
|
+
let(:space_after) { space_hash_after }
|
345
|
+
let(:space_past) { space_hash_past }
|
346
|
+
let(:space_first) { space_hash_first }
|
347
|
+
let(:space_second) { space_hash_second }
|
348
|
+
|
349
|
+
def tuple_by_i(i)
|
350
|
+
{id: i, name: i.to_s, val: i}
|
351
|
+
end
|
352
|
+
|
353
|
+
let(:one) { tuple_by_i(1) }
|
354
|
+
let(:two) { tuple_by_i(2) }
|
355
|
+
let(:three) { tuple_by_i(3) }
|
356
|
+
let(:four) { tuple_by_i(4) }
|
357
|
+
let(:five) { tuple_by_i(5) }
|
358
|
+
let(:increment) { {val: [:+, 1] } }
|
359
|
+
def incremented(tuple)
|
360
|
+
tuple.dup.tap{|t| t[:val]+=1}
|
361
|
+
end
|
362
|
+
def get_id
|
363
|
+
proc{|tuple| tuple[:id]}
|
364
|
+
end
|
365
|
+
|
366
|
+
|
367
|
+
describe "first step" do
|
368
|
+
it_behaves_like "first step reshard"
|
369
|
+
end
|
370
|
+
|
371
|
+
describe "second step" do
|
372
|
+
it_behaves_like "second step reshard"
|
373
|
+
end
|
374
|
+
|
375
|
+
describe "after reshard" do
|
376
|
+
it_behaves_like "after reshard"
|
377
|
+
end
|
378
|
+
end
|
379
|
+
|
380
|
+
end
|
data/test/tarantool.cfg
CHANGED
data/test/test_light_record.rb
CHANGED
@@ -0,0 +1,92 @@
|
|
1
|
+
require File.expand_path('../helper.rb', __FILE__)
|
2
|
+
require 'tarantool/light_record'
|
3
|
+
|
4
|
+
describe 'Tarantool::LightRecord::Callback' do
|
5
|
+
before { TConf.run(:master1) }
|
6
|
+
before { truncate }
|
7
|
+
|
8
|
+
let(:db) { Tarantool.new(TCONFIG.merge(type: :em_fiber)) }
|
9
|
+
let(:klass) {
|
10
|
+
db = db()
|
11
|
+
Class.new(Tarantool::LightRecord) do
|
12
|
+
set_tarantool db
|
13
|
+
set_space_no 1
|
14
|
+
|
15
|
+
field :id, :int
|
16
|
+
field :name, :string
|
17
|
+
field :val, :int
|
18
|
+
end
|
19
|
+
}
|
20
|
+
let(:auto_space) { klass.auto_space }
|
21
|
+
let(:hash1) { {id: 1, name: 'hello', val: 1} }
|
22
|
+
let(:hash2) { {id: 2, name: 'hello', val: 2} }
|
23
|
+
|
24
|
+
it "should be able to insert" do
|
25
|
+
emrun(2) {
|
26
|
+
auto_space.insert_blk(hash1, &setp(0))
|
27
|
+
auto_space.insert_blk(hash2, return_tuple: true, &setp(1))
|
28
|
+
}
|
29
|
+
results[0].must_equal 1
|
30
|
+
results[1].must_be_kind_of klass
|
31
|
+
results[1].attributes.must_equal hash2
|
32
|
+
end
|
33
|
+
|
34
|
+
describe "manipulations" do
|
35
|
+
before {
|
36
|
+
emrun(2) {
|
37
|
+
auto_space.insert_blk(hash1){emstop}
|
38
|
+
auto_space.insert_blk(hash2, return_tuple: true){emstop}
|
39
|
+
}
|
40
|
+
}
|
41
|
+
it "should be able to select" do
|
42
|
+
emrun(5) {
|
43
|
+
auto_space.select_blk({id: 1}, &setp(0))
|
44
|
+
auto_space.select_blk({id: 2}, &setp(1))
|
45
|
+
auto_space.select_blk({id: [1,2]}, &setp(2))
|
46
|
+
auto_space.select_blk([{id: 1}, {id:2}], &setp(3))
|
47
|
+
auto_space.by_pk_blk(1, &setp(4))
|
48
|
+
}
|
49
|
+
results[0][0].must_be_kind_of klass
|
50
|
+
results[0][0].attributes.must_equal hash1
|
51
|
+
results[1][0].must_be_kind_of klass
|
52
|
+
results[1][0].attributes.must_equal hash2
|
53
|
+
results[2].size.must_equal 2
|
54
|
+
results[2].map(&:attributes).sort_by{|a| a[:id]}.must_equal [hash1, hash2]
|
55
|
+
results[3].size.must_equal 2
|
56
|
+
results[3].map(&:attributes).sort_by{|a| a[:id]}.must_equal [hash1, hash2]
|
57
|
+
results[4].must_be_kind_of klass
|
58
|
+
results[4].attributes.must_equal hash1
|
59
|
+
end
|
60
|
+
|
61
|
+
it "should be able to update" do
|
62
|
+
emrun(2) {
|
63
|
+
auto_space.update_blk(1, {name: "world"}, &setp(0))
|
64
|
+
auto_space.update_blk({id:2}, {name: "kill"}, return_tuple: true, &setp(1))
|
65
|
+
}
|
66
|
+
results[0].must_equal 1
|
67
|
+
results[1].must_be_kind_of klass
|
68
|
+
results[1].attributes.must_equal hash2.merge(name: "kill")
|
69
|
+
end
|
70
|
+
|
71
|
+
it "should be able to delete" do
|
72
|
+
emrun(2) {
|
73
|
+
auto_space.delete_blk(1, &setp(0))
|
74
|
+
auto_space.delete_blk({id:2}, return_tuple: true, &setp(1))
|
75
|
+
}
|
76
|
+
results[0].must_equal 1
|
77
|
+
results[1].must_be_kind_of klass
|
78
|
+
results[1].attributes.must_equal hash2
|
79
|
+
end
|
80
|
+
|
81
|
+
it "should be able to call" do
|
82
|
+
emrun(1) {
|
83
|
+
auto_space.call_blk('box.select_range', [0, 100], &setp(0))
|
84
|
+
}
|
85
|
+
results[0].size.must_equal 2
|
86
|
+
results[0][0].must_be_kind_of klass
|
87
|
+
results[0][1].must_be_kind_of klass
|
88
|
+
results[0].map(&:attributes).sort_by{|a| a[:id]}.must_equal [hash1, hash2]
|
89
|
+
end
|
90
|
+
end
|
91
|
+
|
92
|
+
end
|