class RedSnapper
Constants
- EXIT_ERROR
- GLOB_CHARS
- NOT_OLDER_ERROR
- TARSNAP
- THREAD_POOL_DEFAULT_SIZE
Public Class Methods
new(archive, options = {})
click to toggle source
# File lib/redsnapper.rb, line 32 def initialize(archive, options = {}) @archive = archive @options = options @thread_pool = Thread.pool(options[:thread_pool_size] || THREAD_POOL_DEFAULT_SIZE) @error = false end
Public Instance Methods
empty_dirs(files, dirs)
click to toggle source
# File lib/redsnapper.rb, line 64 def empty_dirs(files, dirs) empty_dirs = dirs.clone files.each { |f| empty_dirs.delete(File.dirname(f) + '/') } dirs.each do |dir| components = dir.split('/')[0..-2] components.each_with_index do |_, i| empty_dirs.delete(components[0, i + 1].join('/') + '/') end end empty_dirs end
file_groups()
click to toggle source
# File lib/redsnapper.rb, line 84 def file_groups groups = (1..@thread_pool.max).map { Group.new } files_to_extract.sort { |a, b| b.last[:size] <=> a.last[:size] }.each do |name, props| # If the previous batch of files had an entry with the same size and date, # assume that this is a duplicate and assign it zero weight. There may be # some false positives here since the granularity of the data we have from # tarsnap is only "same day". However, a false positive just affects the # queing scheme, not which files get queued. size = (@options[:previous] && @options[:previous][name] == props) ? 0 : props[:size] groups.sort.last.add(name, size) end groups.map(&:files).reject(&:empty?) end
files()
click to toggle source
# File lib/redsnapper.rb, line 39 def files return @files if @files command = [ TARSNAP, '-tvf', @archive, *@options[:tarsnap_options] ] command.push(@options[:directory]) if @options[:directory] @files = {} Open3.popen3(*command) do |_, out, _| out.gets(nil).split("\n").each do |entry| (_, _, _, _, size, month, day, year_or_time, name) = entry.split(/\s+/, 9) date = DateTime.parse("#{month} #{day}, #{year_or_time}") date = date.prev_year if date < DateTime.now @files[name] = { :size => size.to_i, :date => date } end end @files end
files_to_extract()
click to toggle source
# File lib/redsnapper.rb, line 76 def files_to_extract files_to_extract, dirs = files.partition { |f| !f.first.end_with?('/') }.map(&:to_h) empty_dirs(files_to_extract.keys, dirs.keys).each do |dir| files_to_extract[dir] = { :size => 0 } end files_to_extract end
run()
click to toggle source
# File lib/redsnapper.rb, line 100 def run file_groups.each do |chunk| @thread_pool.process do chunk.map! { |file| file.gsub(/([#{Regexp.escape(GLOB_CHARS)}])/) { |m| "\\#{m}" } } command = [ TARSNAP, '-xvf', @archive, *(@options[:tarsnap_options] + chunk) ] Open3.popen3(*command) do |_, _, err| while line = err.gets next if line.end_with?(NOT_OLDER_ERROR) if line == EXIT_ERROR @error = true next end @@output_mutex.synchronize { warn line.chomp } end end end end @thread_pool.shutdown @@output_mutex.synchronize { warn EXIT_ERROR } if @error end