mogbak 0.1.2 → 0.2.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/CHANGES +10 -0
 - data/Gemfile.lock +31 -7
 - data/bin/mogbak +33 -2
 - data/lib/backup.rb +70 -55
 - data/lib/forkinator.rb +0 -7
 - data/lib/list.rb +2 -1
 - data/lib/log.rb +17 -0
 - data/lib/mogbak_version.rb +1 -1
 - data/lib/restore.rb +4 -2
 - data/lib/signal_handler.rb +19 -0
 - data/lib/validations.rb +1 -1
 - data/mogbak.gemspec +0 -3
 - metadata +5 -18
 
    
        data/CHANGES
    ADDED
    
    | 
         @@ -0,0 +1,10 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            2012-04-03: Release version 0.2.0
         
     | 
| 
      
 2 
     | 
    
         
            +
             
     | 
| 
      
 3 
     | 
    
         
            +
               * Graceful handling of SIGINT and SIGTERM have been added.  Upon receiving, mogbak will finish 
         
     | 
| 
      
 4 
     | 
    
         
            +
                 the files being transfered and will exit. (Jesse Angell <jesse.angell@firespring.com>)
         
     | 
| 
      
 5 
     | 
    
         
            +
                 
         
     | 
| 
      
 6 
     | 
    
         
            +
               * Backup now has an optional --non-stop switch.  This will cause mogbak to perform backup after backup 
         
     | 
| 
      
 7 
     | 
    
         
            +
                 without exiting until SIGTERM or SIGINT is received. (Jesse Angell <jesse.angell@firespring.com>)
         
     | 
| 
      
 8 
     | 
    
         
            +
             
     | 
| 
      
 9 
     | 
    
         
            +
               * --log-file option added to Restore, List, and Backup.  Optional parameter that will redirect
         
     | 
| 
      
 10 
     | 
    
         
            +
                 output to a log file.  Works well in conjunction with --non-stop. (Jesse Angell <jesse.angell@firespring.com>)
         
     | 
    
        data/Gemfile.lock
    CHANGED
    
    | 
         @@ -1,20 +1,44 @@ 
     | 
|
| 
       1 
1 
     | 
    
         
             
            PATH
         
     | 
| 
       2 
2 
     | 
    
         
             
              remote: .
         
     | 
| 
       3 
3 
     | 
    
         
             
              specs:
         
     | 
