protected function getConnection(DataSourceMetaData $datasource) { $transaction = TransactionManager::getInstance()->getTransaction($datasource->name); $connectionName = $transaction->assembleResourceName(get_class($datasource), $datasource->name); $connection = $transaction->findResource($connectionName); if (!isset($connection)) { $connection = $this->getExtension('initializeConnection')->initialize($this, $datasource); if (!$datasource->temporary) { $transaction->registerResource($connectionName, $connection); $transaction->registerActionCallback(new ConnectionTransactionActionCallback($datasource->name)); } } return $connection; }
/** * blcLink::save() * Save link data to DB. * * @return bool True if saved successfully, false otherwise. */ function save() { global $wpdb, $blclog; /** @var wpdb $wpdb */ if (!$this->valid()) { return false; } //A link can't be broken and treated as a warning at the same time. if ($this->broken && $this->warning) { $this->warning = false; } //Make a list of fields to be saved and their values in DB format $values = array(); foreach ($this->field_format as $field => $format) { $values[$field] = $this->{$field}; } $values = $this->to_db_format($values); if ($this->is_new) { TransactionManager::getInstance()->commit(); //BUG: Technically, there should be a 'LOCK TABLES wp_blc_links WRITE' here. In fact, //the plugin should probably lock all involved tables whenever it parses something, lest //the user (ot another plugin) modify the thing being parsed while we're working. //The problem with table locking, though, is that parsing takes a long time and having //all of WP freeze while the plugin is working would be a Bad Thing. Food for thought. //Check if there's already a link with this URL present $q = $wpdb->prepare("SELECT link_id FROM {$wpdb->prefix}blc_links WHERE url = %s", $this->url); $existing_id = $wpdb->get_var($q); if (!empty($existing_id)) { //Dammit. $this->link_id = $existing_id; $this->is_new = false; return true; } //Insert a new row $q = sprintf("INSERT INTO {$wpdb->prefix}blc_links( %s ) VALUES( %s )", implode(', ', array_keys($values)), implode(', ', array_values($values))); //FB::log($q, 'Link add query'); $blclog->debug(__CLASS__ . ':' . __FUNCTION__ . ' Adding a new link. SQL query:' . "\n", $q); $rez = $wpdb->query($q) !== false; if ($rez) { $this->link_id = $wpdb->insert_id; $blclog->debug(__CLASS__ . ':' . __FUNCTION__ . ' Database record created. ID = ' . $this->link_id); //FB::info($this->link_id, "Link added"); //If the link was successfully saved then it's no longer "new" $this->is_new = false; } else { $blclog->error(__CLASS__ . ':' . __FUNCTION__ . ' Error adding link', $this->url); //FB::error($wpdb->last_error, "Error adding link {$this->url}"); } return $rez; } else { if ($this->isOptionLinkChanged !== true) { TransactionManager::getInstance()->start(); } $this->isOptionLinkChanged = false; //Generate the field = dbvalue expressions $set_exprs = array(); foreach ($values as $name => $value) { $set_exprs[] = "{$name} = {$value}"; } $set_exprs = implode(', ', $set_exprs); //Update an existing DB record $q = sprintf("UPDATE {$wpdb->prefix}blc_links SET %s WHERE link_id=%d", $set_exprs, intval($this->link_id)); //FB::log($q, 'Link update query'); $blclog->debug(__CLASS__ . ':' . __FUNCTION__ . ' Updating a link. SQL query:' . "\n", $q); $rez = $wpdb->query($q) !== false; if ($rez) { //FB::log($this->link_id, "Link updated"); $blclog->debug(__CLASS__ . ':' . __FUNCTION__ . ' Link updated.'); } else { $blclog->error(__CLASS__ . ':' . __FUNCTION__ . ' Error updating link', $this->url); //FB::error($wpdb->last_error, "Error updating link {$this->url}"); } return $rez; } }
/** * Parse the container for links and save the results to the DB. * * @return void */ function synch() { //FB::log("Parsing {$this->container_type}[{$this->container_id}]"); //Remove any existing link instance records associated with the container $this->delete_instances(); //Load the wrapped object, if not done already $this->get_wrapped_object(); //FB::log($this->fields, "Parseable fields :"); //Iterate over all parse-able fields foreach ($this->fields as $name => $format) { //Get the field value $value = $this->get_field($name); if (empty($value)) { //FB::log($name, "Skipping empty field"); continue; } //FB::log($name, "Parsing field"); //Get all parsers applicable to this field $parsers = blcParserHelper::get_parsers($format, $this->container_type); //FB::log($parsers, "Applicable parsers"); if (empty($parsers)) { continue; } $base_url = $this->base_url(); $default_link_text = $this->default_link_text($name); //Parse the field with each parser foreach ($parsers as $parser) { //FB::log("Parsing $name with '{$parser->parser_type}' parser"); $found_instances = $parser->parse($value, $base_url, $default_link_text); //FB::log($found_instances, "Found instances"); $transactionManager = TransactionManager::getInstance(); $transactionManager->start(); //Complete the link instances by adding container info, then save them to the DB. foreach ($found_instances as $instance) { $instance->set_container($this, $name); $instance->save(); } $transactionManager->commit(); } } $this->mark_as_synched(); }
/** * The main worker function that does all kinds of things. * * @return void */ function work() { global $blclog; //Close the session to prevent lock-ups. //PHP sessions are blocking. session_start() will wait until all other scripts that are using the same session //are finished. As a result, a long-running script that unintentionally keeps the session open can cause //the entire site to "lock up" for the current user/browser. WordPress itself doesn't use sessions, but some //plugins do, so we should explicitly close the session (if any) before starting the worker. if (session_id() != '') { session_write_close(); } if (!$this->acquire_lock()) { //FB::warn("Another instance of BLC is already working. Stop."); $blclog->info('Another instance of BLC is already working. Stop.'); return; } if ($this->server_too_busy()) { //FB::warn("Server is too busy. Stop."); $blclog->warn('Server load is too high, stopping.'); return; } $this->start_timer(); $blclog->info('work() starts'); $max_execution_time = $this->conf->options['max_execution_time']; /***************************************** Preparation ******************************************/ // Check for safe mode if (blcUtility::is_safe_mode()) { // Do it the safe mode way - obey the existing max_execution_time setting $t = ini_get('max_execution_time'); if ($t && $t < $max_execution_time) { $max_execution_time = $t - 1; } } else { // Do it the regular way @set_time_limit($max_execution_time * 2); //x2 should be plenty, running any longer would mean a glitch. } //Don't stop the script when the connection is closed ignore_user_abort(true); //Close the connection as per http://www.php.net/manual/en/features.connection-handling.php#71172 //This reduces resource usage. //(Disable when debugging or you won't get the FirePHP output) if (!headers_sent() && (defined('DOING_AJAX') && constant('DOING_AJAX')) && (!defined('BLC_DEBUG') || !constant('BLC_DEBUG'))) { @ob_end_clean(); //Discard the existing buffer, if any header("Connection: close"); ob_start(); echo 'Connection closed'; //This could be anything $size = ob_get_length(); header("Content-Length: {$size}"); ob_end_flush(); // Strange behaviour, will not work flush(); // Unless both are called ! } //Load modules for this context $moduleManager = blcModuleManager::getInstance(); $moduleManager->load_modules('work'); $target_usage_fraction = $this->conf->get('target_resource_usage', 0.25); //Target usage must be between 1% and 100%. $target_usage_fraction = max(min($target_usage_fraction, 1), 0.01); /***************************************** Parse posts and bookmarks ******************************************/ $orphans_possible = false; $still_need_resynch = $this->conf->options['need_resynch']; if ($still_need_resynch) { //FB::log("Looking for containers that need parsing..."); $max_containers_per_query = 50; $start = microtime(true); $containers = blcContainerHelper::get_unsynched_containers($max_containers_per_query); $get_containers_time = microtime(true) - $start; while (!empty($containers)) { //FB::log($containers, 'Found containers'); $this->sleep_to_maintain_ratio($get_containers_time, $target_usage_fraction); foreach ($containers as $container) { $synch_start_time = microtime(true); //FB::log($container, "Parsing container"); $container->synch(); $synch_elapsed_time = microtime(true) - $synch_start_time; $blclog->info(sprintf('Parsed container %s[%s] in %.2f ms', $container->container_type, $container->container_id, $synch_elapsed_time * 1000)); //Check if we still have some execution time left if ($this->execution_time() > $max_execution_time) { //FB::log('The allotted execution time has run out'); blc_cleanup_links(); $this->release_lock(); return; } //Check if the server isn't overloaded if ($this->server_too_busy()) { //FB::log('Server overloaded, bailing out.'); blc_cleanup_links(); $this->release_lock(); return; } //Intentionally slow down parsing to reduce the load on the server. Basically, //we work $target_usage_fraction of the time and sleep the rest of the time. $this->sleep_to_maintain_ratio($synch_elapsed_time, $target_usage_fraction); } $orphans_possible = true; $start = microtime(true); $containers = blcContainerHelper::get_unsynched_containers($max_containers_per_query); $get_containers_time = microtime(true) - $start; } //FB::log('No unparsed items found.'); $still_need_resynch = false; } else { //FB::log('Resynch not required.'); } /****************************************** Resynch done? *******************************************/ if ($this->conf->options['need_resynch'] && !$still_need_resynch) { $this->conf->options['need_resynch'] = $still_need_resynch; $this->conf->save_options(); } /****************************************** Remove orphaned links *******************************************/ if ($orphans_possible) { $start = microtime(true); $blclog->info('Removing orphaned links.'); blc_cleanup_links(); $get_links_time = microtime(true) - $start; $this->sleep_to_maintain_ratio($get_links_time, $target_usage_fraction); } //Check if we still have some execution time left if ($this->execution_time() > $max_execution_time) { //FB::log('The allotted execution time has run out'); $blclog->info('The allotted execution time has run out.'); $this->release_lock(); return; } if ($this->server_too_busy()) { //FB::log('Server overloaded, bailing out.'); $blclog->info('Server load too high, stopping.'); $this->release_lock(); return; } /***************************************** Check links ******************************************/ $max_links_per_query = 30; $start = microtime(true); $links = $this->get_links_to_check($max_links_per_query); $get_links_time = microtime(true) - $start; while ($links) { $this->sleep_to_maintain_ratio($get_links_time, $target_usage_fraction); //Some unchecked links found //FB::log("Checking ".count($links)." link(s)"); $blclog->info("Checking " . count($links) . " link(s)"); //Randomizing the array reduces the chances that we'll get several links to the same domain in a row. shuffle($links); $transactionManager = TransactionManager::getInstance(); $transactionManager->start(); foreach ($links as $link) { //Does this link need to be checked? Excluded links aren't checked, but their URLs are still //tested periodically to see if they're still on the exclusion list. if (!$this->is_excluded($link->url)) { //Check the link. //FB::log($link->url, "Checking link {$link->link_id}"); $link->check(true); } else { //FB::info("The URL {$link->url} is excluded, skipping link {$link->link_id}."); $link->last_check_attempt = time(); $link->save(); } //Check if we still have some execution time left if ($this->execution_time() > $max_execution_time) { //FB::log('The allotted execution time has run out'); $blclog->info('The allotted execution time has run out.'); $this->release_lock(); return; } //Check if the server isn't overloaded if ($this->server_too_busy()) { //FB::log('Server overloaded, bailing out.'); $blclog->info('Server load too high, stopping.'); $this->release_lock(); return; } } $transactionManager->commit(); $start = microtime(true); $links = $this->get_links_to_check($max_links_per_query); $get_links_time = microtime(true) - $start; } //FB::log('No links need to be checked right now.'); $this->release_lock(); $blclog->info('work(): All done.'); //FB::log('All done.'); }
public function abort() { $transaction = TransactionManager::getInstance()->getTransaction($this->datasourceName); $transaction->rollback(); parent::abort(); }