mogilefs-client 2.2.0 → 3.0.0.rc1

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 (63) hide show
  1. data/.document +11 -0
  2. data/.gemtest +0 -0
  3. data/.gitignore +4 -0
  4. data/.wrongdoc.yml +5 -0
  5. data/GIT-VERSION-GEN +28 -0
  6. data/GNUmakefile +44 -0
  7. data/HACKING +33 -0
  8. data/{History.txt → History} +0 -1
  9. data/{LICENSE.txt → LICENSE} +0 -1
  10. data/Manifest.txt +34 -7
  11. data/README +51 -0
  12. data/Rakefile +11 -11
  13. data/TODO +10 -0
  14. data/bin/mog +109 -68
  15. data/examples/mogstored_rack.rb +189 -0
  16. data/lib/mogilefs.rb +56 -17
  17. data/lib/mogilefs/admin.rb +128 -62
  18. data/lib/mogilefs/backend.rb +205 -95
  19. data/lib/mogilefs/bigfile.rb +54 -70
  20. data/lib/mogilefs/bigfile/filter.rb +58 -0
  21. data/lib/mogilefs/chunker.rb +30 -0
  22. data/lib/mogilefs/client.rb +0 -2
  23. data/lib/mogilefs/copy_stream.rb +30 -0
  24. data/lib/mogilefs/http_file.rb +175 -0
  25. data/lib/mogilefs/http_reader.rb +79 -0
  26. data/lib/mogilefs/mogilefs.rb +242 -148
  27. data/lib/mogilefs/mysql.rb +3 -4
  28. data/lib/mogilefs/paths_size.rb +24 -0
  29. data/lib/mogilefs/pool.rb +0 -1
  30. data/lib/mogilefs/socket.rb +9 -0
  31. data/lib/mogilefs/socket/kgio.rb +55 -0
  32. data/lib/mogilefs/socket/pure_ruby.rb +70 -0
  33. data/lib/mogilefs/socket_common.rb +58 -0
  34. data/lib/mogilefs/util.rb +6 -169
  35. data/test/aggregate.rb +11 -11
  36. data/test/exec.rb +72 -0
  37. data/test/fresh.rb +222 -0
  38. data/test/integration.rb +43 -0
  39. data/test/setup.rb +1 -0
  40. data/test/socket_test.rb +98 -0
  41. data/test/test_admin.rb +14 -37
  42. data/test/test_backend.rb +50 -107
  43. data/test/test_bigfile.rb +2 -2
  44. data/test/test_db_backend.rb +1 -2
  45. data/test/test_fresh.rb +8 -0
  46. data/test/test_http_reader.rb +34 -0
  47. data/test/test_mogilefs.rb +278 -98
  48. data/test/test_mogilefs_integration.rb +174 -0
  49. data/test/test_mogilefs_integration_large_pipe.rb +62 -0
  50. data/test/test_mogilefs_integration_list_keys.rb +40 -0
  51. data/test/test_mogilefs_socket_kgio.rb +11 -0
  52. data/test/test_mogilefs_socket_pure.rb +7 -0
  53. data/test/test_mogstored_rack.rb +89 -0
  54. data/test/test_mogtool_bigfile.rb +116 -0
  55. data/test/test_mysql.rb +1 -2
  56. data/test/test_pool.rb +1 -1
  57. data/test/test_unit_mogstored_rack.rb +72 -0
  58. metadata +76 -54
  59. data/README.txt +0 -80
  60. data/lib/mogilefs/httpfile.rb +0 -157
  61. data/lib/mogilefs/network.rb +0 -107
  62. data/test/test_network.rb +0 -56
  63. data/test/test_util.rb +0 -121
@@ -1,7 +1,6 @@
1
1
  # -*- encoding: binary -*-
2
- require 'mogilefs'
3
- require 'mogilefs/backend' # for the exceptions
4
-
2
+ # Consider this deprecated, to be removed at some point...
3
+ #
5
4
  # read-only interface that can be a backend for MogileFS::MogileFS
6
5
  #
7
6
  # This provides direct, read-only access to any slave MySQL database to
@@ -29,7 +28,7 @@ class MogileFS::Mysql
29
28
  ##
30
29
  # Lists keys starting with +prefix+ follwing +after+ up to +limit+. If
31
30
  # +after+ is nil the list starts at the beginning.
