tarantool 0.3.0.7 → 0.4.2.1

Sign up to get free protection for your applications and to get access to all the features.
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
@@ -5,9 +5,10 @@ require 'tarantool/core-ext'
5
5
 
6
6
  module Tarantool
7
7
  class SpaceHash
8
+ include CommonSpace
8
9
  include Request
9
10
 
10
- def initialize(tarantool, space_no, fields_def, indexes)
11
+ def initialize(tarantool, space_no, fields_def, indexes, shard_fields = nil, shard_proc = nil)
11
12
  @tarantool = tarantool
12
13
  @space_no = space_no
13
14
 
@@ -46,16 +47,17 @@ module Tarantool
46
47
  @index_fields = [*indexes].map{|ind| [*ind].map{|fld| fld.to_sym}.freeze}.freeze
47
48
  @indexes = _map_indexes(@index_fields)
48
49
  @translators = [TranslateToHash.new(@field_names - [:_tail], @tail_size)].freeze
49
- end
50
50
 
51
- def _add_translator(v)
52
- @translators += [v]
51
+ @shard_fields = [*(shard_fields || @index_fields[0])]
52
+ @shard_positions = @shard_fields.map{|name| @field_to_pos[name]}
53
+ _init_shard_vars(shard_proc)
53
54
  end
54
55
 
55
56
  def with_translator(cb = nil, &block)
56
- copy = dup
57
- copy._add_translator(cb || block)
58
- copy
57
+ clone.instance_exec do
58
+ @translators += [cb || block]
59
+ self
60
+ end
59
61
  end
60
62
 
61
63
  def _map_indexes(indexes)
@@ -66,10 +68,6 @@ module Tarantool
66
68
  end.freeze
67
69
  end
68
70
 
69
- def _send_request(type, body, cb)
70
- @tarantool._send_request(type, body, cb)
71
- end
72
-
73
71
  def select_cb(keys, offset, limit, cb)
74
72
  index_names = Hash === keys ? keys.keys : keys.first.keys
75
73
  index_no = @index_fields.index{|fields|
@@ -102,7 +100,9 @@ module Tarantool
102
100
  end
103
101
  end
104
102
 
105
- _select(@space_no, index_no, offset, limit, keys, cb, @field_types, index_types, @translators)
103
+ shard_nums = _get_shard_nums { _detect_shards_for_keys(keys, index_no) }
104
+
105
+ _select(@space_no, index_no, offset, limit, keys, cb, @field_types, index_types, shard_nums, @translators)
106
106
  end
107
107
 
108
108
  def all_cb(keys, cb, opts = {})
@@ -114,15 +114,17 @@ module Tarantool
114
114
  end
115
115
 
116
116
  def all_by_pks_cb(keys, cb, opts={})
117
- keys = [*keys].map{|key| _prepare_pk(key)}
117
+ keys = (Hash === keys ? [keys] : [*keys]).map{|key| _prepare_pk(key)}
118
+ shard_nums = _get_shard_nums{ _detect_shards_for_keys(keys, 0) }
118
119
  _select(@space_no, 0,
119
120
  opts[:offset] || 0, opts[:limit] || -1,
120
- keys, cb, @field_types, @indexes[0], @translators)
121
+ keys, cb, @field_types, @indexes[0], shard_nums, @translators)
121
122
  end
122
123
 
123
- def by_pk_cb(key_array, cb)
124
- key_array = _prepare_pk(key_array)
125
- _select(@space_no, 0, 0, :first, [key_array], cb, @field_types, @indexes[0], @translators)
124
+ def by_pk_cb(pk, cb)
125
+ pk = _prepare_pk(pk)
126
+ shard_nums = _get_shard_nums{ _detect_shards_for_key(pk, 0) }
127
+ _select(@space_no, 0, 0, :first, [pk], cb, @field_types, @indexes[0], shard_nums, @translators)
126
128
  end
127
129
 
128
130
  def _prepare_tuple(tuple)
@@ -142,13 +144,30 @@ module Tarantool
142
144
  end
143
145
 
144
146
  def insert_cb(tuple, cb, opts = {})
