mogilefs-client 2.0.2 → 2.1.0

Sign up to get free protection for your applications and to get access to all the features.
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