| 
       4 
     | 
    
         
            -
                mogbak (0. 
     | 
| 
      
 4 
     | 
    
         
            +
                mogbak (0.1.3)
         
     | 
| 
      
 5 
     | 
    
         
            +
                  activerecord-import (>= 0.2.9)
         
     | 
| 
      
 6 
     | 
    
         
            +
                  gli (>= 1.5.1)
         
     | 
| 
      
 7 
     | 
    
         
            +
                  mogilefs-client (>= 3.1.1)
         
     | 
| 
      
 8 
     | 
    
         
            +
                  mysql2 (>= 0.3.11)
         
     | 
| 
      
 9 
     | 
    
         
            +
                  sqlite3 (>= 1.3.5)
         
     | 
| 
       5 
10 
     | 
    
         | 
| 
       6 
11 
     | 
    
         
             
            GEM
         
     | 
| 
       7 
12 
     | 
    
         
             
              remote: http://rubygems.org/
         
     | 
| 
       8 
13 
     | 
    
         
             
              specs:
         
     | 
| 
       9 
     | 
    
         
            -
                 
     | 
| 
       10 
     | 
    
         
            -
             
     | 
| 
       11 
     | 
    
         
            -
             
     | 
| 
       12 
     | 
    
         
            -
             
     | 
| 
      
 14 
     | 
    
         
            +
                activemodel (3.2.2)
         
     | 
| 
      
 15 
     | 
    
         
            +
                  activesupport (= 3.2.2)
         
     | 
| 
      
 16 
     | 
    
         
            +
                  builder (~> 3.0.0)
         
     | 
| 
      
 17 
     | 
    
         
            +
                activerecord (3.2.2)
         
     | 
| 
      
 18 
     | 
    
         
            +
                  activemodel (= 3.2.2)
         
     | 
| 
      
 19 
     | 
    
         
            +
                  activesupport (= 3.2.2)
         
     | 
| 
      
 20 
     | 
    
         
            +
                  arel (~> 3.0.2)
         
     | 
| 
      
 21 
     | 
    
         
            +
                  tzinfo (~> 0.3.29)
         
     | 
| 
      
 22 
     | 
    
         
            +
                activerecord-import (0.2.9)
         
     | 
| 
      
 23 
     | 
    
         
            +
                  activerecord (~> 3.0)
         
     | 
| 
      
 24 
     | 
    
         
            +
                  activerecord (~> 3.0)
         
     | 
| 
      
 25 
     | 
    
         
            +
                activesupport (3.2.2)
         
     | 
| 
      
 26 
     | 
    
         
            +
                  i18n (~> 0.6)
         
     | 
| 
      
 27 
     | 
    
         
            +
                  multi_json (~> 1.0)
         
     | 
| 
      
 28 
     | 
    
         
            +
                arel (3.0.2)
         
     | 
| 
      
 29 
     | 
    
         
            +
                awesome_print (1.0.2)
         
     | 
| 
      
 30 
     | 
    
         
            +
                builder (3.0.0)
         
     | 
| 
      
 31 
     | 
    
         
            +
                gli (1.5.1)
         
     | 
| 
      
 32 
     | 
    
         
            +
                i18n (0.6.0)
         
     | 
| 
      
 33 
     | 
    
         
            +
                mogilefs-client (3.1.1)
         
     | 
| 
      
 34 
     | 
    
         
            +
                multi_json (1.2.0)
         
     | 
| 
      
 35 
     | 
    
         
            +
                mysql2 (0.3.11)
         
     | 
| 
      
 36 
     | 
    
         
            +
                sqlite3 (1.3.5)
         
     | 
| 
      
 37 
     | 
    
         
            +
                tzinfo (0.3.32)
         
     | 
| 
       13 
38 
     | 
    
         | 
| 
       14 
39 
     | 
    
         
             
            PLATFORMS
         
     | 
| 
       15 
40 
     | 
    
         
             
              ruby
         
     | 
| 
       16 
41 
     | 
    
         | 
| 
       17 
42 
     | 
    
         
             
            DEPENDENCIES
         
     | 
| 
      
 43 
     | 
    
         
            +
              awesome_print
         
     | 
| 
       18 
44 
     | 
    
         
             
              mogbak!
         
     | 
| 
       19 
     | 
    
         
            -
              rake
         
     | 
| 
       20 
     | 
    
         
            -
              rdoc
         
     | 
    
        data/bin/mogbak
    CHANGED
    
    | 
         @@ -26,6 +26,11 @@ require 'sqlite3' 
     | 
|
| 
       26 
26 
     | 
    
         
             
            require 'yaml'
         
     | 
| 
       27 
27 
     | 
    
         
             
            require 'forkinator'
         
     | 
| 
       28 
28 
     | 
    
         
             
            require 'path_helper'
         
     | 
| 
      
 29 
     | 
    
         
            +
            require 'signal_handler'
         
     | 
| 
      
 30 
     | 
    
         
            +
            require 'log'
         
     | 
| 
      
 31 
     | 
    
         
            +
             
     | 
| 
      
 32 
     | 
    
         
            +
            #activate signal handler
         
     | 
| 
      
 33 
     | 
    
         
            +
            SignalHandler.instance
         
     | 
| 
       29 
34 
     | 
    
         | 
| 
       30 
35 
     | 
    
         | 
| 
       31 
36 
     | 
    
         
             
            #Set master process name
         
     | 
| 
         @@ -102,15 +107,25 @@ command :backup do |c| 
     | 
|
| 
       102 
107 
     | 
    
         
             
              c.desc 'do not remove deleted files from the backup (faster)'
         
     | 
| 
       103 
108 
     | 
    
         
             
              c.switch ['no-delete']
         
     | 
| 
       104 
109 
     | 
    
         | 
| 
      
 110 
     | 
    
         
            +
              c.desc 'after backup is complete, start another backup. This causes mogbak to run non-stop.  Send SIGINT or SIGTERM to exit cleanly'
         
     | 
| 
      
 111 
     | 
    
         
            +
              c.switch ['non-stop']
         
     | 
| 
      
 112 
     | 
    
         
            +
             
     | 
| 
       105 
113 
     | 
    
         
             
              c.desc 'Number of worker processes'
         
     | 
| 
       106 
114 
     | 
    
         
             
              c.default_value 1
         
     | 
| 
       107 
115 
     | 
    
         
             
              c.flag :workers
         
     | 
| 
       108 
116 
     | 
    
         | 
| 
      
 117 
     | 
    
         
            +
              c.desc 'send backup output to log file'
         
     | 
| 
      
 118 
     | 
    
         
            +
              c.flag ['log-file']
         
     | 
| 
      
 119 
     | 
    
         
            +
             
     | 
| 
       109 
120 
     | 
    
         
             
              c.action do |global_options, options, args|
         
     | 
| 
       110 
121 
     | 
    
         
             
                raise '[backup_path] is required - see: mogbak help backup' unless args[0]
         
     | 
| 
       111 
122 
     | 
    
         
             
                $backup_path = args[0]
         
     | 
| 
      
 123 
     | 
    
         
            +
             
     | 
| 
      
 124 
     | 
    
         
            +
                #setup a logger for output
         
     | 
| 
      
 125 
     | 
    
         
            +
                Log.instance(options[:'log-file'])
         
     | 
| 
      
 126 
     | 
    
         
            +
             
     | 
| 
       112 
127 
     | 
    
         
             
                mog = Backup.new(:backup_path => args[0], :workers => options[:workers].to_i)
         
     | 
| 
       113 
     | 
    
         
            -
                mog.backup(:no_delete => options[:"no-delete"])
         
     | 
| 
      
 128 
     | 
    
         
            +
                mog.backup(:no_delete => options[:"no-delete"], :non_stop => options[:"non-stop"])
         
     | 
| 
       114 
129 
     | 
    
         
             
              end
         
     | 
| 
       115 
130 
     | 
    
         | 
| 
       116 
131 
     | 
    
         
             
            end
         
     | 
| 
         @@ -142,11 +157,17 @@ command :restore do |c| 
     | 
|
| 
       142 
157 
     | 
    
         
             
              c.desc 'restore a single file by dkey'
         
     | 
| 
       143 
158 
     | 
    
         
             
              c.flag :"single-file"
         
     | 
| 
       144 
159 
     | 
    
         | 
| 
      
 160 
     | 
    
         
            +
              c.desc 'send restore output to log file'
         
     | 
| 
      
 161 
     | 
    
         
            +
              c.flag ['log-file']
         
     | 
| 
      
 162 
     | 
    
         
            +
             
     | 
| 
       145 
163 
     | 
    
         
             
              c.action do |global_options, options, args|
         
     | 
| 
       146 
164 
     | 
    
         
             
                raise 'domain parameter is required - see: mogbak help restore' unless options[:domain]
         
     | 
| 
       147 
165 
     | 
    
         
             
                raise '[backup_path] is required - see: mogbak help restore' unless args[0]
         
     | 
| 
       148 
166 
     | 
    
         
             
                $backup_path = args[0]
         
     | 
| 
       149 
167 
     | 
    
         | 
| 
      
 168 
     | 
    
         
            +
                #setup a logger for output
         
     | 
| 
      
 169 
     | 
    
         
            +
                Log.instance(options[:'log-file'])
         
     | 
| 
      
 170 
     | 
    
         
            +
             
     | 
| 
       150 
171 
     | 
    
         
             
                restore = Restore.new(:tracker_ip => options[:trackerip],
         
     | 
| 
       151 
172 
     | 
    
         
             
                                      :tracker_port => options[:trackerport],
         
     | 
| 
       152 
173 
     | 
    
         
             
                                      :domain => options[:domain],
         
     | 
| 
         @@ -162,18 +183,28 @@ Output is: fid,dkey,length,classname 
     | 
|
| 
       162 
183 
     | 
    
         
             
            EOS
         
     | 
| 
       163 
184 
     | 
    
         
             
            arg_name '[backup_path]'
         
     | 
| 
       164 
185 
     | 
    
         
             
            command :list do |c|
         
     | 
| 
      
 186 
     | 
    
         
            +
             
     | 
| 
      
 187 
     | 
    
         
            +
              c.desc 'send list output to log file'
         
     | 
| 
      
 188 
     | 
    
         
            +
              c.flag ['log-file']
         
     | 
| 
      
 189 
     | 
    
         
            +
             
     | 
| 
       165 
190 
     | 
    
         
             
              c.action do |global_options, options, args|
         
     | 
| 
       166 
191 
     | 
    
         
             
                raise '[backup_path] is required - see: mogbak help list' unless args[0]
         
     | 
| 
       167 
192 
     | 
    
         
             
                $backup_path = args[0]
         
     | 
| 
       168 
193 
     | 
    
         | 
| 
      
 194 
     | 
    
         
            +
                #setup a logger for output
         
     | 
| 
      
 195 
     | 
    
         
            +
                Log.instance(options[:'log-file'])
         
     | 
| 
      
 196 
     | 
    
         
            +
             
     | 
| 
       169 
197 
     | 
    
         
             
                list = List.new(:backup_path => args[0])
         
     | 
| 
       170 
198 
     | 
    
         
             
                list.list
         
     | 
| 
       171 
199 
     | 
    
         
             
              end
         
     | 
| 
       172 
200 
     | 
    
         
             
            end
         
     | 
| 
       173 
201 
     | 
    
         | 
| 
       174 
     | 
    
         
            -
            # 
     | 
| 
      
 202 
     | 
    
         
            +
            #Handle --debug
         
     | 
| 
       175 
203 
     | 
    
         
             
            pre do |global_options, command, options, args|
         
     | 
| 
      
 204 
     | 
    
         
            +
             
     | 
| 
      
 205 
     | 
    
         
            +
              #This is used to enable some more verbose output
         
     | 
| 
       176 
206 
     | 
    
         
             
              $debug = global_options[:debug]
         
     | 
| 
      
 207 
     | 
    
         
            +
             
     | 
| 
       177 
208 
     | 
    
         
             
              true
         
     | 
| 
       178 
209 
     | 
    
         
             
            end
         
     | 
| 
       179 
210 
     | 
    
         | 
    
        data/lib/backup.rb
    CHANGED
    
    | 
         @@ -22,7 +22,7 @@ class Backup 
     | 
|
| 
       22 
22 
     | 
    
         | 
| 
       23 
23 
     | 
    
         | 
| 
       24 
24 
     | 
    
         
             
                #run validations and setup
         
     | 
| 
       25 
     | 
    
         
            -
                raise 
     | 
| 
      
 25 
     | 
    
         
            +
                raise unless check_backup_path
         
     | 
| 
       26 
26 
     | 
    
         
             
                create_sqlite_db
         
     | 
| 
       27 
27 
     | 
    
         
             
                connect_sqlite
         
     | 
| 
       28 
28 
     | 
    
         
             
                migrate_sqlite
         
     | 
| 
         @@ -43,9 +43,9 @@ class Backup 
     | 
|
| 
       43 
43 
     | 
    
         
             
              def bak_file(file)
         
     | 
| 
       44 
44 
     | 
    
         
             
                saved = file.bak_it
         
     | 
| 
       45 
45 
     | 
    
         
             
                if saved
         
     | 
| 
       46 
     | 
    
         
            -
             
     | 
| 
      
 46 
     | 
    
         
            +
                   Log.instance.info("Backed up: FID #{file.fid}")
         
     | 
| 
       47 
47 
     | 
    
         
             
                else
         
     | 
| 
       48 
     | 
    
         
            -
             
     | 
| 
      
 48 
     | 
    
         
            +
                   Log.instance.info("Error - will try again on next run: FID #{file.fid}")
         
     | 
| 
       49 
49 
     | 
    
         
             
                end
         
     | 
| 
       50 
50 
     | 
    
         | 
| 
       51 
51 
     | 
    
         
             
                return saved
         
     | 
| 
         @@ -72,11 +72,13 @@ class Backup 
     | 
|
| 
       72 
72 
     | 
    
         
             
                  SqliteActiveRecord.clear_active_connections!
         
     | 
| 
       73 
73 
     | 
    
         
             
                }
         
     | 
| 
       74 
74 
     | 
    
         | 
| 
       75 
     | 
    
         
            -
                #This proc receives an array of BakFiles,  proccesses them,  and returns a result array to the parent proc
         
     | 
| 
      
 75 
     | 
    
         
            +
                #This proc receives an array of BakFiles,  proccesses them,  and returns a result array to the parent proc. We will break
         
     | 
| 
      
 76 
     | 
    
         
            +
                #from the files if the signal handler says so.
         
     | 
| 
       76 
77 
     | 
    
         
             
                child = Proc.new { |files|
         
     | 
| 
       77 
78 
     | 
    
         
             
                  result = []
         
     | 
| 
       78 
79 
     | 
    
         
             
                  files.each do |file|
         
     | 
| 
       79 
80 
     | 
    
         
             
                    break if file.nil?
         
     | 
| 
      
 81 
     | 
    
         
            +
                    break if SignalHandler.instance.should_quit
         
     | 
| 
       80 
82 
     | 
    
         
             
                    saved = bak_file(file)
         
     | 
| 
       81 
83 
     | 
    
         
             
                    result << {:saved => saved, :file => file}
         
     | 
| 
       82 
84 
     | 
    
         
             
                  end
         
     | 
| 
         @@ -91,16 +93,18 @@ class Backup 
     | 
|
| 
       91 
93 
     | 
    
         
             
              #param [Array] files must be an array of BakFiles that need to be deleted
         
     | 
| 
       92 
94 
     | 
    
         
             
              def launch_delete_workers(fids)
         
     | 
| 
       93 
95 
     | 
    
         | 
| 
       94 
     | 
    
         
            -
                #This proc receives an array of BakFiles, handles them,  and spits them back to the parent 
     | 
| 
      
 96 
     | 
    
         
            +
                #This proc receives an array of BakFiles, handles them,  and spits them back to the parent, break from the fids if
         
     | 
| 
      
 97 
     | 
    
         
            +
                #the signal handler says so.
         
     | 
| 
       95 
98 
     | 
    
         
             
                child = Proc.new { |fids|
         
     | 
| 
       96 
99 
     | 
    
         
             
                  result = []
         
     | 
| 
       97 
100 
     | 
    
         
             
                  fids.each do |fid|
         
     | 
| 
       98 
101 
     | 
    
         
             
                    break if fid.nil?
         
     | 
| 
      
 102 
     | 
    
         
            +
                    break if SignalHandler.instance.should_quit
         
     | 
| 
       99 
103 
     | 
    
         
             
                    deleted = BakFile.delete_from_fs(fid)
         
     | 
| 
       100 
104 
     | 
    
         
             
                    if deleted
         
     | 
| 
       101 
     | 
    
         
            -
                       
     | 
| 
      
 105 
     | 
    
         
            +
                      Log.instance.info("Deleting from backup: FID #{fid}")
         
     | 
| 
       102 
106 
     | 
    
         
             
                    else
         
     | 
| 
       103 
     | 
    
         
            -
                       
     | 
| 
      
 107 
     | 
    
         
            +
                      Log.instance.info("Failed to delete from backup: FID #{fid}")
         
     | 
| 
       104 
108 
     | 
    
         
             
                    end
         
     | 
| 
       105 
109 
     | 
    
         | 
| 
       106 
110 
     | 
    
         
             
                    result << fid
         
     | 
| 
         @@ -133,64 +137,75 @@ class Backup 
     | 
|
| 
       133 
137 
     | 
    
         
             
              #@param [Hash] o if :no_delete then don't remove deleted files from the backup (intensive process)
         
     | 
| 
       134 
138 
     | 
    
         
             
              def backup(o = {})
         
     | 
| 
       135 
139 
     | 
    
         | 
| 
       136 
     | 
    
         
            -
                 
     | 
| 
       137 
     | 
    
         
            -
                 
     | 
| 
       138 
     | 
    
         
            -
             
     | 
| 
       139 
     | 
    
         
            -
                  files  
     | 
| 
       140 
     | 
    
         
            -
             
     | 
| 
      
 140 
     | 
    
         
            +
                #Loop over the main backup logic.  We'll break out at the end unless o[:non_stop] is set
         
     | 
| 
      
 141 
     | 
    
         
            +
                loop do
         
     | 
| 
      
 142 
     | 
    
         
            +
                  files = []
         
     | 
| 
      
 143 
     | 
    
         
            +
                  #first we retry files that we haven't been able to backup successfully, if any.
         
     | 
| 
      
 144 
     | 
    
         
            +
                  BakFile.find_each(:conditions => ['saved = ?', false]) do |bak_file|
         
     | 
| 
      
 145 
     | 
    
         
            +
                    files << bak_file
         
     | 
| 
      
 146 
     | 
    
         
            +
                  end
         
     | 
| 
       141 
147 
     | 
    
         | 
| 
       142 
     | 
    
         
            -
             
     | 
| 
      
 148 
     | 
    
         
            +
                  launch_backup_workers(files)
         
     | 
| 
       143 
149 
     | 
    
         | 
| 
       144 
     | 
    
         
            -
             
     | 
| 
       145 
     | 
    
         
            -
             
     | 
| 
       146 
     | 
    
         
            -
             
     | 
| 
       147 
     | 
    
         
            -
             
     | 
| 
      
 150 
     | 
    
         
            +
                  #now back up any new files.  if they fail to be backed up we'll retry them the next time the backup
         
     | 
| 
      
 151 
     | 
    
         
            +
                  #command is ran.
         
     | 
| 
      
 152 
     | 
    
         
            +
                  dmid = Domain.find_by_namespace(self.domain)
         
     | 
| 
      
 153 
     | 
    
         
            +
                  results = Fid.find_in_batches(:conditions => ['dmid = ? AND fid > ?', dmid, BakFile.max_fid], :batch_size => 500 * self.workers.to_i, :include => [:domain, :fileclass]) do |batch|
         
     | 
| 
      
 154 
     | 
    
         
            +
             
     | 
| 
      
 155 
     | 
    
         
            +
                    #Insert all the files into our bak db with :saved false so that we don't think we backed up something that crashed
         
     | 
| 
      
 156 
     | 
    
         
            +
                    files = []
         
     | 
| 
      
 157 
     | 
    
         
            +
                    batch.each do |file|
         
     | 
| 
      
 158 
     | 
    
         
            +
                      files << BakFile.new(:fid => file.fid,
         
     | 
| 
      
 159 
     | 
    
         
            +
                                           :domain => file.domain.namespace,
         
     | 
| 
      
 160 
     | 
    
         
            +
                                           :dkey => file.dkey,
         
     | 
| 
      
 161 
     | 
    
         
            +
                                           :length => file.length,
         
     | 
| 
      
 162 
     | 
    
         
            +
                                           :classname => file.classname,
         
     | 
| 
      
 163 
     | 
    
         
            +
                                           :saved => false)
         
     | 
| 
      
 164 
     | 
    
         
            +
                    end
         
     | 
| 
       148 
165 
     | 
    
         | 
| 
       149 
     | 
    
         
            -
             
     | 
| 
       150 
     | 
    
         
            -
             
     | 
| 
       151 
     | 
    
         
            -
             
     | 
| 
       152 
     | 
    
         
            -
             
     | 
| 
       153 
     | 
    
         
            -
             
     | 
| 
       154 
     | 
    
         
            -
                                         :dkey => file.dkey,
         
     | 
| 
       155 
     | 
    
         
            -
                                         :length => file.length,
         
     | 
| 
       156 
     | 
    
         
            -
                                         :classname => file.classname,
         
     | 
| 
       157 
     | 
    
         
            -
                                         :saved => false)
         
     | 
| 
       158 
     | 
    
         
            -
                  end
         
     | 
| 
      
 166 
     | 
    
         
            +
                    #There is no way to do a bulk insert in sqlite so this generates a lot of inserts.  wrapping all of the inserts
         
     | 
| 
      
 167 
     | 
    
         
            +
                    #inside a single transaction makes it much much faster.
         
     | 
| 
      
 168 
     | 
    
         
            +
                    BakFile.transaction do
         
     | 
| 
      
 169 
     | 
    
         
            +
                      BakFile.import files, :validate => false
         
     | 
| 
      
 170 
     | 
    
         
            +
                    end
         
     | 
| 
       159 
171 
     | 
    
         | 
| 
       160 
     | 
    
         
            -
             
     | 
| 
       161 
     | 
    
         
            -
             
     | 
| 
       162 
     | 
    
         
            -
             
     | 
| 
       163 
     | 
    
         
            -
                     
     | 
| 
      
 172 
     | 
    
         
            +
                    #Fire up the workers now that we have work for them to do
         
     | 
| 
      
 173 
     | 
    
         
            +
                    launch_backup_workers(files)
         
     | 
| 
      
 174 
     | 
    
         
            +
             
     | 
| 
      
 175 
     | 
    
         
            +
                    #Terminate program if the signal handler says so and this is a clean place to do it
         
     | 
| 
      
 176 
     | 
    
         
            +
                    return true if SignalHandler.instance.should_quit
         
     | 
| 
       164 
177 
     | 
    
         
             
                  end
         
     | 
| 
       165 
178 
     | 
    
         | 
| 
       166 
     | 
    
         
            -
                  # 
     | 
| 
       167 
     | 
    
         
            -
                   
     | 
| 
      
 179 
     | 
    
         
            +
                  #Delete files from the backup that no longer exist in the mogilefs domain.  Unfortunently there is no easy way to detect
         
     | 
| 
      
 180 
     | 
    
         
            +
                  #which files have been deleted from the MogileFS domain.  Our only option is to brute force our way through.  This is a bulk
         
     | 
| 
      
 181 
     | 
    
         
            +
                  #query that checks a thousand files in each query against the MogileFS database server.  The query is kind of tricky because
         
     | 
| 
      
 182 
     | 
    
         
            +
                  #I wanted to do this with nothing but SELECT privileges which meant I couldn't create a temporary table (which would require,
         
     | 
| 
      
 183 
     | 
    
         
            +
                  #create temporary table and insert privleges).  You might want to only run this operation every once and awhile if you have a
         
     | 
| 
      
 184 
     | 
    
         
            +
                  #very large domain.  In my testing,  it is able to get through domains with millions of files in a matter of a second.  So
         
     | 
| 
      
 185 
     | 
    
         
            +
                  #all in all it's not so bad
         
     | 
| 
      
 186 
     | 
    
         
            +
                  if !o[:no_delete]
         
     | 
| 
       168 
187 
     | 
    
         | 
| 
       169 
     | 
    
         
            -
             
     | 
| 
      
 188 
     | 
    
         
            +
                    BakFile.find_in_batches { |bak_files|
         
     | 
| 
      
 189 
     | 
    
         
            +
                      union = "SELECT #{bak_files.first.fid} as fid"
         
     | 
| 
      
 190 
     | 
    
         
            +
                      bak_files.shift
         
     | 
| 
      
 191 
     | 
    
         
            +
                      bak_files.each do |bakfile|
         
     | 
| 
      
 192 
     | 
    
         
            +
                        union = "#{union} UNION SELECT #{bakfile.fid}"
         
     | 
| 
      
 193 
     | 
    
         
            +
                      end
         
     | 
| 
      
 194 
     | 
    
         
            +
                      connection = ActiveRecord::Base.connection
         
     | 
| 
      
 195 
     | 
    
         
            +
                      files = connection.select_values("SELECT t1.fid FROM (#{union}) as t1 LEFT JOIN file on t1.fid = file.fid WHERE file.fid IS NULL")
         
     | 
| 
      
 196 
     | 
    
         
            +
                      launch_delete_workers(files)
         
     | 
| 
       170 
197 
     | 
    
         | 
| 
       171 
     | 
    
         
            -
             
     | 
| 
       172 
     | 
    
         
            -
             
     | 
| 
       173 
     | 
    
         
            -
             
     | 
| 
       174 
     | 
    
         
            -
                #I wanted to do this with nothing but SELECT privileges which meant I couldn't create a temporary table (which would require,
         
     | 
| 
       175 
     | 
    
         
            -
                #create temporary table and insert privleges).  You might want to only run this operation every once and awhile if you have a
         
     | 
| 
       176 
     | 
    
         
            -
                #very large domain.  In my testing,  it is able to get through domains with millions of files in a matter of a second.  So
         
     | 
| 
       177 
     | 
    
         
            -
                #all in all it's not so bad
         
     | 
| 
       178 
     | 
    
         
            -
                if !o[:no_delete]
         
     | 
| 
       179 
     | 
    
         
            -
                  files_to_delete = Array.new
         
     | 
| 
       180 
     | 
    
         
            -
                  BakFile.find_in_batches { |bak_files|
         
     | 
| 
       181 
     | 
    
         
            -
             
     | 
| 
       182 
     | 
    
         
            -
                    union = "SELECT #{bak_files.first.fid} as fid"
         
     | 
| 
       183 
     | 
    
         
            -
                    bak_files.shift
         
     | 
| 
       184 
     | 
    
         
            -
                    bak_files.each do |bakfile|
         
     | 
| 
       185 
     | 
    
         
            -
                      union = "#{union} UNION SELECT #{bakfile.fid}"
         
     | 
| 
       186 
     | 
    
         
            -
                    end
         
     | 
| 
       187 
     | 
    
         
            -
                    connection = ActiveRecord::Base.connection
         
     | 
| 
       188 
     | 
    
         
            -
                    files = connection.select_values("SELECT t1.fid FROM (#{union}) as t1 LEFT JOIN file on t1.fid = file.fid WHERE file.fid IS NULL")
         
     | 
| 
       189 
     | 
    
         
            -
                    files_to_delete += files
         
     | 
| 
       190 
     | 
    
         
            -
                  }
         
     | 
| 
      
 198 
     | 
    
         
            +
                      #Terminate program if the signal handler says so and this is a clean place to do it
         
     | 
| 
      
 199 
     | 
    
         
            +
                      return true if SignalHandler.instance.should_quit
         
     | 
| 
      
 200 
     | 
    
         
            +
                    }
         
     | 
| 
       191 
201 
     | 
    
         | 
| 
       192 
     | 
    
         
            -
                  launch_delete_workers(files_to_delete)
         
     | 
| 
       193 
202 
     | 
    
         | 
| 
      
 203 
     | 
    
         
            +
             
     | 
| 
      
 204 
     | 
    
         
            +
                  end
         
     | 
| 
      
 205 
     | 
    
         
            +
             
     | 
| 
      
 206 
     | 
    
         
            +
                  #Break out of infinite loop unless o[:non_stop] is set
         
     | 
| 
      
 207 
     | 
    
         
            +
                  break unless o[:non_stop]
         
     | 
| 
      
 208 
     | 
    
         
            +
                  sleep 1
         
     | 
| 
       194 
209 
     | 
    
         
             
                end
         
     | 
| 
       195 
210 
     | 
    
         | 
| 
       196 
211 
     | 
    
         
             
              end
         
     | 
    
        data/lib/forkinator.rb
    CHANGED
    
    | 
         @@ -88,13 +88,6 @@ class Forkinator 
     | 
|
| 
       88 
88 
     | 
    
         
             
                children = []
         
     | 
| 
       89 
89 
     | 
    
         
             
                qty.times { children << make_child(child_proc)}
         
     | 
| 
       90 
90 
     | 
    
         | 
| 
       91 
     | 
    
         
            -
                #register signal handler so that children kill if program receives a SIGINT
         
     | 
| 
       92 
     | 
    
         
            -
                #which will happen if the user ctrl c's the parent process
         
     | 
| 
       93 
     | 
    
         
            -
                Signal.trap :SIGINT do
         
     | 
| 
       94 
     | 
    
         
            -
                  children.each { |child| Process.kill(:KILL, child[:pid]) if child[:pid]}
         
     | 
| 
       95 
     | 
    
         
            -
                  exit 1
         
     | 
| 
       96 
     | 
    
         
            -
                end
         
     | 
| 
       97 
     | 
    
         
            -
             
     | 
| 
       98 
91 
     | 
    
         
             
                #For each worker
         
     | 
| 
       99 
92 
     | 
    
         
             
                qty.times do |i|
         
     | 
| 
       100 
93 
     | 
    
         | 
    
        data/lib/list.rb
    CHANGED
    
    | 
         @@ -22,7 +22,8 @@ class List 
     | 
|
| 
       22 
22 
     | 
    
         
             
              #fid,key,length,class
         
     | 
| 
       23 
23 
     | 
    
         
             
              def list
         
     | 
| 
       24 
24 
     | 
    
         
             
                files = BakFile.find_each(:conditions => ['saved = ?', true]) do |file|
         
     | 
| 
       25 
     | 
    
         
            -
                   
     | 
| 
      
 25 
     | 
    
         
            +
                  Log.instance.info("#{file.fid},#{file.dkey},#{file.length},#{file.classname}")
         
     | 
| 
      
 26 
     | 
    
         
            +
                  break if SignalHandler.instance.should_quit
         
     | 
| 
       26 
27 
     | 
    
         
             
                end
         
     | 
| 
       27 
28 
     | 
    
         
             
              end
         
     | 
| 
       28 
29 
     | 
    
         
             
            end
         
     | 
    
        data/lib/log.rb
    ADDED
    
    | 
         @@ -0,0 +1,17 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            require 'logger'
         
     | 
| 
      
 2 
     | 
    
         
            +
            #Create a Logger that is a singleton provided by the instance method
         
     | 
| 
      
 3 
     | 
    
         
            +
            class Log
         
     | 
| 
      
 4 
     | 
    
         
            +
              def self.instance(log_file = nil)
         
     | 
| 
      
 5 
     | 
    
         
            +
                @@instance ||= create_logger(log_file)
         
     | 
| 
      
 6 
     | 
    
         
            +
              end
         
     | 
| 
      
 7 
     | 
    
         
            +
             
     | 
| 
      
 8 
     | 
    
         
            +
              def self.create_logger(log_file)
         
     | 
| 
      
 9 
     | 
    
         
            +
                log_file = STDOUT if log_file == nil
         
     | 
| 
      
 10 
     | 
    
         
            +
                logger = Logger.new(log_file)
         
     | 
| 
      
 11 
     | 
    
         
            +
                logger.datetime_format = "%Y-%m-%d %H:%M:%S"
         
     | 
| 
      
 12 
     | 
    
         
            +
                logger.formatter = proc do |severity, datetime, progname, msg|
         
     | 
| 
      
 13 
     | 
    
         
            +
                  "#{datetime}: #{msg}\n"
         
     | 
| 
      
 14 
     | 
    
         
            +
                end
         
     | 
| 
      
 15 
     | 
    
         
            +
                logger
         
     | 
| 
      
 16 
     | 
    
         
            +
              end
         
     | 
| 
      
 17 
     | 
    
         
            +
            end
         
     | 
    
        data/lib/mogbak_version.rb
    CHANGED
    
    
    
        data/lib/restore.rb
    CHANGED
    
    | 
         @@ -28,9 +28,9 @@ class Restore 
     | 
|
| 
       28 
28 
     | 
    
         | 
| 
       29 
29 
     | 
    
         
             
              def output_save(save, fid)
         
     | 
| 
       30 
30 
     | 
    
         
             
                if save
         
     | 
| 
       31 
     | 
    
         
            -
                   
     | 
| 
      
 31 
     | 
    
         
            +
                  Log.instance.info("Restored: FID #{fid}")
         
     | 
| 
       32 
32 
     | 
    
         
             
                else
         
     | 
| 
       33 
     | 
    
         
            -
                   
     | 
| 
      
 33 
     | 
    
         
            +
                  Log.instance.info("Error: FID #{fid}")
         
     | 
| 
       34 
34 
     | 
    
         
             
                end
         
     | 
| 
       35 
35 
     | 
    
         
             
              end
         
     | 
| 
       36 
36 
     | 
    
         | 
| 
         @@ -39,6 +39,7 @@ class Restore 
     | 
|
| 
       39 
39 
     | 
    
         
             
                  results = []
         
     | 
| 
       40 
40 
     | 
    
         
             
                  files.each do |file|
         
     | 
| 
       41 
41 
     | 
    
         
             
                    break if file.nil?
         
     | 
| 
      
 42 
     | 
    
         
            +
                    break if SignalHandler.instance.should_quit
         
     | 
| 
       42 
43 
     | 
    
         
             
                    save = file.restore
         
     | 
| 
       43 
44 
     | 
    
         
             
                    output_save(save, file.fid)
         
     | 
| 
       44 
45 
     | 
    
         
             
                    results << {:restored => save, :fid => file.fid}
         
     | 
| 
         @@ -64,6 +65,7 @@ class Restore 
     | 
|
| 
       64 
65 
     | 
    
         | 
| 
       65 
66 
     | 
    
         
             
                  BakFile.find_in_batches(:conditions => ['saved = ?', true], :batch_size => 2000) do |batch|
         
     | 
| 
       66 
67 
     | 
    
         
             
                    launch_restore_workers(batch)
         
     | 
| 
      
 68 
     | 
    
         
            +
                    break if SignalHandler.instance.should_quit
         
     | 
| 
       67 
69 
     | 
    
         
             
                  end
         
     | 
| 
       68 
70 
     | 
    
         | 
| 
       69 
71 
     | 
    
         
             
                end
         
     | 
| 
         @@ -0,0 +1,19 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            require 'singleton'
         
     | 
| 
      
 2 
     | 
    
         
            +
             
     | 
| 
      
 3 
     | 
    
         
            +
            #Singleton class used to intercept signals.  If a SIGINT or SIGTERM is received a message is outputted and @should_quit
         
     | 
| 
      
 4 
     | 
    
         
            +
            #is set to true
         
     | 
| 
      
 5 
     | 
    
         
            +
            class SignalHandler
         
     | 
| 
      
 6 
     | 
    
         
            +
              include Singleton
         
     | 
| 
      
 7 
     | 
    
         
            +
              attr_reader :should_quit
         
     | 
| 
      
 8 
     | 
    
         
            +
             
     | 
| 
      
 9 
     | 
    
         
            +
              def handle_signal
         
     | 
| 
      
 10 
     | 
    
         
            +
                puts "PID #{Process.pid} is gracefully shutting down..."
         
     | 
| 
      
 11 
     | 
    
         
            +
                @should_quit = true
         
     | 
| 
      
 12 
     | 
    
         
            +
              end
         
     | 
| 
      
 13 
     | 
    
         
            +
             
     | 
| 
      
 14 
     | 
    
         
            +
              def initialize
         
     | 
| 
      
 15 
     | 
    
         
            +
                @should_quit = false
         
     | 
| 
      
 16 
     | 
    
         
            +
                Signal.trap("SIGINT") { handle_signal }
         
     | 
| 
      
 17 
     | 
    
         
            +
                Signal.trap("SIGTERM") { handle_signal }
         
     | 
| 
      
 18 
     | 
    
         
            +
              end
         
     | 
| 
      
 19 
     | 
    
         
            +
            end
         
     | 
    
        data/lib/validations.rb
    CHANGED
    
    | 
         @@ -45,7 +45,7 @@ module Validations 
     | 
|
| 
       45 
45 
     | 
    
         
             
              #@return [Bool]
         
     | 
| 
       46 
46 
     | 
    
         
             
              def connect_sqlite(raise_msg = nil)
         
     | 
| 
       47 
47 
     | 
    
         
             
                begin
         
     | 
| 
       48 
     | 
    
         
            -
                  ActiveRecord::Base.establish_connection(:adapter => 'sqlite3', :database => "#{$backup_path}/db.sqlite", :timeout =>  
     | 
| 
      
 48 
     | 
    
         
            +
                  ActiveRecord::Base.establish_connection(:adapter => 'sqlite3', :database => "#{$backup_path}/db.sqlite", :timeout => 10000)
         
     | 
| 
       49 
49 
     | 
    
         
             
                rescue Exception => e
         
     | 
| 
       50 
50 
     | 
    
         
             
                  raise raise_msg if raise_msg
         
     | 
| 
       51 
51 
     | 
    
         
             
                  raise e if $debug
         
     | 
    
        data/mogbak.gemspec
    CHANGED
    
    | 
         @@ -19,9 +19,6 @@ spec = Gem::Specification.new do |s| 
     | 
|
| 
       19 
19 
     | 
    
         
             
              s.add_runtime_dependency('gli', '>= 1.5.1')
         
     | 
| 
       20 
20 
     | 
    
         
             
              s.add_runtime_dependency('mysql2', '>= 0.3.11')
         
     | 
| 
       21 
21 
     | 
    
         
             
              s.add_runtime_dependency('mogilefs-client','>= 3.1.1')
         
     | 
| 
       22 
     | 
    
         
            -
              s.add_runtime_dependency('json','>= 1.6.5')
         
     | 
| 
       23 
22 
     | 
    
         
             
              s.add_runtime_dependency('sqlite3','>=1.3.5')
         
     | 
| 
       24 
23 
     | 
    
         
             
              s.add_runtime_dependency('activerecord-import','>=0.2.9')
         
     | 
| 
       25 
     | 
    
         
            -
             
     | 
| 
       26 
     | 
    
         
            -
             
     | 
| 
       27 
24 
     | 
    
         
             
            end
         
     | 
    
        metadata
    CHANGED
    
    | 
         @@ -1,7 +1,7 @@ 
     | 
|
| 
       1 
1 
     | 
    
         
             
            --- !ruby/object:Gem::Specification
         
     | 
| 
       2 
2 
     | 
    
         
             
            name: mogbak
         
     | 
| 
       3 
3 
     | 
    
         
             
            version: !ruby/object:Gem::Version
         
     | 
| 
       4 
     | 
    
         
            -
              version: 0. 
     | 
| 
      
 4 
     | 
    
         
            +
              version: 0.2.0
         
     | 
| 
       5 
5 
     | 
    
         
             
              prerelease: 
         
     | 
| 
       6 
6 
     | 
    
         
             
            platform: ruby
         
     | 
| 
       7 
7 
     | 
    
         
             
            authors:
         
     | 
| 
         @@ -9,7 +9,7 @@ authors: 
     | 
|
| 
       9 
9 
     | 
    
         
             
            autorequire: 
         
     | 
| 
       10 
10 
     | 
    
         
             
            bindir: bin
         
     | 
| 
       11 
11 
     | 
    
         
             
            cert_chain: []
         
     | 
| 
       12 
     | 
    
         
            -
            date: 2012-03 
     | 
| 
      
 12 
     | 
    
         
            +
            date: 2012-04-03 00:00:00.000000000 Z
         
     | 
| 
       13 
13 
     | 
    
         
             
            dependencies:
         
     | 
| 
       14 
14 
     | 
    
         
             
            - !ruby/object:Gem::Dependency
         
     | 
| 
       15 
15 
     | 
    
         
             
              name: awesome_print
         
     | 
| 
         @@ -75,22 +75,6 @@ dependencies: 
     | 
|
| 
       75 
75 
     | 
    
         
             
                - - ! '>='
         
     | 
| 
       76 
76 
     | 
    
         
             
                  - !ruby/object:Gem::Version
         
     | 
| 
       77 
77 
     | 
    
         
             
                    version: 3.1.1
         
     | 
| 
       78 
     | 
    
         
            -
            - !ruby/object:Gem::Dependency
         
     | 
| 
       79 
     | 
    
         
            -
              name: json
         
     | 
| 
       80 
     | 
    
         
            -
              requirement: !ruby/object:Gem::Requirement
         
     | 
| 
       81 
     | 
    
         
            -
                none: false
         
     | 
| 
       82 
     | 
    
         
            -
                requirements:
         
     | 
| 
       83 
     | 
    
         
            -
                - - ! '>='
         
     | 
| 
       84 
     | 
    
         
            -
                  - !ruby/object:Gem::Version
         
     | 
| 
       85 
     | 
    
         
            -
                    version: 1.6.5
         
     | 
| 
       86 
     | 
    
         
            -
              type: :runtime
         
     | 
| 
       87 
     | 
    
         
            -
              prerelease: false
         
     | 
| 
       88 
     | 
    
         
            -
              version_requirements: !ruby/object:Gem::Requirement
         
     | 
| 
       89 
     | 
    
         
            -
                none: false
         
     | 
| 
       90 
     | 
    
         
            -
                requirements:
         
     | 
| 
       91 
     | 
    
         
            -
                - - ! '>='
         
     | 
| 
       92 
     | 
    
         
            -
                  - !ruby/object:Gem::Version
         
     | 
| 
       93 
     | 
    
         
            -
                    version: 1.6.5
         
     | 
| 
       94 
78 
     | 
    
         
             
            - !ruby/object:Gem::Dependency
         
     | 
| 
       95 
79 
     | 
    
         
             
              name: sqlite3
         
     | 
| 
       96 
80 
     | 
    
         
             
              requirement: !ruby/object:Gem::Requirement
         
     | 
| 
         @@ -131,6 +115,7 @@ extensions: [] 
     | 
|
| 
       131 
115 
     | 
    
         
             
            extra_rdoc_files: []
         
     | 
| 
       132 
116 
     | 
    
         
             
            files:
         
     | 
| 
       133 
117 
     | 
    
         
             
            - .gitignore
         
     | 
| 
      
 118 
     | 
    
         
            +
            - CHANGES
         
     | 
| 
       134 
119 
     | 
    
         
             
            - Gemfile
         
     | 
| 
       135 
120 
     | 
    
         
             
            - Gemfile.lock
         
     | 
| 
       136 
121 
     | 
    
         
             
            - LICENSE
         
     | 
| 
         @@ -145,10 +130,12 @@ files: 
     | 
|
| 
       145 
130 
     | 
    
         
             
            - lib/fileclass.rb
         
     | 
| 
       146 
131 
     | 
    
         
             
            - lib/forkinator.rb
         
     | 
| 
       147 
132 
     | 
    
         
             
            - lib/list.rb
         
     | 
| 
      
 133 
     | 
    
         
            +
            - lib/log.rb
         
     | 
| 
       148 
134 
     | 
    
         
             
            - lib/mogbak_version.rb
         
     | 
| 
       149 
135 
     | 
    
         
             
            - lib/monkey_patch.rb
         
     | 
| 
       150 
136 
     | 
    
         
             
            - lib/path_helper.rb
         
     | 
| 
       151 
137 
     | 
    
         
             
            - lib/restore.rb
         
     | 
| 
      
 138 
     | 
    
         
            +
            - lib/signal_handler.rb
         
     | 
| 
       152 
139 
     | 
    
         
             
            - lib/validations.rb
         
     | 
| 
       153 
140 
     | 
    
         
             
            - mogbak.gemspec
         
     | 
| 
       154 
141 
     | 
    
         
             
            homepage: http://www.github.com/firespring/mogbak
         
     |