145
- _insert(@space_no, BOX_ADD, _prepare_tuple(tuple),
146
- @field_types, cb, opts[:return_tuple], @translators)
147
+ tuple = _prepare_tuple(tuple)
148
+ shard_nums = detect_shard_for_insert(tuple.values_at(*@shard_positions))
149
+ _insert(@space_no, BOX_ADD, tuple, @field_types, cb, opts[:return_tuple],
150
+ shard_nums, nil, @translators)
147
151
  end
148
152
 
149
153
  def replace_cb(tuple, cb, opts = {})
150
- _insert(@space_no, BOX_REPLACE, _prepare_tuple(tuple),
151
- @field_types, cb, opts[:return_tuple], @translators)
154
+ tuple = _prepare_tuple(tuple)
155
+ shard_nums = detect_shard(tuple.values_at(*@shard_positions))
156
+ _insert(@space_no, BOX_REPLACE, tuple, @field_types, cb, opts[:return_tuple],
157
+ shard_nums, opts.fetch(:in_any_shard, true), @translators)
158
+ end
159
+
160
+ def store_cb(tuple, cb, opts = {})
161
+ tuple = _prepare_tuple(tuple)
162
+ shard_nums = _get_shard_nums{
163
+ if opts.fetch(:to_insert_shard, true)
164
+ _detect_shard_for_insert(tuple.values_at(*@shard_positions))
165
+ else
166
+ _detect_shard(tuple.values_at(*@shard_positions))
167
+ end
168
+ }
169
+ _insert(@space_no, 0, tuple, @field_types, cb, opts[:return_tuple],
170
+ shard_nums, nil, @translators)
152
171
  end
153
172
 
154
173
  def _prepare_pk(pk)
@@ -209,13 +228,16 @@ module Tarantool
209
228
  )
210
229
  end
211
230
  }
212
- _update(@space_no, pk, opers, @field_types,
213
- @indexes[0], cb, opts[:return_tuple], @translators)
231
+ shard_nums = _get_shard_nums{ _detect_shards_for_key(pk, 0) }
232
+ _update(@space_no, pk, opers, @field_types, @indexes[0], cb, opts[:return_tuple],
233
+ shard_nums, @translators)
214
234
  end
215
235
 
216
236
  def delete_cb(pk, cb, opts = {})
217
- _delete(@space_no, _prepare_pk(pk), @field_types,
218
- @indexes[0], cb, opts[:return_tuple], @translators)
237
+ pk = _prepare_pk(pk)
238
+ shard_nums = _get_shard_nums{ _detect_shards_for_key(pk, 0) }
239
+ _delete(@space_no, pk, @field_types, @indexes[0], cb, opts[:return_tuple],
240
+ shard_nums, @translators)
219
241
  end
220
242
 
221
243
  def invoke_cb(func_name, values, cb, opts = {})
@@ -252,10 +274,10 @@ module Tarantool
252
274
  end
253
275
 
254
276
  def first_blk(key, &block)
255
- first_blk(key, block)
277
+ first_cb(key, block)
256
278
  end
257
279
 
258
- def select_blk(keys, offset=0, limit=1, &block)
280
+ def select_blk(keys, offset=0, limit=-1, &block)
259
281
  select_cb(keys, offset, limit, block)
260
282
  end
261
283
  end
@@ -1,8 +1,36 @@
1
1
  module Tarantool
2
2
  module Util
3
3
  module Packer
4
+ INT8 = 'C'.freeze
5
+ INT16 = 'v'.freeze
6
+ INT32 = 'V'.freeze
7
+ INT64 = 'Q<'.freeze
8
+ SINT8 = 'c'.freeze
9
+ SINT16 = 's<'.freeze
10
+ SINT32 = 'l<'.freeze
11
+ SINT64 = 'q<'.freeze
12
+ MIN_INT = 0
13
+ MAX_INT64 = 2**64 - 1
14
+ MAX_INT32 = 2**32 - 1
15
+ MAX_INT16 = 2**16 - 1
16
+ MAX_INT8 = 2**8 - 1
17
+ MAX_SINT64 = 2**63 - 1
18
+ MAX_SINT32 = 2**31 - 1
19
+ MAX_SINT16 = 2**15 - 1
20
+ MAX_SINT8 = 2**7 - 1
21
+ MIN_SINT64 = -(2**63)
22
+ MIN_SINT32 = -(2**31)
23
+ MIN_SINT16 = -(2**15)
24
+ MIN_SINT8 = -(2**7)
4
25
  private
