mogilefs-client 2.0.2 → 2.1.0

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/GNUmakefile CHANGED
@@ -10,12 +10,26 @@ test: $(T)
10
10
  clean:
11
11
  $(RM) $(TO) $(addsuffix +,$(TO))
12
12
 
13
- t = $(basename $(notdir $@))
14
- t_log = $(subst .rb,.log,$@)
15
13
 
14
+ ifndef V
15
+ quiet_pre = @echo '* $@';
16
+ quiet_post = >$(t) 2>&1
17
+ else
18
+ # we can't rely on -o pipefail outside of bash 3+,
19
+ # so we use a stamp file to indicate success and
20
+ # have rm fail if the stamp didn't get created
21
+ stamp = $@$(log_suffix).ok
22
+ quiet_pre = @echo $(ruby) $@ $(TEST_OPTS); ! test -f $(stamp) && (
23
+ quiet_post = && > $(stamp) )>&2 | tee $(t); rm $(stamp) 2>/dev/null
24
+ endif
25
+ ruby = ruby
26
+ run_test = $(quiet_pre) setsid $(ruby) -w $@ $(TEST_OPTS) $(quiet_post) || \
27
+ (sed "s,^,$(extra): ," >&2 < $(t); exit 1)
28
+
29
+ $(T): t = $(subst .rb,.log,$@)
30
+ $(T): export RUBYLIB := $(CURDIR)/lib:$(RUBYLIB)
16
31
  $(T):
17
- @echo $(t); ruby -I lib $@ $(TEST_OPTS) > $(t_log)+ 2>&1
18
- @mv $(t_log)+ $(t_log)
32
+ $(run_test)
19
33
 
20
34
  # using make instead of rake since Rakefile takes too long to load
21
35
  manifest: Manifest.txt
data/History.txt CHANGED
@@ -1,3 +1,12 @@
1
+ = 2.1.0
2
+ * MySQL interface returns integer length and devcount (API change)
3
+ * Ensure store_{content,file} always returns size (API fix)
4
+ * Add get_uris API method
5
+ * Respect timeout when doing get_file_data
6
+ * MySQL interface filters out URLs for down/dead hosts/devices
7
+ * Really remove all NFS support
8
+ * get_file in slurp mode slurps all output correctly
9
+
1
10
  = 2.0.2
2
11
  * README.txt: update documentation URL
3
12
  * backend: raise exceptions with the error message
data/README.txt CHANGED
@@ -14,15 +14,20 @@ File bugs:
14
14
 
15
15
  http://rubyforge.org/tracker/?func=add&group_id=1513&atid=5921
16
16
 
17
- Source repository (git):
17
+ Please email Eric Wong at normalperson@yhbt.net as well since
18
+ he finds web interfaces difficult to use.
18
19
 
19
- git://git.bogomips.org/mogilefs-client.git
20
+ Source repository (git):
20
21
 
21
- http://git.bogomips.org/mogilefs-client.git
22
+ git://git.bogomips.org/mogilefs-client.git
23
+ http://git.bogomips.org/mogilefs-client.git
24
+ git://repo.or.cz/ruby-mogilefs-client.git (mirror)
25
+ http://repo.or.cz/r/ruby-mogilefs-client.git (mirror)
22
26
 
23
- Repository browser (cgit):
27
+ Repository browsers:
24
28
 
25
- http://git.bogomips.org/cgit/mogilefs-client.git
29
+ * http://git.bogomips.org/cgit/mogilefs-client.git (cgit)
30
+ * http://repo.or.cz/w/ruby-mogilefs-client.git (gitweb mirror)
26
31
 
27
32
  == About
28
33
 
data/bin/mog CHANGED
@@ -186,7 +186,6 @@ begin
186
186
  cfg[:class] or raise ArgumentError, 'E: --class must be specified'
187
187
  buf = ''
188
188
  tmp = Tempfile.new('mog-tee') # TODO: explore Transfer-Encoding:chunked :)
189
- at_exit { tmp.unlink }
190
189
 
191
190
  # if stdout is pointing to /dev/null, don't bother installing the filter.
192
191
  STDOUT.sync = true
@@ -196,7 +195,7 @@ begin
196
195
  sysrwloop(STDIN, tmp, tee_filter)
197
196
  store_file_retry(mg, key, cfg[:class], tmp.path)
198
197
  ensure
199
- tmp.close
198
+ tmp.close!
200
199
  end
201
200
  when 'test'
202
201
  truth, ok = true, nil
@@ -56,7 +56,7 @@ module MogileFS::Bigfile
56
56
  raise MogileFS::Backend::NoDevices if uris.empty?
