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.
Files changed (51) hide show
  1. data/Gemfile +2 -3
  2. data/README.md +90 -30
  3. data/Rakefile +6 -1
  4. data/lib/tarantool.rb +93 -18
  5. data/lib/tarantool/base_record.rb +97 -10
  6. data/lib/tarantool/block_db.rb +104 -6
  7. data/lib/tarantool/callback_db.rb +7 -0
  8. data/lib/tarantool/core-ext.rb +24 -8
  9. data/lib/tarantool/em_db.rb +189 -20
  10. data/lib/tarantool/exceptions.rb +4 -0
  11. data/lib/tarantool/fiber_db.rb +15 -1
  12. data/lib/tarantool/light_record.rb +17 -0
  13. data/lib/tarantool/query.rb +15 -9
  14. data/lib/tarantool/record/select.rb +21 -3
  15. data/lib/tarantool/request.rb +130 -43
  16. data/lib/tarantool/response.rb +70 -7
  17. data/lib/tarantool/serializers.rb +26 -5
  18. data/lib/tarantool/serializers/ber_array.rb +14 -0
  19. data/lib/tarantool/shards_support.rb +204 -0
  20. data/lib/tarantool/space_array.rb +38 -13
  21. data/lib/tarantool/space_hash.rb +49 -27
  22. data/lib/tarantool/util.rb +96 -10
  23. data/lib/tarantool/version.rb +2 -1
  24. data/test/helper.rb +154 -4
  25. data/test/{tarant/init.lua → init.lua} +0 -0
  26. data/test/run_all.rb +2 -2
  27. data/test/shared_record.rb +59 -0
  28. data/test/shared_replicated_shard.rb +1018 -0
  29. data/test/shared_reshard.rb +380 -0
  30. data/test/tarantool.cfg +2 -0
  31. data/test/test_light_record.rb +2 -0
  32. data/test/test_light_record_callback.rb +92 -0
  33. data/test/test_query_block.rb +1 -0
  34. data/test/test_query_fiber.rb +1 -0
  35. data/test/test_reshard_block.rb +7 -0
  36. data/test/test_reshard_fiber.rb +11 -0
  37. data/test/test_shard_replication_block.rb +7 -0
  38. data/test/test_shard_replication_fiber.rb +11 -0
  39. data/test/test_space_array_block.rb +1 -0
  40. data/test/test_space_array_callback.rb +50 -121
  41. data/test/test_space_array_callback_nodef.rb +39 -96
  42. data/test/test_space_array_fiber.rb +1 -0
  43. data/test/test_space_hash_block.rb +1 -0
  44. data/test/test_space_hash_fiber.rb +1 -0
  45. metadata +54 -17
  46. data/lib/tarantool/record.rb +0 -164
  47. data/test/box.pid +0 -1
  48. data/test/tarantool.log +0 -6
  49. data/test/tarantool_repl.cfg +0 -53
  50. data/test/test_record.rb +0 -88
  51. 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
@@ -7,6 +7,8 @@ work_dir="tarant"
7
7
  primary_port = 33013
8
8
  secondary_port = 33014
9
9
  admin_port = 33015
10
+ replication_port = 33016
11
+ #replication_source = "localhost:32016"
10
12
 
11
13
  rows_per_wal = 50000
12
14
 
@@ -3,6 +3,8 @@ require File.expand_path('../shared_record', __FILE__)
3
3
  require 'tarantool/light_record'
4
4
 
5
5
  describe 'Tarantool::LightRecord' do
6
+ before { TConf.run(:master1) }
7
+
6
8
  let(:base_class){ Tarantool::LightRecord }
7
9
  it_behaves_like :record
8
10
 
@@ -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