5
26
  EMPTY = ''.freeze
27
+ ONE = "\x01".freeze
28
+ def unpack_int8!(data)
29
+ int = data.getbyte(0)
30
+ data[0, 1] = EMPTY
31
+ int
32
+ end
33
+
6
34
  def unpack_int16(data)
7
35
  data.getbyte(0) + data.getbyte(1) * 256
8
36
  end
@@ -10,7 +38,7 @@ module Tarantool
10
38
  def unpack_int16!(data)
11
39
  int = data.getbyte(0) + data.getbyte(1) * 256
12
40
  data[0, 2] = EMPTY
13
- data
41
+ int
14
42
  end
15
43
 
16
44
  def unpack_int32(int)
@@ -26,20 +54,33 @@ module Tarantool
26
54
  end
27
55
 
28
56
  def unpack_int64!(data)
29
- int = (data.getbyte(0) + data.getbyte(1) * 256 +
30
- data.getbyte(2) * 65536 + data.getbyte(3) * 16777216 +
31
- data.getbyte(4) << 32 + data.getbyte(5) << 40 +
32
- data.getbyte(6) << 48 + data.getbyte(7) << 56
33
- )
57
+ int = data.unpack(INT64)[0]
34
58
  data[0, 8] = EMPTY
35
59
  int
36
60
  end
37
61
 
38
62
  def unpack_int64(data)
39
- data.getbyte(0) + data.getbyte(1) * 256 +
40
- data.getbyte(2) * 65536 + data.getbyte(3) * 16777216 +
41
- data.getbyte(4) << 32 + data.getbyte(5) << 40 +
42
- data.getbyte(6) << 48 + data.getbyte(7) << 56
63
+ data.unpack(INT64)[0]
64
+ end
65
+
66
+ def unpack_sint8!(data)
67
+ i = unpack_int8!(data)
68
+ i - ((i & 128) << 1)
69
+ end
70
+
71
+ def unpack_sint16!(data)
72
+ i = unpack_int16!(data)
73
+ i - ((i & 32768) << 1)
74
+ end
75
+
76
+ def unpack_sint32!(data)
77
+ i = unpack_int32!(data)
78
+ i - ((i >> 31) << 32)
79
+ end
80
+
81
+ def unpack_sint64!(data)
82
+ i = unpack_int64!(data)
83
+ i - ((i >> 63) << 64)
43
84
  end
44
85
 
45
86
  def ber_size(int)
@@ -64,6 +105,51 @@ module Tarantool
64
105
  data[0, pos+1] = EMPTY
65
106
  res
66
107
  end
108
+
109
+ def append_int8!(str, int)
110
+ str << (int & 255)
111
+ end
112
+
113
+ def append_int16!(str, int)
114
+ str << (int & 255) << ((int>>8) & 255)
115
+ end
116
+
117
+ def append_int32!(str, int)
118
+ str << (int & 255) << ((int>>8) & 255) <<
119
+ ((int>>16) & 255) << ((int>>24) & 255)
120
+ end
121
+
122
+ def append_int64!(str, int)
123
+ str << [int].pack(INT64)
124
+ end
125
+
126
+ alias append_sint8! append_int8!
127
+ alias append_sint16! append_int16!
128
+ alias append_sint32! append_int32!
129
+ alias append_sint64! append_int64!
130
+
131
+ def append_ber_int8!(str, int)
132
+ str << 1 << (int & 255)
133
+ end
134
+
135
+ def append_ber_int16!(str, int)
136
+ str << 2 << (int & 255) << ((int>>8) & 255)
137
+ end
138
+
139
+ def append_ber_int32!(str, int)
140
+ str << 4 <<
141
+ (int & 255) << ((int>>8) & 255) <<
142
+ ((int>>16) & 255) << ((int>>24) & 255)
143
+ end
144
+
145
+ def append_ber_int64!(str, int)
146
+ str << 8 << [int].pack(INT64)
147
+ end
148
+
149
+ alias append_ber_sint8! append_ber_int8!
150
+ alias append_ber_sint16! append_ber_int16!
151
+ alias append_ber_sint32! append_ber_int32!
152
+ alias append_ber_sint64! append_ber_int64!
67
153
  end