57
57
  end
58
58
 
59
- sock = http_get_sock(uris[0])
59
+ sock = http_read_sock(uris[0])
60
60
  md5.reset if md5
61
61
  w = sysrwloop(sock, wr_io, filter)
62
62
 
@@ -19,10 +19,6 @@ class MogileFS::MogileFS < MogileFS::Client
19
19
 
20
20
  attr_accessor :get_file_data_timeout
21
21
 
22
- ##
23
- # internal Regexp for matching an "HTTP 200 OK" head response
24
- HTTP_200_OK = %r{\AHTTP/\d+\.\d+\s+200\s+}.freeze
25
-
26
22
  ##
27
23
  # Creates a new MogileFS::MogileFS instance. +args+ must include a key
28
24
  # :domain specifying the domain of this client.
@@ -61,27 +57,20 @@ class MogileFS::MogileFS < MogileFS::Client
61
57
  # Retrieves the contents of +key+.
62
58
 
63
59
  def get_file_data(key, &block)
64
- paths = get_paths key
65
-
66
- return nil unless paths
67
-
60
+ paths = get_paths(key) or return nil
68
61
  paths.each do |path|
69
- next unless path
70
- case path
71
- when /^http:\/\// then
62
+ begin
63
+ sock = http_read_sock(URI.parse(path))
72
64
  begin
73
- sock = http_get_sock(URI.parse(path))
74
- return block_given? ? yield(sock) : sock.read
75
- rescue MogileFS::Timeout, Errno::ECONNREFUSED,
76
- EOFError, SystemCallError, MogileFS::InvalidResponseError
77
- next
65
+ return yield(sock) if block_given?
66
+ return sysread_full(sock, sock.mogilefs_size, @get_file_data_timeout)
67
+ ensure
68
+ sock.close rescue nil
78
69
  end
79
- else
80
- next unless File.exist? path
81
- return File.read(path)
70
+ rescue MogileFS::Timeout, MogileFS::InvalidResponseError,
71
+ Errno::ECONNREFUSED, EOFError, SystemCallError
82
72
  end
83
73
  end
84
-
85
74
  nil
86
75
  end
87
76
 
@@ -93,7 +82,14 @@ class MogileFS::MogileFS < MogileFS::Client
93
82
  :noverify => noverify ? 1 : 0, :zone => zone }
94
83
  @backend.respond_to?(:_get_paths) and return @backend._get_paths(opts)
95
84
  res = @backend.get_paths(opts)
96
- (1..res['paths'].to_i).map { |i| res["path#{i}"] }
85
+ (1..res['paths'].to_i).map { |i| res["path#{i}"] }.compact
86
+ end
87
+
88
+ ##
89
+ # Get the URIs for +key+.
90
+
91
+ def get_uris(key, noverify = true, zone = nil)
92
+ get_paths(key, noverify, zone).map { |path| URI.parse(path) }
97
93
  end
98
94
 
99
95
  ##
@@ -135,20 +131,22 @@ class MogileFS::MogileFS < MogileFS::Client
135
131
 
136
132
  ##
137
133
  # Copies the contents of +file+ into +key+ in class +klass+. +file+ can be
138
- # either a file name or an object that responds to #read.
134
+ # either a file name or an object that responds to #sysread.
135
+ # Returns size of +file+ stored
139
136
 
140
137
  def store_file(key, klass, file)
141
138
  raise MogileFS::ReadOnlyError if readonly?
142
139
 
143
140
  new_file key, klass do |mfp|
144
141
  if file.respond_to? :sysread then
145
- return sysrwloop(file, mfp)
142
+ sysrwloop(file, mfp)
146
143
  else
147
- if File.size(file) > 0x10000 # Bigass file, handle differently
148
- mfp.big_io = file
149
- return
150
- else
151
- return File.open(file, "rb") { |fp| sysrwloop(fp, mfp) }
144
+ size = File.size(file)
145
+ if size > 0x10000 # Bigass file, handle differently
146
+ mfp.big_io = file
147
+ size
148
+ else
149
+ File.open(file, "rb") { |fp| sysrwloop(fp, mfp) }
152
150
  end
153
151
  end
154
152
  end
@@ -207,34 +205,13 @@ class MogileFS::MogileFS < MogileFS::Client
207
205
 
208
206
  def paths_size(paths)
209
207
  paths.each do |path|
