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
@@ -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