68
154
 
69
155
  module TailGetter
@@ -1,3 +1,4 @@
1
1
  module Tarantool
2
- VERSION = "0.3.0.7"
2
+ VERSION = "0.4.2.1"
3
+ RECORD_VERSION = "0.4.1.1"
3
4
  end
data/test/helper.rb CHANGED
@@ -1,6 +1,6 @@
1
1
  require 'minitest/spec'
2
- require 'minitest/autorun'
3
2
  require 'rr'
3
+ require 'fileutils'
4
4
 
5
5
  require 'tarantool'
6
6
 
@@ -14,6 +14,138 @@ class ArrayPackSerializer
14
14
  end
15
15
  end
16
16
 
17
+ module TConf
18
+ extend FileUtils
19
+ CONF = {
20
+ master1: {port: 33013, replica: :imaster},
21
+ slave1: {port: 34013, replica: :master1},
22
+ master2: {port: 35013, replica: :imaster},
23
+ slave2: {port: 36013, replica: :master2},
24
+ }
25
+ DIR = File.expand_path('..', __FILE__)
26
+ def self.fjoin(*args)
27
+ File.join(*args.map(&:to_s))
28
+ end
29
+ def self.dir(name)
30
+ fjoin(DIR, "tarantool_#{name}")
31
+ end
32
+
33
+ def self._to_master(cfg, conf)
34
+ cfg.sub!(/^replication_source.*$/, '#\0')
35
+ end
36
+
37
+ def self._to_slave(cfg, master)
38
+ replica_source = "127.0.0.1:#{CONF.fetch(master)[:port]+3}"
39
+ cfg.sub!(/^#?replication_source.*$/, "replication_source = \"#{replica_source}\"")
40
+ end
41
+
42
+ def self.prepare(name)
43
+ conf = CONF.fetch(name)
44
+ return if conf[:dir]
45
+ clear(name)
46
+ dir = dir(name)
47
+ FileUtils.rm_rf dir
48
+ mkdir_p(dir)
49
+ cp fjoin(DIR, 'init.lua'), dir
50
+ cfg = File.read(fjoin(DIR, 'tarantool.cfg'))
51
+ cfg.sub!(/(pid_file\s*=\s*)"[^"]*"/, "\\1\"#{fjoin(dir, 'box.pid')}\"")
52
+ cfg.sub!(/(work_dir\s*=\s*)"[^"]*"/, "\\1\"#{dir}\"")
53
+ cfg.sub!(/(primary_port\s*=\s*)\d+/, "\\1#{conf[:port]}")
54
+ cfg.sub!(/(secondary_port\s*=\s*)\d+/, "\\1#{conf[:port]+1}")
55
+ cfg.sub!(/(admin_port\s*=\s*)\d+/, "\\1#{conf[:port]+2}")
56
+ cfg.sub!(/(replication_port\s*=\s*)\d+/, "\\1#{conf[:port]+3}")
57
+ if conf[:replica] == :imaster
58
+ _to_master(cfg, conf)
59
+ else
60
+ _to_slave(cfg, conf[:replica])
61
+ end
62
+ File.open(fjoin(dir, 'tarantool.cfg'), 'w'){|f| f.write(cfg)}
63
+ Dir.chdir(dir) do
64
+ `tarantool_box --init-storage 2>&1`
65
+ end
66
+ conf[:dir] = dir
67
+ end
68
+
69
+ def self.run(name)
70
+ conf = CONF.fetch(name)
71
+ return if conf[:pid]
72
+ prepare(name)
73
+ Dir.chdir(conf[:dir]) do
74
+ conf[:pid] = spawn('tarantool_box')
75
+ end
76
+ end
77
+
78
+ def self.stop(name)
79
+ conf = CONF.fetch(name)
80
+ return unless conf[:pid]
81
+ Process.kill('INT', conf[:pid])
82
+ Process.wait2(conf[:pid])
83
+ conf.delete :pid
84
+ end
85
+
86
+ def self.clear(name)
87
+ conf = CONF.fetch(name)
88
+ return unless conf[:dir]
89
+ stop(name)
90
+ rm_rf(conf[:dir])
91
+ conf.delete :dir
92
+ end
93
+
94
+ def self.clear_all
95
+ CONF.keys.each{|name| clear(name) }
96
+ end
97
+
98
+ def self.reset_and_up_all
99
+ clear_all
100
+ CONF.keys.each{|name| run(name) }
101
+ end
102
+
103
+ def self.reset_and_up_masters
104
+ clear_masters
105
+ run(:master1)
106
+ run(:master2)
107
+ sleep(0.01)
108
+ end
109
+
110
+ def self.clear_masters
111
+ clear(:master1)
112
+ clear(:master2)
113
+ end
114
+
115
+ def self.conf(name)
116
+ {host: '127.0.0.1', port: CONF.fetch(name)[:port]}
117
+ end
118
+
119
+ def self.promote_to_master(name)
120
+ conf = CONF.fetch(name)
121
+ raise "#{name} not running" unless conf[:pid]
122
+ fcfg = fjoin(dir(name), 'tarantool.cfg')
123
+ cfg = File.read(fcfg)
124
+ _to_master(cfg, conf)
125
+ File.open(fcfg, 'w'){|f| f.write(cfg)}
126
+ sock = TCPSocket.new('127.0.0.1', conf[:port]+2)
127
+ sock.write("reload configuration\n")
128
+ 3.times{ sock.gets }
129
+ end
130
+
131
+ def self.promote_to_slave(slave, master)
132
+ clear(slave)
133
+ prepare(slave)
134
+ fcfg = fjoin(dir(slave), 'tarantool.cfg')
135
+ cfg = File.read(fcfg)
136
+ _to_slave(cfg, master)
137
+ File.open(fcfg, 'w'){|f| f.write(cfg)}
138
+ run(slave)
139
+ end
140
+
141
+ at_exit do
142
+ CONF.keys.each{|name| clear(name)}
143
+ end
144
+ end
145
+ require 'minitest/autorun'
146
+ TConf.run(:master1)
147
+
148
+
17
149
  TCONFIG = { host: '127.0.0.1', port: 33013, admin: 33015 }