210
- next unless path
211
- case path
212
- when /^http:\/\// then
213
- begin
214
- url = URI.parse path
215
- s = Socket.mogilefs_new_request(url.host, url.port,
216
- "HEAD #{url.request_uri} HTTP/1.0\r\n\r\n",
217
- @get_file_data_timeout)
218
- res = s.recv(4096, 0)
219
- if res =~ HTTP_200_OK
220
- head, body = res.split(/\r\n\r\n/, 2)
221
- if head =~ /^Content-Length:\s*(\d+)/i
222
- return $1.to_i
223
- end
224
- end
225
- next
226
- rescue MogileFS::Timeout, Errno::ECONNREFUSED,
227
- EOFError, SystemCallError
228
- next
229
- ensure
230
- s.close rescue nil
231
- end
232
- else
233
- next unless File.exist? path
234
- return File.size(path)
208
+ begin
209
+ return http_read_sock(URI.parse(path), "HEAD").mogilefs_size
210
+ rescue MogileFS::InvalidResponseError, MogileFS::Timeout,
211
+ Errno::ECONNREFUSED, EOFError, SystemCallError => err
212
+ next
235
213
  end
236
214
  end
237
-
238
215
  nil
239
216
  end
240
217
 
@@ -271,19 +248,27 @@ class MogileFS::MogileFS < MogileFS::Client
271
248
 
272
249
  # given a URI, this returns a readable socket with ready data from the
273
250
  # body of the response.
274
- def http_get_sock(uri)
251
+ def http_read_sock(uri, http_method = "GET")
275
252
  sock = Socket.mogilefs_new_request(uri.host, uri.port,
276
- "GET #{uri.request_uri} HTTP/1.0\r\n\r\n",
277
- @get_file_data_timeout)
278
- buf = sock.recv(4096, Socket::MSG_PEEK)
253
+ "#{http_method} #{uri.request_uri} HTTP/1.0\r\n\r\n",
254
+ @get_file_data_timeout)
255
+ buf = sock.recv_nonblock(4096, Socket::MSG_PEEK)
279
256
  head, body = buf.split(/\r\n\r\n/, 2)
280
- if head =~ HTTP_200_OK
281
- sock.recv(head.size + 4, 0)
257
+
258
+ # we're dealing with a seriously slow/stupid HTTP server if we can't
259
+ # get the header in a single read(2) syscall.
260
+ if head =~ %r{\AHTTP/\d+\.\d+\s+200\s*} &&
261
+ head =~ %r{^Content-Length:\s*(\d+)}i
262
+ sock.mogilefs_size = $1.to_i
263
+ case http_method
264
+ when "HEAD" then sock.close
265
+ when "GET" then sock.recv(head.size + 4, 0)
266
+ end
282
267
  return sock
283
268
  end
269
+ sock.close rescue nil
284
270
  raise MogileFS::InvalidResponseError,
285
- "GET on #{uri} returned: #{head.inspect}"
286
- end # def http_get_sock
271
+ "#{http_method} on #{uri} returned: #{head.inspect}"
272
+ end # def http_read_sock
287
273
 
288
274
  end
289
-
@@ -55,7 +55,7 @@ class MogileFS::Mysql
55
55
 
56
56
  keys = []
57
57
  query(sql).each do |dkey,length,devcount|
58
- yield(dkey, length, devcount) if block_given?
58
+ yield(dkey, length.to_i, devcount.to_i) if block_given?
59
59
  keys << dkey
60
60
  end
61
61
 
@@ -101,7 +101,7 @@ class MogileFS::Mysql
101
101
  devices = refresh_device(true)
102
102
  devinfo = devices[devid.to_i] or next
103
103
  end
104
-
104
+ devinfo[:readable] or next
105
105
  port = devinfo[:http_get_port]
106
106
  host = zone && zone == 'alt' ? devinfo[:altip] : devinfo[:hostip]
107
107
  nfid = '%010u' % fid
@@ -120,10 +120,10 @@ class MogileFS::Mysql
120
120
  GET_DOMAINS = 'SELECT dmid,namespace FROM domain'.freeze
121
121
 
122
122
  GET_DEVICES = <<-EOS
123
- SELECT d.devid, h.hostip, h.altip, h.http_port, h.http_get_port
123
+ SELECT d.devid, h.hostip, h.altip, h.http_port, h.http_get_port,
124
+ d.status, h.status
124
125
  FROM device d
125
126
  LEFT JOIN host h ON d.hostid = h.hostid
126
- WHERE d.status IN ('alive','readonly','drain');
127
127
  EOS
128
128
  GET_DEVICES.freeze
129
129
  end
@@ -132,15 +132,24 @@ class MogileFS::Mysql
132
132
  @my.send(@query_method, sql)
