mogilefs-client 3.0.0.rc1 → 3.0.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/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
|