32
- def _list_keys(domain, prefix = '', after = '', limit = 1000, &block)
31
+ def _list_keys(domain, prefix = '', after = '', limit = 1000)
33
32
  # this code is based on server/lib/MogileFS/Worker/Query.pm
34
33
  dmid = get_dmid(domain)
35
34
 
@@ -0,0 +1,24 @@
1
+ # -*- encoding: binary -*-
2
+ # This is only a hack for old MogileFS installs that didn't have file_info
3
+ require "net/http"
4
+ require "uri"
5
+ module MogileFS::PathsSize
6
+ def self.call(paths)
7
+ errors = {}
8
+ paths.each do |path|
9
+ uri = URI.parse(path)
10
+ begin
11
+ case r = Net::HTTP.start(uri.host, uri.port) { |x| x.head(uri.path) }
12
+ when Net::HTTPOK
13
+ return r["Content-Length"].to_i
14
+ else
15
+ errors[path] = r
16
+ end
17
+ rescue => err
18
+ errors[path] = err
19
+ end
20
+ end
21
+ errors = errors.map { |path,err| "#{path} - #{err.message} (#{err.class})" }
22
+ raise MogileFS::Error, "all paths failed with HEAD: #{errors.join(', ')}"
23
+ end
24
+ end
data/lib/mogilefs/pool.rb CHANGED
@@ -1,6 +1,5 @@
1
1
  # -*- encoding: binary -*-
2
2
  require 'thread'
3
- require 'mogilefs'
4
3
 
5
4
  class MogileFS::Pool
6
5
 