133
133
  end
134
134
 
135
+ DEV_STATUS_READABLE = {
136
+ "alive" => true,
137
+ "readonly" => true,
138
+ "drain" => true,
139
+ }.freeze
140
+
135
141
  def refresh_device(force = false)
136
142
  return @cache_device if ! force && ((Time.now - @last_update_device) < 60)
137
143
  tmp = {}
138
144
  res = query(GET_DEVICES)
139
- res.each do |devid, hostip, altip, http_port, http_get_port|
145
+ res.each do |devid, hostip, altip, http_port, http_get_port,
146
+ dev_status, host_status|
140
147
  http_port = http_port ? http_port.to_i : 80
141
148
  tmp[devid.to_i] = {
142
149
  :hostip => hostip.freeze,
143
150
  :altip => (altip || hostip).freeze,
151
+ :readable => (host_status == "alive" &&
152
+ DEV_STATUS_READABLE.include?(dev_status)),
144
153
  :http_port => http_port,
145
154
  :http_get_port => http_get_port ? http_get_port.to_i : http_port,
146
155
  }.freeze
@@ -57,9 +57,11 @@ module MogileFS::Network
57
57
 
58
58
  r[1].each do |sock|
59
59
  begin
60
- # we don't about short/interrupted writes here, if the following
61
- # request fails or blocks then the server is flat-out hopeless
62
- sock.syswrite "HEAD #{uri_socks[sock].request_uri} HTTP/1.0\r\n\r\n"
60
+ # we don't care about short/interrupted writes here, if the
61
+ # following request fails or blocks then the server is
62
+ # flat-out hopeless
63
+ sock.write_nonblock(
64
+ "HEAD #{uri_socks[sock].request_uri} HTTP/1.0\r\n\r\n")
63
65
  sockets << sock
64
66
  rescue
65
67
  sock.close rescue nil
data/lib/mogilefs/util.rb CHANGED
@@ -67,6 +67,29 @@ module MogileFS::Util
67
67
  # should never get here
68
68
  end
69
69
 
70
+ def sysread_full(io_rd, size, timeout = nil, full_timeout = false)
71
+ tmp = [] # avoid expensive string concatenation with every loop iteration
72
+ reader = io_rd.method(timeout ? :read_nonblock : :sysread)
73
+ begin
74
+ while size > 0
75
+ tmp << reader.call(size)
76
+ size -= tmp.last.size
77
+ end
78
+ rescue Errno::EAGAIN, Errno::EINTR
79
+ t0 = Time.now
80
+ r = IO.select([ io_rd ], nil, nil, timeout)
81
+ if timeout
82
+ timeout -= (Time.now - t0) if full_timeout
83
+ if !(r && r[0]) || timeout < 0
84
+ raise MogileFS::Timeout, 'sysread_full timeout'
85
+ end
86
+ end
87
+ retry
88
+ rescue EOFError
89
+ end
90
+ tmp.join('')
91
+ end
92
+
70
93
  class StoreContent < Proc
71
94
  def initialize(total_size, &writer_proc)
72
95
  @total_size = total_size
@@ -87,7 +110,7 @@ require 'timeout'
87
110
  class MogileFS::Timeout < Timeout::Error; end
88
111
 
89
112
  class Socket
90
- attr_accessor :mogilefs_addr, :mogilefs_connected
113
+ attr_accessor :mogilefs_addr, :mogilefs_connected, :mogilefs_size
91
114
 
92
115
  TCP_CORK = 3 if ! defined?(TCP_CORK) && RUBY_PLATFORM =~ /linux/
93
116
 
@@ -159,9 +182,14 @@ class Socket
159
182
  sock = mogilefs_new(host, port, timeout)
160
183
  syswrite_full(sock, request, timeout)
161
184
  timeout -= (Time.now - t0)
162
- raise MogileFS::Timeout, 'socket read timeout' if timeout < 0
185
+ if timeout < 0
186
+ sock.close rescue nil
187
+ raise MogileFS::Timeout, 'socket read timeout'
188
+ end
163
189
  r = IO.select([sock], nil, nil, timeout)
164
190
  return sock if r && r[0]
191
+
192
+ sock.close rescue nil
165
193
  raise MogileFS::Timeout, 'socket read timeout'
166
194
  end
167
195
 
data/lib/mogilefs.rb CHANGED
@@ -6,7 +6,7 @@
6
6
 
7
7
  module MogileFS
8
8
 
9
- VERSION = '2.0.2'.freeze
9
+ VERSION = '2.1.0'.freeze
10
10
 
11
11
  ##
