Beispiel #1
0
 private static function _step_send_pending_db_snapshots($startAt = 0)
 {
     // Load state into self::$_state & fileoptions object into self::$_stateObj.
     if (false === self::_load_state()) {
         return false;
     }
     if (false === self::_load_tables()) {
         return false;
     }
     if (0 != $startAt) {
         pb_backupbuddy::status('details', 'Resuming snapshot send at point `' . $startAt . '`.');
     }
     require_once pb_backupbuddy::plugin_path() . '/destinations/bootstrap.php';
     backupbuddy_live::update_db_live_activity_time();
     // On first pass create and send backupbuddy_dat.php and importbuddy.php.
     if (0 == $startAt) {
         // Render backupbuddy_dat.php
         $dat_file = backupbuddy_live::getLiveDatabaseSnapshotDir() . 'backupbuddy_dat.php';
         // Make sure directory exists.
         if (!file_exists(dirname($dat_file))) {
             if (false === pb_backupbuddy_filesystem::mkdir(dirname($dat_file))) {
                 pb_backupbuddy::status('warning', 'Warning #34893498434: Unable to mkdir("' . $dat_file . '").');
             }
         }
         $tableSizes = array();
         foreach (self::$_tables as $tableName => $table) {
             $tableSizes[$tableName] = $table['s'];
         }
         $table_results = backupbuddy_live::_calculate_table_includes_excludes_basedump();
         $dat_settings = array('backup_type' => 'live', 'profile' => array(), 'serial' => '', 'breakout_tables' => backupbuddy_live::calculateTables(), 'table_sizes' => $tableSizes, 'force_single_db_file' => false, 'trigger' => 'live', 'db_excludes' => $table_results[1], 'db_includes' => $table_results[0]);
         pb_backupbuddy::status('details', 'Rendering DAT file to `' . $dat_file . '`.');
         if (!is_array(backupbuddy_core::render_dat_contents($dat_settings, $dat_file))) {
             $error = 'Error #47949743: Since DAT file could not be written aborting. Check permissions writing to `' . $dat_file . '`.';
             pb_backupbuddy::status('error', $error);
             return $error;
         }
         // Render importbuddy.php
         $importbuddy_file = backupbuddy_live::getLiveDatabaseSnapshotDir() . 'importbuddy.php';
         pb_backupbuddy::status('details', 'Rendering importbuddy file to `' . $importbuddy_file . '`.');
         if (false === backupbuddy_core::importbuddy($importbuddy_file, $pass = NULL)) {
             // NULL pass leaves #PASSWORD# placeholder in place.
             pb_backupbuddy::status('warning', 'Warning #348438345: Unable to render importbuddy. Not backing up importbuddy.php.');
         }
         // Load destination settings.
         $destination_settings = self::get_destination_settings();
         // Send DAT file.
         $send_id = 'live_' . md5($dat_file) . '-' . pb_backupbuddy::random_string(6);
         $destination_settings['_database_table'] = 'backupbuddy_dat.php';
         if (false === pb_backupbuddy_destinations::send($destination_settings, $dat_file, $send_id, $delete_after = true, $isRetry = false, $trigger = 'live_periodic', $destination_id = backupbuddy_live::getLiveID())) {
             $error = 'Error #389398: Unable to send DAT file to Live servers. See error log above for details.';
             pb_backupbuddy::status('error', $error);
             backupbuddy_core::addNotification('live_error', 'BackupBuddy Stash Live Error', $error);
         }
         // Send importbuddy.
         $send_id = 'live_' . md5($importbuddy_file) . '-' . pb_backupbuddy::random_string(6);
         $destination_settings['_database_table'] = 'importbuddy.php';
         if (false === pb_backupbuddy_destinations::send($destination_settings, $importbuddy_file, $send_id, $delete_after = true, $isRetry = false, $trigger = 'live_periodic', $destination_id = backupbuddy_live::getLiveID())) {
             pb_backupbuddy::status('error', 'Error #329327: Unable to send importbuddy file to Live servers. See error log above for details.');
         }
     }
     // Loop through files in the catalog.
     $loopCount = 0;
     $checkCount = 0;
     $sendTimeSum = 0;
     $sendSizeSum = 0;
     $sendsStarted = 0;
     $sendsSucceeded = 0;
     $sendsMultiparted = 0;
     $sendsFailed = 0;
     $alreadyBackedUp = 0;
     $tooManySendFails = 0;
     $lastSendThisPass = false;
     $sendMoreRemain = false;
     foreach (self::$_tables as $table => &$tableDetails) {
         $loopCount++;
         if (0 != $startAt) {
             // Resuming...
             if ($loopCount < $startAt) {
                 continue;
             }
         }
         $checkCount++;
         // If backed up after modified time then it's up to date. Skip.
         if ($tableDetails['b'] > $tableDetails['m']) {
             pb_backupbuddy::status('details', 'Skipping send of table `' . $table . '` because it has already been sent since SQL file was made.');
             $alreadyBackedUp++;
             continue;
         }
         // Calculate table file.
         $tableFile = backupbuddy_live::getLiveDatabaseSnapshotDir() . $table . '.sql';
         // If too many attempts have passed then skip.
         if ($tableDetails['t'] >= self::MAX_SEND_ATTEMPTS) {
             pb_backupbuddy::status('error', 'Error #389328: This database file has failed transfer too many times. Skipping until next restart of periodic proces. File: `' . $tableFile . '`. Size: `' . pb_backupbuddy::$format->file_size(filesize($tableFile)) . '`.');
             $tooManySendFails++;
             continue;
         }
         // Load destination settings.
         $destination_settings = self::get_destination_settings();
         // If too many remote sends have failed today then give up for now since something is likely wrong.
         if (self::$_state['stats']['recent_send_fails'] > $destination_settings['max_daily_failures']) {
             $error = 'Error #4937743: Too many file transfer failures have occurred. Halting sends for today.';
             backupbuddy_core::addNotification('live_error', 'BackupBuddy Stash Live Error', $error);
             self::$_state['step']['last_status'] = $error;
             pb_backupbuddy::status('error', $error);
             return false;
         }
         // If this is not the first file we've sent this pass, see if we have enough time for more.
         if ($sendSizeSum > 0) {
             // Check if it appears we have enough time to send at least a full single chunk in this pass or if we need to pass off to a subsequent run.
             $send_speed = $sendSizeSum / 1048576 / $sendTimeSum;
             // Estimated speed at which we can send files out. Unit: MB / sec.
             $time_elapsed = microtime(true) - pb_backupbuddy::$start_time;
             $time_remaining = $destination_settings['max_time'] - ($time_elapsed + self::TIME_WIGGLE_ROOM);
             // Estimated time remaining before PHP times out. Unit: seconds.
             $size_possible_with_remaining_time = $send_speed * $time_remaining;
             // Size possible to send with remaining time (takes into account wiggle room).
             $size_to_send = $tableDetails['s'] / 1048576;
             // Size we want to send this pass. Unit: MB.
             if ($destination_settings['max_burst'] < $size_to_send) {
                 // If the chunksize is smaller than the full file then cap at sending that much.
                 $size_to_send = $destination_settings['max_burst'];
             }
             if ($size_possible_with_remaining_time < $size_to_send) {
                 // File (or chunk) is bigger than what we have time to send.
                 $lastSendThisPass = true;
                 $sendMoreRemain = true;
                 $send_speed_status = 'Not enough time to send more. To continue in next live_periodic pass.';
             } else {
                 $send_speed_status = 'Enough time to send more. Preparing for send.';
             }
             pb_backupbuddy::status('details', 'Not the first DB file to send this pass. Send speed: `' . $send_speed . '` MB/sec. Time elapsed: `' . $time_elapsed . '` sec. Time remaining: `' . $time_remaining . '` sec based on reported max time of `' . $destination_settings['max_time'] . '` sec with wiggle room `' . self::TIME_WIGGLE_ROOM . '` sec. Size possible with remaining time: `' . $size_possible_with_remaining_time . '` MB. Size to chunk (greater of filesize or chunk): `' . $size_to_send . '` MB. Conclusion: `' . $send_speed_status . '`.');
         }
         // end subsequent send time check.
         // NOT out of time so send this.
         if (true !== $lastSendThisPass) {
             // Run cleanup on send files.
             require_once pb_backupbuddy::plugin_path() . '/classes/housekeeping.php';
             backupbuddy_housekeeping::trim_remote_send_stats($file_prefix = 'send-live_', $limit = $destination_settings['max_send_details_limit'], '', $purge_log = true);
             // Only keep last X send fileoptions.
             // Moved to trim_remote_send_stats(). backupbuddy_housekeeping::purge_logs( $file_prefix = 'status-remote_send-live_', $limit = $destination_settings['max_send_details_limit'] ); // Only keep last X send logs.
             // Increment try count for transfer attempts and save.
             $tableDetails['t']++;
             self::$_tablesObj->save();
             // Send file. AFTER success sending this Stash2 destination will automatically trigger the live_periodic processing _IF_ multipart send. If success or fail the we come back here to potentially send more files in the same PHP pass so small files don't each need their own PHP page run.  Unless the process has restarted then this will still be the 'next' function to run.
             $send_id = 'live_' . md5($tableFile) . '-' . pb_backupbuddy::random_string(6);
             pb_backupbuddy::status('details', 'Live starting send function.');
             $sendTimeStart = microtime(true);
             // Mark table as unsent just before sending new version.
             $tableDetails['b'] = 0;
             // Close catalog & state while sending if > X size to prevent collisions.
             if ($tableDetails['s'] > self::CLOSE_CATALOG_WHEN_SENDING_FILESIZE) {
                 self::$_tablesObj = '';
                 self::$_stateObj = '';
             }
             // Set database table name into settings so send confirmation knows where to update sent timestamp.
             $destination_settings['_database_table'] = $table;
             // Send file to remote.
             $sendsStarted++;
             $result = pb_backupbuddy_destinations::send($destination_settings, $tableFile, $send_id, $delete_after = true, $isRetry = false, $trigger = 'live_periodic', $destination_id = backupbuddy_live::getLiveID());
             // Re-open catalog (if closed).
             self::_load_tables();
             self::_load_state();
             $sendTimeFinish = microtime(true);
             if (true === $result) {
                 $sendsSucceeded++;
                 $result_status = 'Success sending in single pass.';
                 $sendTimeSum += $sendTimeFinish - $sendTimeStart;
                 // Add to time sent sending.
                 // Set a minimum threshold so small files don't make server appear slower than reality due to overhead.
                 $minimum_size_threshold = self::MINIMUM_SIZE_THRESHOLD_FOR_SPEED_CALC;
                 // Pretend file is at least 500k each.
                 if ($tableDetails['s'] < $minimum_size_threshold) {
                     $sendSizeSum += $minimum_size_threshold;
                 } else {
                     $sendSizeSum += $tableDetails['s'];
                     // Add to size of data sent.
                 }
             } elseif (false === $result) {
                 $sendsFailed++;
                 self::$_state['stats']['recent_send_fails']++;
                 $result_status = 'Failure sending in single/first pass. See log above for error details. Failed sends today: `' . self::$_state['stats']['recent_send_fails'] . '`.';
             } elseif (is_array($result)) {
                 $sendsMultiparted++;
                 $result_status = 'Chunking commenced. Ending sends for this pass.';
                 $lastSendThisPass = true;
             }
             pb_backupbuddy::status('details', 'Live ended send database function. Status: ' . $result_status . '.');
         }
         // Check if we are done sending for this PHP pass/run.
         if (true === $lastSendThisPass) {
             break;
         }
     }
     // End foreach signatures.
     pb_backupbuddy::status('details', 'Snapshot send details for this round: Checked `' . $loopCount . '` tables, transfers started: `' . $sendsStarted . '`, transfers succeeded: `' . $sendsSucceeded . '`, transfers multiparted: `' . $sendsMultiparted . '`, transfers failed: `' . $sendsFailed . '`, skipped because already backed up: `' . $alreadyBackedUp . '`, skipped because too many send failures: `' . $tooManySendFails . '`.');
     // Schedule next run if we still have more files to potentially send.
     if (true === $sendMoreRemain) {
         return array('Sending queued tables', array($loopCount));
     } else {
         // No more files.
         return true;
     }
 }