@@ -0,0 +1,9 @@
1
+ # -*- encoding: binary -*-
2
+ # internal implementation details here, do not rely on this in your code
3
+ require "mogilefs/socket_common"
4
+ begin
5
+ raise LoadError, "testing pure Ruby version" if ENV["MOGILEFS_CLIENT_PURE"]
6
+ require "mogilefs/socket/kgio"
7
+ rescue LoadError
8
+ require "mogilefs/socket/pure_ruby"
9
+ end
@@ -0,0 +1,55 @@
1
+ # -*- encoding: binary -*-
2
+ # internal implementation details here, do not rely on them in your code
3
+ require "kgio"
4
+
5
+ class MogileFS::Socket < Kgio::Socket
6
+ include MogileFS::SocketCommon
7
+
8
+ def self.start(host, port)
9
+ sock = super(Socket.sockaddr_in(port, host))
10
+ sock.post_init(host, port)
11
+ end
12
+
13
+ def self.tcp(host, port, timeout = 5)
14
+ sock = start(host, port)
15
+ unless sock.kgio_wait_writable(timeout)
16
+ sock.close
17
+ raise MogileFS::Timeout, 'socket connect timeout'
18
+ end
19
+ sock
20
+ end
21
+
22
+ def timed_read(len, dst = "", timeout = 5)
23
+ case rc = kgio_tryread(len, dst)
24
+ when :wait_readable
25
+ kgio_wait_readable(timeout) or unreadable_socket!
26
+ else
27
+ return rc
28
+ end while true
29
+ end
30
+
31
+ def timed_peek(len, dst, timeout = 5)
32
+ case rc = kgio_trypeek(len, dst)
33
+ when :wait_readable
34
+ kgio_wait_readable(timeout) or unreadable_socket!
35
+ else
36
+ return rc
37
+ end while true
38
+ end
39
+
40
+ def timed_write(buf, timeout = 5)
41
+ written = 0
42
+ expect = buf.bytesize
43
+ case rc = kgio_trywrite(buf)
44
+ when :wait_writable
45
+ kgio_wait_writable(timeout) or request_truncated!(written, expect)
46
+ when String
47
+ written += expect - rc.bytesize
48
+ buf = rc
49
+ else
50
+ return expect
51
+ end while true
52
+ end
53
+
54
+ alias write timed_write
55
+ end
@@ -0,0 +1,70 @@
1
+ # -*- encoding: binary -*-
2
+ # internal implementation details here, do not rely on them in your code
3
+
4
+ class MogileFS::Socket < Socket
5
+ include MogileFS::SocketCommon
6
+
7
+ def self.start(host, port)
8
+ sock = new(Socket::AF_INET, Socket::SOCK_STREAM, 0)
9
+ begin
10
+ sock.connect_nonblock(sockaddr_in(port, host))
11
+ rescue Errno::EINPROGRESS
12
+ end
13
+ sock.post_init(host, port)
14
+ end
15
+
16
+ def self.tcp(host, port, timeout = 5)
17
+ sock = start(host, port)
18
+ unless IO.select(nil, [ sock ], nil, timeout)
19
+ sock.close
20
+ raise MogileFS::Timeout, 'socket connect timeout'
21
+ end
22
+ sock
23
+ end
24
+
25
+ def timed_read(len, dst = "", timeout = 5)
26
+ begin
27
+ IO.select([self], nil, nil, timeout) or unreadable_socket!
28
+ return read_nonblock(len, dst)
29
+ rescue Errno::EAGAIN
30
+ rescue EOFError
31
+ return
32
+ end while true
33
+ end
34
+
35
+ def timed_peek(len, dst, timeout = 5)
36
+ begin
37
+ IO.select([self], nil, nil, timeout) or unreadable_socket!
38
+ rc = recv_nonblock(len, Socket::MSG_PEEK)
39
+ return rc.empty? ? nil : dst.replace(rc)
40
+ rescue Errno::EAGAIN
41
+ rescue EOFError
42
+ dst.replace("")
43
+ return
44
+ end while true
45
+ end
46
+
47
+ def timed_write(buf, timeout = 5)
48
+ written = 0
49
+ expect = buf.bytesize
50
+ begin
51
+ rc = write_nonblock(buf)
52
+ return expect if rc == buf.bytesize
53
+ written += rc
54
+
55
+ if buf.respond_to?(:byteslice)
56
+ buf = buf.byteslice(rc, buf.bytesize)
57
+ else
58
+ if buf.respond_to?(:encoding) && buf.encoding != Encoding::BINARY
59
+ buf = buf.dup.force_encoding(Encoding::BINARY)
60
+ end
61
+ buf = buf.slice(rc, buf.bytesize)
62
+ end
63
+ rescue Errno::EAGAIN
64
+ IO.select(nil, [self], nil, timeout) or
65
+ request_truncated!(written, expect)
66
+ end while true
67
+ end
68
+
69
+ alias write timed_write
70
+ end
@@ -0,0 +1,58 @@
1
+ # -*- encoding: binary -*-
2
+ # internal implementation details here, do not rely on this in your code
3
+ require "socket"
4
+
5
+ module MogileFS::SocketCommon
6
+ attr_reader :mogilefs_addr
7
+
8
+ def post_init(host, port)
9
+ @mogilefs_addr = "#{host}:#{port}"
10
+ Socket.const_defined?(:TCP_NODELAY) and
11
+ setsockopt(Socket::IPPROTO_TCP, Socket::TCP_NODELAY, 1)
12
+ self
13
+ end
14
+
15
+ def unreadable_socket!
16
+ raise MogileFS::UnreadableSocketError,
17
+ "#@mogilefs_addr never became readable"
18
+ end
19
+
20
+ def request_truncated!(written, expect)
21
+ raise MogileFS::RequestTruncatedError,
22
+ "request truncated (sent #{written} expected #{expect})"
23
+ end
24
+
25
+ SEP_RE = /\A(.*?#{Regexp.escape("\n")})/
26
+ def timed_gets(timeout = 5)
27
+ unless defined?(@rbuf) && @rbuf
28
+ @rbuf = timed_read(1024, "", timeout) or return # EOF
29
+ end
30
+ begin
31
+ @rbuf.sub!(SEP_RE, "") and return $1
32
+ tmp ||= ""
33
+ if timed_read(1024, tmp, timeout)
34
+ @rbuf << tmp
35
+ else
36
+ # EOF, return the last buffered bit even without SEP_RE matching
37
+ # (not ideal for MogileFS, this is an error)
38
+ return @rbuf.empty? ? nil : @rbuf.slice!(0, @rbuf.size)
39
+ end
40
+ end while true
41
+ end
42
+
43
+ def read(size, buf = "", timeout = 5)
44
+ timed_read(size, buf, timeout) or return # nil/EOF
45
+
46
+ while size > buf.bytesize
47
+ tmp ||= ""
48
+ timed_read(size - buf.bytesize, tmp, timeout) or return buf # truncated
49
+ buf << tmp
50
+ end
51
+
52
+ buf # full read
53
+ end
54
+
55
+ def readpartial(size, buf = "", timeout = 5)
56
+ timed_read(size, buf, timeout) or raise EOFError, "end of file reached"
57
+ end
58
+ end
data/lib/mogilefs/util.rb CHANGED
@@ -1,96 +1,13 @@
1
1
  # -*- encoding: binary -*-
2
- require 'mogilefs'
3
- require 'socket'
4
2
 
5
3
  module MogileFS::Util
6
4
 
7
- CHUNK_SIZE = 65536
8
-
9
- # for copying large files while avoiding GC thrashing as much as possible
10
- # writes the contents of io_rd into io_wr, running through filter if
11
- # it is a Proc object. The filter proc must respond to a string
12
- # argument (and return a string) and to nil (possibly returning a
13
- # string or nil). This can be used to filter I/O through an
14
- # Zlib::Inflate or Digest::MD5 object
15
- def sysrwloop(io_rd, io_wr, filter = nil)
16
- copied = 0
17
- # avoid making sysread repeatedly allocate a new String
18
- # This is not well-documented, but both read/sysread can take
19
- # an optional second argument to use as the buffer to avoid
20
- # GC overhead of creating new strings in a loop
21
- buf = ' ' * CHUNK_SIZE # preallocate to avoid GC thrashing
22
- io_rd.flush rescue nil # flush may be needed for sockets/pipes, be safe
23
- io_wr.flush
24
- io_rd.sync = io_wr.sync = true
25
- loop do
26
- b = begin
27
- io_rd.sysread(CHUNK_SIZE, buf)
28
- rescue Errno::EAGAIN, Errno::EINTR
29
- IO.select([io_rd], nil, nil, nil)
30
- retry
31
- rescue EOFError
32
- break
33
- end
34
- b = filter.call(b) if filter
35
- copied += syswrite_full(io_wr, b)
36
- end
37
-
38
- # filter must take nil as a possible argument to indicate EOF
39
- if filter
40
- b = filter.call(nil)
41
- copied += syswrite_full(io_wr, b) if b && b.length > 0
42
- end
43
- copied
44
- end # sysrwloop
45
-
46
- # writes the contents of buf to io_wr in full w/o blocking
47
- def syswrite_full(io_wr, buf, timeout = nil)
48
- written = 0
49
- loop do
50
- begin
51
- w = io_wr.syswrite(buf)
52
- written += w
53
- return written if w == buf.size
54
- buf = buf[w..-1]
55
-
56
- # a short syswrite means the next syswrite will likely block
57
- # inside the interpreter. so force an IO.select on it so we can
58
- # timeout there if one was specified
59
- raise Errno::EAGAIN if timeout
60
- rescue Errno::EAGAIN, Errno::EINTR
61
- t0 = Time.now if timeout
62
- IO.select(nil, [io_wr], nil, timeout)
63
- if timeout && ((timeout -= (Time.now - t0)) < 0)
64
- raise MogileFS::Timeout, 'syswrite_full timeout'
65
- end
66
- end
67
- end
68
- # should never get here
69
- end
70
-
71
- def sysread_full(io_rd, size, timeout = nil, full_timeout = false)
72
- tmp = [] # avoid expensive string concatenation with every loop iteration
73
- reader = io_rd.method(timeout ? :read_nonblock : :sysread)
74
- begin
75
- while size > 0
76
- tmp << reader.call(size)
77
- size -= tmp.last.size
78
- end
79
- rescue Errno::EAGAIN, Errno::EINTR
80
- t0 = Time.now
81
- r = IO.select([ io_rd ], nil, nil, timeout)
82
- if timeout
83
- timeout -= (Time.now - t0) if full_timeout
84
- if !(r && r[0]) || timeout < 0
85
- raise MogileFS::Timeout, 'sysread_full timeout'
86
- end
87
- end
88
- retry
89
- rescue EOFError
90
- end
91
- tmp.join('')
92
- end
93
-
5
+ # MogileFS::Util::StoreContent allows you to roll your own method
6
+ # of streaming data on an upload (instead of using a string or file)
7
+ #
8
+ # Current versions of this library support streaming a IO or IO-like
9
+ # object to using MogileFS::MogileFS#store_file, so using StoreContent
10
+ # may no longer be necessary.
94
11
  class StoreContent < Proc
95
12
  def initialize(total_size, &writer_proc)
96
13
  @total_size = total_size
@@ -100,7 +17,6 @@ module MogileFS::Util
100
17
  @total_size
101
18
  end
102
19
  end
103
-
104
20
  end
105
21
 
106
22
  require 'timeout'
@@ -109,82 +25,3 @@ require 'timeout'
109
25
  # reason we require the 'timeout' module, otherwise that module is
110
26
  # broken and worthless to us.
111
27
  class MogileFS::Timeout < Timeout::Error; end
112
-
113
- class Socket
114
- attr_accessor :mogilefs_addr, :mogilefs_connected, :mogilefs_size
115
-
116
- # Socket lacks peeraddr method of the IPSocket/TCPSocket classes
117
- def mogilefs_peername
118
- Socket.unpack_sockaddr_in(getpeername).reverse.map {|x| x.to_s }.join(':')
119
- end
120
-
121
- def mogilefs_init(host = nil, port = nil)
122
- return true if defined?(@mogilefs_connected)
123
-
124
- @mogilefs_addr = Socket.sockaddr_in(port, host).freeze if port && host
125
-
126
- begin
127
- connect_nonblock(@mogilefs_addr)
128
- @mogilefs_connected = true
129
- rescue Errno::EINPROGRESS
130
- nil
131
- rescue Errno::EISCONN
132
- @mogilefs_connected = true
133
- end
134
- end
135
-
136
- class << self
137
-
138
- # Creates a new (TCP) Socket and initiates (but does not wait for) the
139
- # connection
140
- def mogilefs_new_nonblock(host, port)
141
- sock = Socket.new(Socket::AF_INET, Socket::SOCK_STREAM, 0)
142
- if defined?(Socket::TCP_NODELAY)
143
- sock.setsockopt(Socket::IPPROTO_TCP, Socket::TCP_NODELAY, 1)
144
- end
145
- sock.mogilefs_init(host, port)
146
- sock
147
- end
148
-
149
- # Like TCPSocket.new(host, port), but with an explicit timeout
150
- # (and we don't care for local address/port we're binding to).
151
- # This raises MogileFS::Timeout if timeout expires
152
- def mogilefs_new(host, port, timeout = 5.0)
153
- sock = mogilefs_new_nonblock(host, port) or return sock
154
-
155
- while timeout > 0
156
- t0 = Time.now
157
- r = IO.select(nil, [sock], nil, timeout)
158
- return sock if r && r[1] && sock.mogilefs_init
159
- timeout -= (Time.now - t0)
160
- end
161
-
162
- sock.close rescue nil
163
- raise MogileFS::Timeout, 'socket write timeout'
164
- end
165
-
166
- include MogileFS::Util
167
-
168
- # Makes a request on a new TCP Socket and returns with a readble socket
169
- # within the given timeout.
170
- # This raises MogileFS::Timeout if timeout expires
171
- def mogilefs_new_request(host, port, request, timeout = 5.0)
172
- t0 = Time.now
173
- sock = mogilefs_new(host, port, timeout)
174
- syswrite_full(sock, request, timeout)
175
- timeout -= (Time.now - t0)
176
- if timeout < 0
177
- sock.close rescue nil
178
- raise MogileFS::Timeout, 'socket read timeout'
179
- end
180
- r = IO.select([sock], nil, nil, timeout)
181
- return sock if r && r[0]
182
-
183
- sock.close rescue nil
184
- raise MogileFS::Timeout, 'socket read timeout'
185
- end
186
-
187
- end
188
-
189
- end
190
-
data/test/aggregate.rb CHANGED
@@ -1,14 +1,14 @@
1
- #!/usr/bin/ruby -n
1
+ #!/usr/bin/ruby
2
2
  # -*- encoding: binary -*-
3
- BEGIN { $tests = $assertions = $failures = $errors = 0 }
3
+ $tests = $assertions = $failures = $errors = 0
4
4
 
5
- $_ =~ /(\d+) tests, (\d+) assertions, (\d+) failures, (\d+) errors/ or next
6
- $tests += $1.to_i
7
- $assertions += $2.to_i
8
- $failures += $3.to_i
9
- $errors += $4.to_i
5
+ STDIN.each_line do |l|
6
+ l =~ /(\d+) tests, (\d+) assertions, (\d+) failures, (\d+) errors/ or next
7
+ $tests += $1.to_i
8
+ $assertions += $2.to_i
9
+ $failures += $3.to_i
10
+ $errors += $4.to_i
11
+ end
10
12
 
11
- END {
12
- printf("\n%d tests, %d assertions, %d failures, %d errors\n",
13
- $tests, $assertions, $failures, $errors)
14
- }
13
+ printf("\n%d tests, %d assertions, %d failures, %d errors\n",
14
+ $tests, $assertions, $failures, $errors)