12
12
  # Raised when a socket remains unreadable for too long.
data/test/setup.rb CHANGED
@@ -65,15 +65,18 @@ class TempServer
65
65
  def initialize(server_proc, port = nil)
66
66
  @pid = @sock = nil
67
67
  @port = port
68
- retries = 0
68
+ retries = 10
69
69
  begin
70
70
  @port ||= 1024 + rand(32768 - 1024)
71
71
  @sock = Socket.new(Socket::AF_INET, Socket::SOCK_STREAM, 0)
72
72
  @sock.bind(Socket.pack_sockaddr_in(@port.to_i, '127.0.0.1'))
73
73
  @sock.listen(5)
74
- rescue Errno::EADDRINUSE, Errno::EACCES
74
+ rescue Errno::EADDRINUSE, Errno::EACCES => err
75
75
  @sock.close rescue nil
76
- retry if (retries += 1) < 10
76
+ @port = nil
77
+ retry if port.nil? && (retries -= 1) > 0
78
+ warn "retries failed: #{retries} port=#{port.inspect}"
79
+ raise err
77
80
  end
78
81
  @pid = fork { server_proc.call(@sock, @port) }
79
82
  @sock.close rescue nil
@@ -106,11 +109,13 @@ end
106
109
  class FakeMysql
107
110
  attr_reader :expect
108
111
  TBL_DEVICES = [
109
- # devid, hostip, altip, http_port, http_get_port
110
- [ 1, '10.0.0.1', '192.168.0.1', 7500, 7600 ],
111
- [ 2, '10.0.0.2', '192.168.0.2', 7500, 7600 ],
112
- [ 3, '10.0.0.3', nil, 7500, nil ],
113
- [ 4, '10.0.0.4', nil, 7500, nil ],
112
+ # devid, hostip, altip, http_port, http_get_port, dev status, host status
113
+ [ 1, '10.0.0.1', '192.168.0.1', 7500, 7600, 'readonly', 'alive' ],
114
+ [ 2, '10.0.0.2', '192.168.0.2', 7500, 7600, 'alive', 'alive' ],
115
+ [ 3, '10.0.0.3', nil, 7500, nil, 'readonly', 'alive' ],
116
+ [ 4, '10.0.0.4', nil, 7500, nil, 'alive', 'alive' ],
117
+ [ 5, '10.0.0.5', nil, 7500, nil, 'dead', 'alive' ],
118
+ [ 6, '10.0.0.6', nil, 7500, nil, 'alive', 'down' ],
114
119
  ]
