mogilefs-client 3.7.1 → 3.8.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.document +0 -1
- data/.gitignore +3 -3
- data/{.wrongdoc.yml → .olddoc.yml} +2 -0
- data/GIT-VERSION-GEN +13 -2
- data/GNUmakefile +12 -86
- data/README +19 -9
- data/TODO +0 -4
- data/examples/mog-sync.rb +245 -0
- data/examples/usage_fetcher.rb +44 -0
- data/lib/mogilefs/admin.rb +12 -10
- data/mogilefs-client.gemspec +20 -0
- data/pkg.mk +150 -0
- data/test/fresh.rb +10 -82
- data/test/setup.rb +2 -6
- data/test/test_admin.rb +25 -1
- data/test/test_fresh.rb +71 -0
- data/test/test_mogilefs.rb +25 -29
- data/test/test_mogilefs_integration.rb +15 -5
- data/test/test_mogilefs_integration_large_pipe.rb +7 -3
- data/test/test_mogilefs_integration_list_keys.rb +8 -4
- data/test/test_mogtool_bigfile.rb +7 -3
- metadata +39 -76
- data/.gemtest +0 -0
- data/Rakefile +0 -56
- data/test/integration.rb +0 -43
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: 65d3ada1142a27870a3926eeae7d15a13d28b016
|
4
|
+
data.tar.gz: 7ef65f2858cb0a130cbb7c0200946a7df05bf701
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: edecf8b75e5f3e218e63383c79da8ceaab6daed1890c3d51c0ca418936c30e6a613eb187ae5258137570eee8d649a612fcbaaf0cc606baa4165a817f146a681f
|
7
|
+
data.tar.gz: b9448f892913e8104c611badaf368e22acaa6faf60c70abf729200a6c0725efe2657a13498aeeb64b11b4701a62caa7f1f547eefff4c0652834745380dd04e8e
|
data/.document
CHANGED
data/.gitignore
CHANGED
data/GIT-VERSION-GEN
CHANGED
@@ -1,7 +1,8 @@
|
|
1
1
|
#!/usr/bin/env ruby
|
2
2
|
CONSTANT = "MogileFS::VERSION"
|
3
3
|
RVF = "lib/mogilefs/version.rb"
|
4
|
-
|
4
|
+
GVF = "GIT-VERSION-FILE"
|
5
|
+
DEF_VER = "v3.8.0"
|
5
6
|
vn = DEF_VER
|
6
7
|
|
7
8
|
# First see if there is a version file (included in release tarballs),
|
@@ -20,9 +21,19 @@ if File.exist?(".git")
|
|
20
21
|
end
|
21
22
|
|
22
23
|
vn = vn.sub!(/\Av/, "")
|
23
|
-
new_ruby_version = "#{CONSTANT} = '#{vn}'
|
24
|
+
new_ruby_version = "#{CONSTANT} = '#{vn}' # :nodoc:\n"
|
24
25
|
cur_ruby_version = File.read(RVF) rescue nil
|
25
26
|
if new_ruby_version != cur_ruby_version
|
26
27
|
File.open(RVF, "w") { |fp| fp.write(new_ruby_version) }
|
27
28
|
end
|
29
|
+
File.chmod(0644, RVF)
|
30
|
+
|
31
|
+
# generate the makefile snippet
|
32
|
+
new_make_version = "GIT_VERSION = #{vn}\n"
|
33
|
+
cur_make_version = File.read(GVF) rescue nil
|
34
|
+
if new_make_version != cur_make_version
|
35
|
+
File.open(GVF, "w") { |fp| fp.write(new_make_version) }
|
36
|
+
end
|
37
|
+
File.chmod(0644, GVF)
|
38
|
+
|
28
39
|
puts vn if $0 == __FILE__
|
data/GNUmakefile
CHANGED
@@ -1,90 +1,16 @@
|
|
1
|
-
|
2
|
-
all:: test
|
1
|
+
all::
|
3
2
|
RSYNC_DEST := bogomips.org:/srv/bogomips/mogilefs-client
|
4
|
-
|
3
|
+
rfpackage := mogilefs-client
|
4
|
+
pkg_extra += lib/mogilefs/version.rb
|
5
5
|
|
6
|
-
|
7
|
-
|
6
|
+
ex := $(shell git ls-files examples/)
|
7
|
+
doc_ex := $(addprefix doc/, $(ex))
|
8
8
|
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
$(RM) $(TO) $(addsuffix +,$(TO))
|
9
|
+
$(doc_ex): $(ex)
|
10
|
+
mkdir -p $(@D)
|
11
|
+
install -m 644 $< $@
|
12
|
+
-touch -r $< $@ # GNU
|
14
13
|
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
quiet_post = >$(t) 2>&1
|
19
|
-
else
|
20
|
-
# we can't rely on -o pipefail outside of bash 3+,
|
21
|
-
# so we use a stamp file to indicate success and
|
22
|
-
# have rm fail if the stamp didn't get created
|
23
|
-
stamp = $@$(log_suffix).ok
|
24
|
-
quiet_pre = @echo $(ruby) $@ $(TEST_OPTS); ! test -f $(stamp) && (
|
25
|
-
quiet_post = && > $(stamp) )>&2 | tee $(t); rm $(stamp) 2>/dev/null
|
26
|
-
endif
|
27
|
-
ruby = ruby
|
28
|
-
run_test = $(quiet_pre) setsid $(ruby) -w $@ $(TEST_OPTS) $(quiet_post) || \
|
29
|
-
(sed "s,^,$(extra): ," >&2 < $(t); exit 1)
|
30
|
-
|
31
|
-
$(T): t = $(subst .rb,.log,$@)
|
32
|
-
$(T): export RUBYLIB := $(CURDIR)/lib:$(RUBYLIB)
|
33
|
-
$(T):
|
34
|
-
$(run_test)
|
35
|
-
|
36
|
-
RUBY_VERSION_FILE = lib/mogilefs/version.rb
|
37
|
-
package:
|
38
|
-
git diff --exit-code HEAD^0
|
39
|
-
$(RM) -r pkg/
|
40
|
-
rake fix_perms
|
41
|
-
rake package
|
42
|
-
|
43
|
-
libs := $(wildcard lib/*.rb lib/*/*.rb)
|
44
|
-
flay_flags =
|
45
|
-
flog_flags =
|
46
|
-
flay: $(libs)
|
47
|
-
flay $(flay_flags) $^
|
48
|
-
flog: $(libs)
|
49
|
-
flog $(flog_flags) $^
|
50
|
-
.PHONY: $(T)
|
51
|
-
|
52
|
-
check-warnings:
|
53
|
-
@(for i in $$(git ls-files '*.rb'| grep -v '^setup\.rb$$'); \
|
54
|
-
do ruby -d -W2 -c $$i; done) | grep -v '^Syntax OK$$' || :
|
55
|
-
RSYNC = rsync
|
56
|
-
WRONGDOC = wrongdoc
|
57
|
-
|
58
|
-
doc:: .document .wrongdoc.yml $(pkg_extra)
|
59
|
-
-find lib -type f -name '*.rbc' -exec rm -f '{}' ';'
|
60
|
-
$(RM) -r doc
|
61
|
-
$(WRONGDOC) all
|
62
|
-
install -m644 $(shell LC_ALL=C grep '^[A-Z]' .document) doc/
|
63
|
-
cd doc && \
|
64
|
-
ln -s README README.txt && \
|
65
|
-
ln -s README.html README_txt.html && \
|
66
|
-
ln -s LICENSE LICENSE.txt && \
|
67
|
-
ln -s LICENSE.html LICENSE_txt.html && \
|
68
|
-
ln -s History History.txt && \
|
69
|
-
ln -s History.html History_txt.html
|
70
|
-
|
71
|
-
# Create gzip variants of the same timestamp as the original so nginx
|
72
|
-
# "gzip_static on" can serve the gzipped versions directly.
|
73
|
-
doc_gz: docs = $(shell find doc -type f ! -regex '^.*\.\(gif\|jpg\|png\|gz\)$$')
|
74
|
-
doc_gz:
|
75
|
-
for i in $(docs); do \
|
76
|
-
gzip --rsyncable -9 < $$i > $$i.gz; touch -r $$i $$i.gz; done
|
77
|
-
|
78
|
-
# this requires GNU coreutils variants
|
79
|
-
ifneq ($(RSYNC_DEST),)
|
80
|
-
publish_doc:
|
81
|
-
-git set-file-times
|
82
|
-
$(MAKE) doc
|
83
|
-
find doc/images -type f | \
|
84
|
-
TZ=UTC xargs touch -d '1970-01-01 00:00:06' doc/rdoc.css
|
85
|
-
$(MAKE) doc_gz
|
86
|
-
$(RSYNC) -av doc/ $(RSYNC_DEST)/
|
87
|
-
git ls-files | xargs touch
|
88
|
-
endif
|
89
|
-
|
90
|
-
.PHONY: doc .FORCE-GIT-VERSION-FILE
|
14
|
+
include pkg.mk
|
15
|
+
doc::
|
16
|
+
$(MAKE) $(doc_ex)
|
data/README
CHANGED
@@ -9,8 +9,10 @@ MogileFS instance.
|
|
9
9
|
|
10
10
|
rdoc :: http://bogomips.org/mogilefs-client
|
11
11
|
mogilefs :: http://mogilefs.org/
|
12
|
-
list :: mailto:
|
13
|
-
|
12
|
+
list :: mailto:mogilefs-client-public@bogomips.org
|
13
|
+
list-cc :: mailto:mogile@googlegroups.com
|
14
|
+
list-archive :: http://bogomips.org/mogilefs-client-public
|
15
|
+
email :: mailto:e@80x24.org
|
14
16
|
repo :: git://bogomips.org/mogilefs-client.git
|
15
17
|
cgit :: http://bogomips.org/mogilefs-client.git
|
16
18
|
gitweb :: http://repo.or.cz/w/ruby-mogilefs-client.git
|
@@ -35,15 +37,23 @@ See MogileFS::MogileFS
|
|
35
37
|
== Contact
|
36
38
|
|
37
39
|
Feedback (bug reports, user/development discussion, patches, pull
|
38
|
-
requests) are greatly appreciated and handled via email
|
39
|
-
|
40
|
-
|
40
|
+
requests) are greatly appreciated and handled via email to a public
|
41
|
+
inbox. HTML email is considered spam and discarded. No subscription
|
42
|
+
is required or available to post:
|
41
43
|
|
42
|
-
|
43
|
-
mailing list, or if you wish to keep your issue secret, feel free to
|
44
|
-
email Eric Wong at mailto:normalperson@yhbt.net.
|
44
|
+
mogilefs-client-public@bogomips.org
|
45
45
|
|
46
|
-
|
46
|
+
List archives: http://bogomips.org/mogilefs-client-public/
|
47
|
+
|
48
|
+
We may also piggy-back onto the public MogileFS mailing list at
|
49
|
+
mogile@googlegroups.com for feedback (subscription required,
|
50
|
+
unfortunately)
|
51
|
+
|
52
|
+
If you wish to keep your issue secret, feel free to email the author at:
|
53
|
+
|
54
|
+
e@80x24.org
|
55
|
+
|
56
|
+
Do not expect us to read HTML mail under any circumstances.
|
47
57
|
|
48
58
|
== WARNING!
|
49
59
|
|
data/TODO
CHANGED
@@ -0,0 +1,245 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
usage = <<EOF
|
3
|
+
Usage: #$0 SRC_TRACKER_LIST/SRC_DOMAIN DST_TRACKER_LIST/DST_DOMAIN"
|
4
|
+
EOF
|
5
|
+
Thread.abort_on_exception = $stdout.sync = $stderr.sync = true
|
6
|
+
require 'uri'
|
7
|
+
require 'optparse'
|
8
|
+
require 'mogilefs'
|
9
|
+
require 'thread'
|
10
|
+
@verbose = 0
|
11
|
+
copy_jobs = 1
|
12
|
+
jobs = 1
|
13
|
+
keep = nil
|
14
|
+
@dryrun = false
|
15
|
+
opts = {}
|
16
|
+
prefix = ""
|
17
|
+
src_class = dst_class = nil
|
18
|
+
src_maxlen = nil
|
19
|
+
exit_ok = true
|
20
|
+
after = nil
|
21
|
+
clobber_nsum = false
|
22
|
+
|
23
|
+
ARGV.options do |x|
|
24
|
+
x.banner = usage.strip
|
25
|
+
x.separator ''
|
26
|
+
x.on('-j', '--metadata-jobs JOBS', Integer,
|
27
|
+
'Number of metadata jobs to run in parallel') { |n|
|
28
|
+
jobs = n
|
29
|
+
}
|
30
|
+
x.on('-J', '--copy-jobs JOBS', Integer,
|
31
|
+
'Number of copy jobs to run in parallel') { |n|
|
32
|
+
copy_jobs = n
|
33
|
+
}
|
34
|
+
|
35
|
+
x.on('-h', '--help', 'Show this help message.') { puts x; exit }
|
36
|
+
%w(get_file_data_timeout new_file_max_time fail_timeout timeout).each do |t|
|
37
|
+
x.on("--#{t.tr('_', '-')} SECONDS", Integer) { |n| opts[t.to_sym] = n }
|
38
|
+
end
|
39
|
+
x.on('-v', '--verbose') { @verbose += 1 }
|
40
|
+
x.on('-d', '--delete') do
|
41
|
+
begin
|
42
|
+
require 'gdbm'
|
43
|
+
require 'tempfile'
|
44
|
+
tmp = Tempfile.new(%w(mog-sync-keep .gdbm))
|
45
|
+
at_exit { tmp.close! }
|
46
|
+
keep = GDBM.new(tmp.path)
|
47
|
+
rescue LoadError
|
48
|
+
warn "gdbm extension recommended for --delete: #{e.message} (#{e.class})"
|
49
|
+
keep = {}
|
50
|
+
end
|
51
|
+
end
|
52
|
+
x.on('-n', '--dry-run') { @dryrun = opts[:readonly] = true }
|
53
|
+
x.on('-p', '--prefix STRING') { |s| prefix = s }
|
54
|
+
x.on('--src-class STRING') { |s| src_class = s }
|
55
|
+
x.on('--dst-class STRING') { |s| dst_class = s }
|
56
|
+
x.on('--after STRING') { |s| after = s }
|
57
|
+
x.on('--max-size STRING') { |s|
|
58
|
+
mult = 1
|
59
|
+
if s.sub!(/-1\z/, "")
|
60
|
+
off = -1
|
61
|
+
elsif s.sub!(/\+1\z/, "")
|
62
|
+
off = 1
|
63
|
+
else
|
64
|
+
off = 0
|
65
|
+
end
|
66
|
+
{
|
67
|
+
/(?:K|KiB)\z/i => 1024,
|
68
|
+
/(?:M|MiB)\z/i => 1024 ** 2,
|
69
|
+
/(?:G|GiB)\z/i => 1024 ** 3,
|
70
|
+
/KB\z/i => 1000,
|
71
|
+
/MB\z/i => 1000 ** 2,
|
72
|
+
/GB/i => 1000 ** 3,
|
73
|
+
}.each do |re, m|
|
74
|
+
if s.sub!(re, "")
|
75
|
+
mult = m
|
76
|
+
break
|
77
|
+
end
|
78
|
+
end
|
79
|
+
src_maxlen = (s.to_i * mult) + off
|
80
|
+
}
|
81
|
+
x.on('-F', '--clobber-missing-checksum') { clobber_nsum = true }
|
82
|
+
x.parse!
|
83
|
+
end
|
84
|
+
|
85
|
+
@verbose = 1 if @verbose == 0 && @dryrun
|
86
|
+
ARGV.size == 2 or abort "Usage: #{usage}"
|
87
|
+
src_spec, dst_spec = ARGV
|
88
|
+
src_opts = opts.merge(readonly: true)
|
89
|
+
|
90
|
+
def client_for(str, opts = {})
|
91
|
+
trackers, domain = str.split('/', 2)
|
92
|
+
opts[:hosts] = trackers.split(/,/)
|
93
|
+
opts[:domain] = domain
|
94
|
+
MogileFS::MogileFS.new(opts)
|
95
|
+
end
|
96
|
+
|
97
|
+
# atomic for pipes/O_APPEND:
|
98
|
+
def warn(m); $stderr.syswrite("#{m}\n"); end
|
99
|
+
def echo(m); $stdout.syswrite("#{m}\n"); end
|
100
|
+
|
101
|
+
def copy(job_id, reason, src, dst, src_info, dst_info, dst_class)
|
102
|
+
key = src_info["key"]
|
103
|
+
length = src_info["length"]
|
104
|
+
unless @dryrun
|
105
|
+
opts = {
|
106
|
+
largefile: true,
|
107
|
+
class: dst_class || src_info["class"],
|
108
|
+
content_length: length,
|
109
|
+
}
|
110
|
+
|
111
|
+
# FIXME: test/support non-MD5 checksums
|
112
|
+
if /\AMD5:([a-fA-F0-9]{32})\z/ =~ src_info["checksum"]
|
113
|
+
md5 = [ $1 ].pack("H*")
|
114
|
+
opts[:content_md5] = [ md5 ].pack('m0').chomp
|
115
|
+
end
|
116
|
+
if @verbose > 1
|
117
|
+
echo "new_file(#{key}, #{opts.inspect})"
|
118
|
+
end
|
119
|
+
dst.new_file(key, opts) do |dst_io|
|
120
|
+
src.get_file_data(key, dst_io)
|
121
|
+
end
|
122
|
+
end
|
123
|
+
if @verbose > 0
|
124
|
+
echo("#{reason} #{key}")
|
125
|
+
if @verbose > 1 && dst_info
|
126
|
+
echo "I[#{job_id}] before #{dst_info.inspect}"
|
127
|
+
echo "I[#{job_id}] after #{src_info.inspect}"
|
128
|
+
end
|
129
|
+
end
|
130
|
+
Thread.current[:mog_sync_xfer] += length
|
131
|
+
rescue => e
|
132
|
+
warn "E[#{job_id}] #{e.message} (#{e.class}) (src=#{key})"
|
133
|
+
e.backtrace { |l| warn "E[#{job_id}] #{l}" }
|
134
|
+
end
|
135
|
+
|
136
|
+
copy_queue = SizedQueue.new(copy_jobs * 8)
|
137
|
+
copiers = copy_jobs.times.map do |i|
|
138
|
+
Thread.new(i) do |job_id|
|
139
|
+
Thread.current[:mog_sync_xfer] = 0
|
140
|
+
while copy_job = copy_queue.pop
|
141
|
+
copy(job_id, *copy_job)
|
142
|
+
end
|
143
|
+
end
|
144
|
+
end
|
145
|
+
|
146
|
+
queue = SizedQueue.new(jobs * 8)
|
147
|
+
consumers = jobs.times.map do |i|
|
148
|
+
Thread.new(i) do |job_id|
|
149
|
+
dst = client_for(dst_spec, opts)
|
150
|
+
src = client_for(src_spec, src_opts)
|
151
|
+
begin
|
152
|
+
key = queue.pop or break
|
153
|
+
src_info = src.file_info(key)
|
154
|
+
next if src_class && src_class != src_info["class"]
|
155
|
+
src_checksum = src_info["checksum"]
|
156
|
+
next if src_maxlen && src_info["length"] > src_maxlen
|
157
|
+
|
158
|
+
begin
|
159
|
+
# this may raise UnknownKeyError
|
160
|
+
dst_info = dst.file_info(key)
|
161
|
+
|
162
|
+
dst_checksum = dst_info["checksum"]
|
163
|
+
|
164
|
+
# maybe we need to wait for fsck to finish:
|
165
|
+
if dst_checksum == "MISSING"
|
166
|
+
warn "destination checksum broken #{dst_info.inspect} (skipped)"
|
167
|
+
next unless clobber_nsum
|
168
|
+
end
|
169
|
+
|
170
|
+
# tell user to fix source
|
171
|
+
if src_checksum == "MISSING"
|
172
|
+
warn "source checksum broken #{src_info.inspect} (skipped)"
|
173
|
+
exit_ok = false
|
174
|
+
next
|
175
|
+
end
|
176
|
+
|
177
|
+
next if dst_checksum == src_checksum
|
178
|
+
reason = "M"
|
179
|
+
rescue MogileFS::Backend::UnknownKeyError # new file
|
180
|
+
dst_info = nil
|
181
|
+
# tell user to fix source
|
182
|
+
if src_checksum == "MISSING"
|
183
|
+
warn "source checksum broken #{src_info.inspect} (copying)"
|
184
|
+
exit_ok = false
|
185
|
+
end
|
186
|
+
reason = "A"
|
187
|
+
end
|
188
|
+
copy_queue << [ reason, src, dst, src_info, dst_info, dst_class ]
|
189
|
+
rescue => e
|
190
|
+
warn "E[#{job_id}] #{e.message} (#{e.class}) (src=#{key})"
|
191
|
+
e.backtrace { |l| warn "E[#{job_id}] #{l}" }
|
192
|
+
end while true
|
193
|
+
end
|
194
|
+
end
|
195
|
+
|
196
|
+
# producer feeds consumers
|
197
|
+
begin
|
198
|
+
main_src = client_for(src_spec, src_opts)
|
199
|
+
main_src.each_key(prefix, after: after) do |key|
|
200
|
+
keep[key] = "1" if keep
|
201
|
+
queue << key
|
202
|
+
end
|
203
|
+
rescue => e
|
204
|
+
exit_ok = false
|
205
|
+
warn "Aborting due to source error: #{e.message} (#{e.class})"
|
206
|
+
e.backtrace { |l| warn "#{l}" }
|
207
|
+
end
|
208
|
+
|
209
|
+
# terminate producer threads
|
210
|
+
Thread.new { consumers.each { queue << nil } }
|
211
|
+
consumers.each { |t| t.join }
|
212
|
+
Thread.new { copiers.each { copy_queue << nil } }
|
213
|
+
copiers.each { |t| t.join }
|
214
|
+
bytes_sent = copiers.inject(0) { |sent,t| sent += t[:mog_sync_xfer] }
|
215
|
+
bytes_deleted = 0
|
216
|
+
|
217
|
+
# delete is single-threaded, it is not I/O-bound and
|
218
|
+
# we can pipeline in the future
|
219
|
+
if keep && exit_ok
|
220
|
+
queue = SizedQueue.new(8)
|
221
|
+
deleter = Thread.new do
|
222
|
+
dst = client_for(dst_spec, opts)
|
223
|
+
while key = queue.pop
|
224
|
+
begin
|
225
|
+
dst.delete(key) unless @dryrun
|
226
|
+
echo "D #{key}"
|
227
|
+
rescue MogileFS::Backend::UnknownKeyError
|
228
|
+
warn "#{key} disappeared before we could delete it"
|
229
|
+
end
|
230
|
+
end
|
231
|
+
end
|
232
|
+
main_dst = client_for(dst_spec, opts)
|
233
|
+
main_dst.each_file_info(prefix, after: after) do |info|
|
234
|
+
key = info["key"]
|
235
|
+
next if keep.include?(key)
|
236
|
+
queue << key
|
237
|
+
bytes_deleted += info["length"]
|
238
|
+
end
|
239
|
+
queue << nil # terminate
|
240
|
+
deleter.join
|
241
|
+
end
|
242
|
+
if @verbose
|
243
|
+
echo "wrote #{bytes_sent} bytes, removed #{bytes_deleted} bytes"
|
244
|
+
end
|
245
|
+
exit(exit_ok)
|