mogilefs-client 3.0.0.rc1 → 3.0.0
Sign up to get free protection for your applications and to get access to all the features.
- data/GIT-VERSION-GEN +1 -1
- data/examples/mogstored_rack.rb +35 -36
- data/lib/mogilefs/backend.rb +7 -6
- data/lib/mogilefs/copy_stream.rb +1 -1
- data/lib/mogilefs/http_file.rb +17 -1
- data/lib/mogilefs/socket/kgio.rb +0 -2
- data/lib/mogilefs/socket/pure_ruby.rb +0 -2
- data/test/test_mogilefs.rb +2 -2
- data/test/test_mogilefs_integration.rb +0 -2
- metadata +8 -12
data/GIT-VERSION-GEN
CHANGED
data/examples/mogstored_rack.rb
CHANGED
@@ -1,5 +1,4 @@
|
|
1
1
|
# -*- encoding: binary -*-
|
2
|
-
require 'tempfile'
|
3
2
|
require 'digest/md5'
|
4
3
|
require 'rack'
|
5
4
|
|
@@ -22,11 +21,28 @@ class MogstoredRack
|
|
22
21
|
|
23
22
|
def initialize(root, opts = {})
|
24
23
|
@root = File.expand_path(root)
|
24
|
+
@io_size = opts[:io_size] || 0x100000
|
25
25
|
@rack_file = (opts[:app] || Rack::File.new(@root))
|
26
26
|
@fsync = !! opts[:fsync]
|
27
27
|
@creat_perms = opts[:creat_perms] || (~File.umask & 0666)
|
28
28
|
@mkdir_perms = opts[:mkdir_perms] || (~File.umask & 0777)
|
29
29
|
@reread_verify = !! opts[:reread_verify]
|
30
|
+
@open_flags = opts[:open_flags] || 0
|
31
|
+
@open_flags |= IO::RDWR | IO::CREAT | IO::EXCL
|
32
|
+
end
|
33
|
+
|
34
|
+
def tmpfile(basename, dir)
|
35
|
+
t = Time.now.utc.strftime("%Y%m%d%H%M%S")
|
36
|
+
seq = 0
|
37
|
+
begin
|
38
|
+
fp = File.open("#{dir}/#{basename}.#{t}.#{seq}.tmp", @open_flags, 0600)
|
39
|
+
rescue Errno::EEXIST
|
40
|
+
retry if (seq += 1) < 10
|
41
|
+
raise
|
42
|
+
end
|
43
|
+
fp.binmode
|
44
|
+
fp.sync = true
|
45
|
+
fp
|
30
46
|
end
|
31
47
|
|
32
48
|
def call(env)
|
@@ -75,32 +91,29 @@ class MogstoredRack
|
|
75
91
|
dir = File.dirname(path)
|
76
92
|
File.directory?(dir) or return r(403)
|
77
93
|
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
err = content_md5_fail?(env, received) and return err
|
85
|
-
if @reread_verify && err = reread_md5_fail?(env, tmp, received, buf)
|
86
|
-
return err
|
87
|
-
end
|
88
|
-
tmp.chmod(@creat_perms)
|
89
|
-
begin
|
90
|
-
File.link(tmp.path, path)
|
91
|
-
rescue Errno::EEXIST
|
92
|
-
err = rename_overwrite_fail?(tmp.path, path) and return err
|
93
|
-
end
|
94
|
-
fsync(dir, tmp) if @fsync
|
95
|
-
resp = r(201)
|
96
|
-
resp[1]["X-Received-Content-MD5"] = received
|
97
|
-
return resp
|
94
|
+
tmp = tmpfile(File.basename(path), dir)
|
95
|
+
buf = ""
|
96
|
+
received = put_loop(env["rack.input"], tmp, buf)
|
97
|
+
err = content_md5_fail?(env, received) and return err
|
98
|
+
if @reread_verify && err = reread_md5_fail?(env, tmp, received, buf)
|
99
|
+
return err
|
98
100
|
end
|
101
|
+
tmp.chmod(@creat_perms)
|
102
|
+
File.rename(tmp.path, path)
|
103
|
+
fsync(dir, tmp) if @fsync
|
104
|
+
resp = r(201)
|
105
|
+
resp[1]["X-Received-Content-MD5"] = received
|
106
|
+
resp
|
107
|
+
rescue
|
108
|
+
File.unlink(tmp.path) if tmp && File.exist?(tmp.path)
|
109
|
+
raise
|
110
|
+
ensure
|
111
|
+
tmp.close if tmp
|
99
112
|
end
|
100
113
|
|
101
114
|
def put_loop(src, dst, buf)
|
102
115
|
md5 = ContentMD5.new
|
103
|
-
while src.read(
|
116
|
+
while src.read(@io_size, buf)
|
104
117
|
md5.update(buf)
|
105
118
|
dst.write(buf)
|
106
119
|
end
|
@@ -167,20 +180,6 @@ class MogstoredRack
|
|
167
180
|
"received: #{received}", env)
|
168
181
|
end
|
169
182
|
|
170
|
-
def rename_overwrite_fail?(src, dst)
|
171
|
-
10.times do
|
172
|
-
begin
|
173
|
-
tmp_dst = "#{dst}.#{rand}"
|
174
|
-
File.link(src, tmp_dst)
|
175
|
-
rescue Errno::EEXIST
|
176
|
-
next
|
177
|
-
end
|
178
|
-
File.rename(tmp_dst, dst)
|
179
|
-
return false # success!
|
180
|
-
end
|
181
|
-
r(409)
|
182
|
-
end
|
183
|
-
|
184
183
|
# fsync each and every directory component above us on the same device
|
185
184
|
def fsync(dir, tmp)
|
186
185
|
tmp.fsync
|
data/lib/mogilefs/backend.rb
CHANGED
@@ -295,7 +295,10 @@ class MogileFS::Backend
|
|
295
295
|
# Turns the +line+ response from the server into a Hash of options, an
|
296
296
|
# error, or raises, as appropriate.
|
297
297
|
def parse_response(line, request = nil)
|
298
|
-
|
298
|
+
case line
|
299
|
+
when /\AOK\s+\d*\s*(\S*)\r?\n\z/
|
300
|
+
url_decode($1)
|
301
|
+
when /\AERR\s+(\w+)\s*([^\r\n]*)/
|
299
302
|
@lasterr = $1
|
300
303
|
@lasterrstr = $2 ? url_unescape($2) : nil
|
301
304
|
if request
|
@@ -304,12 +307,10 @@ class MogileFS::Backend
|
|
304
307
|
return error(@lasterr).new(@lasterrstr)
|
305
308
|
end
|
306
309
|
raise error(@lasterr).new(@lasterrstr)
|
310
|
+
else
|
311
|
+
raise MogileFS::InvalidResponseError,
|
312
|
+
"Invalid response from server: #{line.inspect}"
|
307
313
|
end
|
308
|
-
|
309
|
-
return url_decode($1) if line =~ /^OK\s+\d*\s*(\S*)\r\n\z/
|
310
|
-
|
311
|
-
raise MogileFS::InvalidResponseError,
|
312
|
-
"Invalid response from server: #{line.inspect}"
|
313
314
|
end
|
314
315
|
|
315
316
|
# this command is special since the cache is per-tracker, so we connect
|
data/lib/mogilefs/copy_stream.rb
CHANGED
@@ -3,7 +3,7 @@
|
|
3
3
|
# internal compatibility class for older Rubies
|
4
4
|
module MogileFS::CopyStream # :nodoc:
|
5
5
|
@r_args = IO::RDONLY | IO::NOCTTY
|
6
|
-
@w_args = [ IO::WRONLY|IO::CREAT|IO::NOCTTY|IO::TRUNC,
|
6
|
+
@w_args = [ IO::WRONLY|IO::CREAT|IO::NOCTTY|IO::TRUNC, 0666 ]
|
7
7
|
def self.copy_stream(src, dst)
|
8
8
|
src_io = src.respond_to?(:to_str) ? File.open(src, @r_args) : src
|
9
9
|
dst_io = dst.respond_to?(:to_str) ? File.open(dst, *@w_args) : dst
|
data/lib/mogilefs/http_file.rb
CHANGED
@@ -20,12 +20,27 @@ class MogileFS::HTTPFile < StringIO
|
|
20
20
|
end
|
21
21
|
class NonRetryableError < MogileFS::Error; end
|
22
22
|
|
23
|
+
class HTTPSock < MogileFS::Socket
|
24
|
+
attr_accessor :start
|
25
|
+
|
26
|
+
# Increase timeout as we become more invested in uploading with
|
27
|
+
# this socket. The server could be experiencing I/O delays
|
28
|
+
# from large uploads because the sysadmin forgot to tune the
|
29
|
+
# VM sysctls for handling large files.
|
30
|
+
def write(buf)
|
31
|
+
timed_write(buf, Time.now - @start + 5.0)
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
23
35
|
# :stopdoc:
|
24
36
|
MD5_TRAILER_NODES = {} # :nodoc: # EXPERIMENTAL
|
25
37
|
class << self
|
26
38
|
attr_accessor :response_timeout_cb
|
27
39
|
end
|
28
40
|
|
41
|
+
# temporary directories (nginx) may not be configured on the
|
42
|
+
# same device, necessitating a time-consuming full file copy
|
43
|
+
# instead of a quick rename(2)/link(2) operation
|
29
44
|
@response_timeout_cb = lambda do |elapsed_time, bytes_uploaded|
|
30
45
|
mbytes_uploaded = bytes_uploaded / (1024.0 * 1024.0)
|
31
46
|
# assumes worst case is 10M/s on the remote storage disk
|
@@ -107,7 +122,8 @@ class MogileFS::HTTPFile < StringIO
|
|
107
122
|
# returns file size if the socket finished writing
|
108
123
|
def upload(devid, uri) # :nodoc:
|
109
124
|
start = Time.now
|
110
|
-
sock =
|
125
|
+
sock = HTTPSock.tcp(uri.host, uri.port)
|
126
|
+
sock.start = start
|
111
127
|
file_size = length
|
112
128
|
|
113
129
|
if @streaming_io
|
data/lib/mogilefs/socket/kgio.rb
CHANGED
data/test/test_mogilefs.rb
CHANGED
@@ -249,7 +249,7 @@ class TestMogileFS__MogileFS < TestMogileFS
|
|
249
249
|
end
|
250
250
|
|
251
251
|
def test_store_content_http
|
252
|
-
received = Tempfile.new('
|
252
|
+
received = Tempfile.new('received')
|
253
253
|
expected = "PUT /path HTTP/1.0\r\nContent-Length: 4\r\n\r\ndata"
|
254
254
|
|
255
255
|
t = TempServer.new(Proc.new do |serv, accept|
|
@@ -282,7 +282,7 @@ class TestMogileFS__MogileFS < TestMogileFS
|
|
282
282
|
|
283
283
|
|
284
284
|
def test_store_content_with_writer_callback
|
285
|
-
received = Tempfile.new('
|
285
|
+
received = Tempfile.new('received')
|
286
286
|
expected = "PUT /path HTTP/1.0\r\nContent-Length: 40\r\n\r\n"
|
287
287
|
10.times do
|
288
288
|
expected += "data"
|
@@ -19,8 +19,6 @@ class TestMogileFSIntegration < TestMogIntegration
|
|
19
19
|
tmp.close!
|
20
20
|
assert_equal 4, @client.get_file_data("CRUD", tmp_path)
|
21
21
|
assert_equal "DATA", File.read(tmp_path)
|
22
|
-
st = File.stat(tmp_path)
|
23
|
-
assert_equal 0100600, st.mode
|
24
22
|
File.unlink(tmp_path)
|
25
23
|
|
26
24
|
sio = StringIO.new("")
|
metadata
CHANGED
@@ -1,15 +1,13 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: mogilefs-client
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
hash:
|
5
|
-
prerelease:
|
4
|
+
hash: 7
|
5
|
+
prerelease:
|
6
6
|
segments:
|
7
7
|
- 3
|
8
8
|
- 0
|
9
9
|
- 0
|
10
|
-
|
11
|
-
- 1
|
12
|
-
version: 3.0.0.rc1
|
10
|
+
version: 3.0.0
|
13
11
|
platform: ruby
|
14
12
|
authors:
|
15
13
|
- Eric Wong
|
@@ -17,7 +15,7 @@ autorequire:
|
|
17
15
|
bindir: bin
|
18
16
|
cert_chain: []
|
19
17
|
|
20
|
-
date: 2011-11-
|
18
|
+
date: 2011-11-28 00:00:00 Z
|
21
19
|
dependencies:
|
22
20
|
- !ruby/object:Gem::Dependency
|
23
21
|
name: hoe
|
@@ -130,14 +128,12 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
130
128
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
131
129
|
none: false
|
132
130
|
requirements:
|
133
|
-
- - "
|
131
|
+
- - ">="
|
134
132
|
- !ruby/object:Gem::Version
|
135
|
-
hash:
|
133
|
+
hash: 3
|
136
134
|
segments:
|
137
|
-
-
|
138
|
-
|
139
|
-
- 1
|
140
|
-
version: 1.3.1
|
135
|
+
- 0
|
136
|
+
version: "0"
|
141
137
|
requirements: []
|
142
138
|
|
143
139
|
rubyforge_project: seattlerb
|