115
120
  TBL_DOMAINS = [
116
121
  # dmid, namespace
@@ -54,8 +54,8 @@ class TestMogileFS__MogileFS < TestMogileFS
54
54
  readed = client.recv(4096, 0)
55
55
  assert(readed =~ \
56
56
  %r{\AGET /dev1/0/000/000/0000000062\.fid HTTP/1.[01]\r\n\r\n\Z})
57
- client.send("HTTP/1.0 404 Not Found\r\n\r\ndata!", 0)
58
57
  accept.syswrite('.')
58
+ client.send("HTTP/1.0 404 Not Found\r\n\r\ndata!", 0)
59
59
  client.close
60
60
  end
61
61
 
@@ -65,8 +65,8 @@ class TestMogileFS__MogileFS < TestMogileFS
65
65
  readed = client.recv(4096, 0)
66
66
  assert(readed =~ \
67
67
  %r{\AGET /dev2/0/000/000/0000000062\.fid HTTP/1.[01]\r\n\r\n\Z})
68
- client.send("HTTP/1.0 200 OK\r\nContent-Length: 5\r\n\r\ndata!", 0)
69
68
  accept.syswrite('.')
69
+ client.send("HTTP/1.0 200 OK\r\nContent-Length: 5\r\n\r\ndata!", 0)
70
70
  client.close
71
71
  end
72
72
 
@@ -145,6 +145,18 @@ class TestMogileFS__MogileFS < TestMogileFS
145
145
  assert_equal expected, @client.get_paths('key').sort
146
146
  end
147
147
 
148
+ def test_get_uris
149
+ path1 = 'http://rur-1/dev1/0/000/000/0000000062.fid'
150
+ path2 = 'http://rur-2/dev2/0/000/000/0000000062.fid'
151
+
152
+ @backend.get_paths = { 'paths' => 2, 'path1' => path1, 'path2' => path2 }
153
+
154
+ expected = [ URI.parse(path1), URI.parse(path2) ]
155
+
156
+ assert_equal expected, @client.get_uris('key')
157
+ end
158
+
159
+
148
160
  def test_get_paths_unknown_key
149
161
  @backend.get_paths = ['unknown_key', '']
150
162
 
@@ -189,7 +201,7 @@ class TestMogileFS__MogileFS < TestMogileFS
189
201
  end
190
202
 
191
203
  def test_list_keys
192
- @backend.list_keys = { 'key_count' => 2, 'next_after' => 'new_key_2',
204
+ @backend.list_keys = { 'key_count' => '2', 'next_after' => 'new_key_2',
193
205
  'key_1' => 'new_key_1', 'key_2' => 'new_key_2' }
194
206
 
195
207
  keys, next_after = @client.list_keys 'new'
@@ -198,7 +210,7 @@ class TestMogileFS__MogileFS < TestMogileFS
198
210
  end
199
211
 
200
212
  def test_list_keys_block
201
- @backend.list_keys = { 'key_count' => 2, 'next_after' => 'new_key_2',
213
+ @backend.list_keys = { 'key_count' => '2', 'next_after' => 'new_key_2',
202
214
  'key_1' => 'new_key_1', 'key_2' => 'new_key_2' }
203
215
  http_resp = "HTTP/1.0 200 OK\r\nContent-Length: %u\r\n"
204
216
  srv = Proc.new do |serv, port, size|
@@ -212,10 +224,10 @@ class TestMogileFS__MogileFS < TestMogileFS
212
224
  t1 = TempServer.new(Proc.new { |serv, port| srv.call(serv, port, 5) })
213
225
  t2 = TempServer.new(Proc.new { |serv, port| srv.call(serv, port, 5) })
214
226
  t3 = TempServer.new(Proc.new { |serv, port| srv.call(serv, port, 10) })
215
- @backend.get_paths = { 'paths' => 2,
227
+ @backend.get_paths = { 'paths' => '2',
216
228
  'path1' => "http://127.0.0.1:#{t1.port}/",
217
229
  'path2' => "http://127.0.0.1:#{t2.port}/" }
218
- @backend.get_paths = { 'paths' => 1,
230
+ @backend.get_paths = { 'paths' => '1',
219
231
  'path1' => "http://127.0.0.1:#{t3.port}/" }
220
232
 
221
233
  res = []
@@ -271,8 +283,8 @@ class TestMogileFS__MogileFS < TestMogileFS
271
283
  client.sync = true
272
284
  readed = client.recv(4096, 0) rescue nil
273
285
  assert_equal "HEAD /path HTTP/1.0\r\n\r\n", readed
274
- client.send("HTTP/1.0 404 Not Found\r\nContent-Length: 5\r\n\r\n", 0)
275
286
  tmp.syswrite('.')
287
+ client.send("HTTP/1.0 404 Not Found\r\nContent-Length: 5\r\n\r\n", 0)
276
288
  client.close
277
289
  end)
278
290
 
@@ -283,6 +295,32 @@ class TestMogileFS__MogileFS < TestMogileFS
283
295
  assert_equal 1, tmp.stat.size
284
296
  end
285
297
 
298
+ def test_store_file_small_http
299
+ received = Tempfile.new('received')
300
+ to_store = Tempfile.new('small')
301
+ to_store.syswrite('data')
302
+
303
+ expected = "PUT /path HTTP/1.0\r\nContent-Length: 4\r\n\r\ndata"
304
+ t = TempServer.new(Proc.new do |serv, accept|
305
+ client, client_addr = serv.accept
306
+ client.sync = true
307
+ received.syswrite(client.recv(4096, 0))
308
+ client.send("HTTP/1.0 200 OK\r\n\r\n", 0)
309
+ client.close
310
+ end)
311
+
312
+ @backend.create_open = {
313
+ 'devid' => '1',
314
+ 'path' => "http://127.0.0.1:#{t.port}/path",
315
+ }
316
+ nr = @client.store_file 'new_key', 'test', to_store.path
317
+ assert_equal 4, nr
318
+ received.sysseek(0)
319
+ assert_equal expected, received.sysread(4096)
320
+ ensure
321
+ TempServer.destroy_all!
322
+ end
323
+
286
324
  def test_store_content_http
287
325
  received = Tempfile.new('recieved')
288
326
  expected = "PUT /path HTTP/1.0\r\nContent-Length: 4\r\n\r\ndata"
@@ -300,7 +338,9 @@ class TestMogileFS__MogileFS < TestMogileFS
300
338
  'path' => "http://127.0.0.1:#{t.port}/path",
301
339
  }
302
340
 
303
- @client.store_content 'new_key', 'test', 'data'
341
+ nr = @client.store_content 'new_key', 'test', 'data'
342
+ assert nr
343
+ assert_equal 4, nr
304
344
 
305
345
  received.sysseek(0)
306
346
  assert_equal expected, received.sysread(4096)
@@ -340,7 +380,9 @@ class TestMogileFS__MogileFS < TestMogileFS
340
380
  write_callback.call("data")
341
381
  end
342
382
  end
343
- @client.store_content('new_key', 'test', cbk)
383
+ assert_equal 40, cbk.length
384
+ nr = @client.store_content('new_key', 'test', cbk)
385
+ assert_equal 40, nr
344
386
 
345
387
  received.sysseek(0)
346
388
  assert_equal expected, received.sysread(4096)
@@ -377,7 +419,8 @@ class TestMogileFS__MogileFS < TestMogileFS
377
419
  'path_2' => "http://127.0.0.1:#{t2.port}/path",
378
420
  }
379
421
 
380
- @client.store_content 'new_key', 'test', 'data'
422
+ nr = @client.store_content 'new_key', 'test', 'data'
423
+ assert_equal 4, nr
381
424
  received1.sysseek(0)
382
425
  received2.sysseek(0)
383
426
  assert_equal expected, received1.sysread(4096)
@@ -421,7 +464,8 @@ class TestMogileFS__MogileFS < TestMogileFS
421
464
  'path' => "http://127.0.0.1:#{t.port}/path",
422
465
  }
423
466
 
424
- @client.store_content 'new_key', 'test', ''
467
+ nr = @client.store_content 'new_key', 'test', ''
468
+ assert_equal 0, nr
425
469
  received.sysseek(0)
426
470
  assert_equal expected, received.sysread(4096)
427
471
  end
@@ -478,7 +522,11 @@ class TestMogileFS__MogileFS < TestMogileFS
478
522
  'path' => "http://127.0.0.1:#{t.port}/path",
479
523
  }