18
150
 
19
151
  SPACE0 = {
@@ -53,7 +185,7 @@ HSPACE3 = {
53
185
  module Helper
54
186
  def tarantool_pipe
55
187
  $tarantool_pipe ||= begin
56
- cnf = TCONFIG
188
+ cnf = {port: 33013, admin: 33015} #TCONFIG
57
189
  tarant = %W{tarantool -p #{cnf[:port]} -m #{cnf[:admin]}}
58
190
  tarant = [{}, *tarant, :err => [:child, :out]]
59
191
  IO.popen(tarant, 'w+').tap{|p| p.sync = true}
@@ -115,17 +247,35 @@ module Helper
115
247
  end
116
248
  end
117
249
 
250
+ def setp(i)
251
+ @results ||= []
252
+ proc{|v| @results[i]=v; emstop}
253
+ end
254
+
255
+ def results
256
+ @results
257
+ end
258
+
259
+ class TimeOut < Exception
260
+ end
118
261
  def fibrun
119
262
  res = nil
120
263
  EM.run {
264
+ t = nil
121
265
  f = Fiber.new{
122
266
  begin
123
267
  res = yield
124
268
  ensure
125
- EM.next_tick{ EM.stop }
269
+ EM.next_tick{
270
+ EM.cancel_timer t
271
+ EM.stop
272
+ }
126
273
  end
127
274
  }
128
275
  EM.next_tick{ f.resume }
276
+ t = EM.add_timer(1) {
277
+ f.resume TimeOut.new
278
+ }
129
279
  }
130
280
  res
131
281
  end
@@ -156,7 +306,7 @@ module MiniTest::Spec::SharedExamples
156
306
  end
157
307
 
158
308
  def it_behaves_like(desc)
159
- class_eval &MiniTest::Spec.shared_examples.fetch(desc)
309
+ class_eval(&MiniTest::Spec.shared_examples.fetch(desc))
160
310
  end
161
311
  end
162
312