Class: RedSnapper

Inherits:
Object
  • Object
show all
Defined in:
lib/redsnapper.rb

Defined Under Namespace

Classes: Group

Constant Summary collapse

TARSNAP =
'tarsnap'
THREAD_POOL_DEFAULT_SIZE =
10
EXIT_ERROR =
"tarsnap: Error exit delayed from previous errors.\n"
@@output_mutex =
Mutex.new

Instance Method Summary collapse

Constructor Details

#initialize(archive, options = {}) ⇒ RedSnapper

Returns a new instance of RedSnapper.



27
28
29
30
31
32
# File 'lib/redsnapper.rb', line 27

def initialize(archive, options = {})
  @archive = archive
  @options = options
  @thread_pool = Thread.pool(options[:thread_pool_size] || THREAD_POOL_DEFAULT_SIZE)
  @error = false
end

Instance Method Details

#file_groupsObject



73
74
75
76
77
78
79
# File 'lib/redsnapper.rb', line 73

def file_groups
  groups = (1..@thread_pool.max).map { Group.new }
  file_sizes.sort { |a, b| b.last <=> a.last }.each do |file|
    groups.sort.last.add(*file)
  end
  groups.map(&:files)
end

#file_sizesObject



34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
# File 'lib/redsnapper.rb', line 34

def file_sizes
  return @sizes if @sizes
  
  command = [ TARSNAP, '-tvf', @archive, *@options[:tarsnap_options] ]
  command.push(@options[:directory]) if @options[:directory]

  @sizes = {}
  dirs = Set.new

  Open3.popen3(*command) do |_, out, _|
    out.gets(nil).split("\n").each do |entry|
      (_, _, _, _, size, _, _, _, name) = entry.split(/\s+/, 9)
      if name.end_with?('/')
        dirs.add(name)
      else
        @sizes[name] = size.to_i
      end
    end
  end

  empty_dirs = dirs.clone
  @sizes.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.each { |dir| @sizes[dir] = 0 }

  @sizes
end

#filesObject



69
70
71
# File 'lib/redsnapper.rb', line 69

def files
  @files ||= file_sizes.keys
end

#runObject



81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
# File 'lib/redsnapper.rb', line 81

def run
  file_groups.each do |chunk|
    @thread_pool.process do
      command = [ TARSNAP, '-xvf', @archive, *(@options[:tarsnap_options] + chunk) ]
      Open3.popen3(*command) do |_, _, err|
        while line = err.gets
          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