480
524
 
481
- @client.store_file('new_key', 'test', to_put.path)
525
+ orig_size = to_put.size
526
+ nr = @client.store_file('new_key', 'test', to_put.path)
527
+ assert nr
528
+ assert_equal orig_size, nr
529
+ assert_equal orig_size, to_put.size
482
530
  readed.sysseek(0)
483
531
  assert_equal expect.stat.size, readed.sysread(4096).to_i
484
532
 
data/test/test_mysql.rb CHANGED
@@ -20,22 +20,38 @@ class TestMogileFS__Mysql < Test::Unit::TestCase
20
20
  {:hostip=>"10.0.0.1",
21
21
  :http_get_port=>7600,
22
22
  :http_port=>7500,
23
+ :readable=>true,
23
24
  :altip=>"192.168.0.1"},
24
25
  2=>
25
26
  {:hostip=>"10.0.0.2",
26
27
  :http_get_port=>7600,
27
28
  :http_port=>7500,
29
+ :readable=>true,
28
30
  :altip=>"192.168.0.2"},
29
31
  3=>
30
32
  {:hostip=>"10.0.0.3",
31
33
  :http_get_port=>7500,
32
34
  :http_port=>7500,
35
+ :readable=>true,
33
36
  :altip=>"10.0.0.3"},
34
37
  4=>
35
38
  {:hostip=>"10.0.0.4",
36
39
  :http_get_port=>7500,
37
40
  :http_port=>7500,
38
- :altip=>"10.0.0.4"}
41
+ :readable=>true,
42
+ :altip=>"10.0.0.4"},
43
+ 5=>
44
+ {:hostip=>"10.0.0.5",
45
+ :http_get_port=>7500,
46
+ :http_port=>7500,
47
+ :readable=>false,
48
+ :altip=>"10.0.0.5"},
49
+ 6=>
50
+ {:hostip=>"10.0.0.6",
51
+ :http_get_port=>7500,
52
+ :http_port=>7500,
53
+ :readable=>false,
54
+ :altip=>"10.0.0.6"}
39
55
  }
40
56
  assert_equal expect, @mg.refresh_device
41
57
  end
@@ -53,6 +69,20 @@ class TestMogileFS__Mysql < Test::Unit::TestCase
53
69
  assert_equal expect, @mg._get_paths(:domain => 'test', :key => 'fookey')
54
70
  end
55
71
 
72
+ def test_get_paths_bad_device
73
+ @my.expect << [ [ 12 ] ] # fid
74
+ @my.expect << [ [ 1 ], [ 6 ] ] # devids
75
+ expect = [ "http://10.0.0.1:7600/dev1/0/000/000/0000000012.fid" ]
76
+ assert_equal expect, @mg._get_paths(:domain => 'test', :key => 'fookey')
77
+ end
78
+
79
+ def test_get_paths_bad_host
80
+ @my.expect << [ [ 12 ] ] # fid
81
+ @my.expect << [ [ 1 ], [ 5 ] ] # devids
82
+ expect = [ "http://10.0.0.1:7600/dev1/0/000/000/0000000012.fid" ]
83
+ assert_equal expect, @mg._get_paths(:domain => 'test', :key => 'fookey')
84
+ end
85
+
56
86
  def test_get_paths_alt
57
87
  @my.expect << [ [ 12 ] ] # fid
58
88
  @my.expect << [ [ 1 ], [ 3 ] ] # devids
@@ -64,8 +94,10 @@ class TestMogileFS__Mysql < Test::Unit::TestCase
64
94
 
65
95
  def test_list_keys
66
96
  expect_full = [ [ 'foo', 123, 2 ], [ 'bar', 456, 1 ] ]
97
+ result_full = eval(expect_full.inspect)
98
+ result_full.each { |x| (1..2).each { |i| x[i] = x[i].to_s } }
67
99
  expect_keys = [ [ 'foo', 'bar' ], 'bar' ]
68
- @my.expect << expect_full
100
+ @my.expect << result_full
69
101
  full = []
70
102
  keys = @mg._list_keys('test') do |dkey,length,devcount|
71
103
  full << [ dkey, length, devcount ]
data/test/test_util.rb CHANGED
@@ -56,4 +56,65 @@ class TestMogileFS__Util < Test::Unit::TestCase
56
56
  t.destroy!
57
57
  end
58
58
 
59
+ def test_sysread_slowly
60
+ nr = 10
61
+ str = 'abcde'
62
+ expect = str * nr
63
+ rd, wr = IO.pipe
64
+ pid = fork do
65
+ rd.close
66
+ nr.times do
67
+ syswrite_full(wr, str)
68
+ sleep(0.1)
69
+ end
70
+ end
71
+ wr.close
72
+ buf = sysread_full(rd, expect.size)
73
+ assert_equal expect, buf
74
+ rd.close
75
+ ensure
76
+ Process.kill('TERM', pid) rescue nil
77
+ Process.waitpid(pid) rescue nil
78
+ end
79
+
80
+ def test_sysread_timeout
81
+ nr = 10
82
+ str = 'abcde'
83
+ expect = str * nr
84
+ rd, wr = IO.pipe
85
+ pid = fork do
86
+ rd.close
87
+ nr.times do
88
+ syswrite_full(wr, str)
89
+ sleep 1
90
+ end
91
+ end
92
+ wr.close
93
+ assert_raises(MogileFS::Timeout) { sysread_full(rd, expect.size, 0.1) }
94
+ rd.close
95
+ ensure
96
+ Process.kill('TERM', pid) rescue nil
97
+ Process.waitpid(pid) rescue nil
98
+ end
99
+
100
+ def test_sysread_full_timeout
101
+ nr = 100
102
+ str = 'abcde'
103
+ expect = str * nr
104
+ rd, wr = IO.pipe
105
+ pid = fork do
106
+ rd.close
107
+ nr.times do
108
+ syswrite_full(wr, str)
109
+ sleep 0.01
110
+ end
111
+ end
112
+ wr.close
113
+ assert_raises(MogileFS::Timeout) { sysread_full(rd,expect.size,0.1,true) }
114
+ rd.close
115
+ ensure
116
+ Process.kill('TERM', pid) rescue nil
117
+ Process.waitpid(pid) rescue nil
118
+ end
119
+
59
120
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: mogilefs-client
3
3
  version: !ruby/object:Gem::Version
4
- version: 2.0.2
4
+ version: 2.1.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Eric Wong
@@ -10,7 +10,7 @@ autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
12
 
13
- date: 2009-03-02 00:00:00 -08:00
13
+ date: 2009-04-11 00:00:00 -07:00
14
14
  default_executable:
15
15
  dependencies:
16
16
  - !ruby/object:Gem::Dependency
@@ -31,9 +31,9 @@ dependencies:
31
31
  requirements:
32
32
  - - ">="
33
33
  - !ruby/object:Gem::Version
34
- version: 1.8.3
34
+ version: 1.10.0
35
35
  version:
36
- description: git://git.bogomips.org/mogilefs-client.git
36
+ description: "Source repository (git):"
37
37
  email: normalperson@yhbt.net
38
38
  executables:
39
39
  - mog