/** * Will acquire a lock with the given name, * if no other lock has been acquired by this process. * * If the same lock has been acquired before (and not been released), * in internal counter is increased. Therefore you can acquire the same * lock multiple times, but you will then have to release them * the same amount of times * * @return a SafeLock instance or NULL if timed out */ public static function acquireLock($name, $timeout = 60) { if (self::$_acquired_lock == NULL) { // it's free, we'll try to take it $lock = new CRM_Core_Lock($name, $timeout); if (version_compare(CRM_Utils_System::version(), '4.6', '>=')) { // before 4.6, a new lock would be automatically acquired $lock->acquire(); } if ($lock != NULL && $lock->isAcquired()) { // we got it! self::$_acquired_lock = new CRM_Utils_SepaSafeLock($lock, $name); //error_log('acquired ' . getmypid()); return self::$_acquired_lock; } else { // timed out return NULL; } } elseif (self::$_acquired_lock->getName() == $name) { // this means acquiring 'our' lock again: $lock = self::$_acquired_lock; $lock->counter += 1; //error_log('acquired ' . getmypid() . "[{$lock->counter}]"); return $lock; } else { // this is the BAD case: somebody's trying to acquire ANOTHER LOCK, // while we still own another one $lock_name = $self::$_acquired_lock->getName(); throw new Exception("This process cannot acquire more than one lock! It still owns lock '{$lock_name}'."); } }
/** * Mailjet.ProcessBounces API * * @param array $params * @return array API result descriptor * @see civicrm_api3_create_success * @see civicrm_api3_create_error * @throws API_Exception */ function civicrm_api3_mailjet_processbounces($params) { $lock = new CRM_Core_Lock('civimail.job.MailjetProcessor'); if (!$lock->isAcquired()) { return civicrm_api3_create_error('Could not acquire lock, another MailjetProcessor process is running'); } $mailingId = CRM_Utils_Array::value('mailing_id', $params); //G: this is called when click on "Manually refresh Mailjet's stats" button if (!CRM_Utils_Mail_MailjetProcessor::processBounces($mailingId)) { $lock->release(); return civicrm_api3_create_error('Process Bounces failed'); } $lock->release(); // FIXME: processBounces doesn't return true/false on success/failure $values = array(); return civicrm_api3_create_success($values, $params, 'mailjet', 'bounces'); }
function run() { require_once 'CRM/Core/Lock.php'; $lock = new CRM_Core_Lock('CiviReportMail'); if ($lock->isAcquired()) { // try to unset any time limits if (!ini_get('safe_mode')) { set_time_limit(0); } // if there are named sets of settings, use them - otherwise use the default (null) require_once 'CRM/Report/Utils/Report.php'; $result = CRM_Report_Utils_Report::processReport(); echo $result['messages']; } else { throw new Exception('Could not acquire lock, another CiviReportMail process is running'); } $lock->release(); }
CRM_Utils_Mail_EmailProcessor::processBounces(); } $lock->release(); } else { session_start(); require_once '../civicrm.config.php'; require_once 'CRM/Core/Config.php'; $config = CRM_Core_Config::singleton(); CRM_Utils_System::authenticateScript(TRUE); require_once 'CRM/Utils/System.php'; CRM_Utils_System::loadBootStrap(); //log the execution of script CRM_Core_Error::debug_log_message('EmailProcessor.php'); require_once 'CRM/Core/Lock.php'; $lock = new CRM_Core_Lock('EmailProcessor'); if (!$lock->isAcquired()) { throw new Exception('Could not acquire lock, another EmailProcessor process is running'); } // try to unset any time limits if (!ini_get('safe_mode')) { set_time_limit(0); } require_once 'CRM/Utils/Mail/EmailProcessor.php'; // cleanup directories with old mail files (if they exist): CRM-4452 CRM_Utils_Mail_EmailProcessor::cleanupDir($config->customFileUploadDir . DIRECTORY_SEPARATOR . 'CiviMail.ignored'); CRM_Utils_Mail_EmailProcessor::cleanupDir($config->customFileUploadDir . DIRECTORY_SEPARATOR . 'CiviMail.processed'); // check if the script is being used for civimail processing or email to // activity processing. $isCiviMail = !empty($_REQUEST['emailtoactivity']) ? FALSE : TRUE; CRM_Utils_Mail_EmailProcessor::process($isCiviMail); $lock->release();
/** * before we run jobs, we need to split the jobs * @param int $offset * @param null $mode */ public static function runJobs_pre($offset = 200, $mode = NULL) { $job = new CRM_Mailing_BAO_MailingJob(); $jobTable = CRM_Mailing_DAO_MailingJob::getTableName(); $mailingTable = CRM_Mailing_DAO_Mailing::getTableName(); $currentTime = date('YmdHis'); $mailingACL = CRM_Mailing_BAO_Mailing::mailingACL('m'); $workflowClause = CRM_Mailing_BAO_MailingJob::workflowClause(); $domainID = CRM_Core_Config::domainID(); $modeClause = 'AND m.sms_provider_id IS NULL'; if ($mode == 'sms') { $modeClause = 'AND m.sms_provider_id IS NOT NULL'; } // Select all the mailing jobs that are created from // when the mailing is submitted or scheduled. $query = "\n SELECT j.*\n FROM {$jobTable} j,\n {$mailingTable} m\n WHERE m.id = j.mailing_id AND m.domain_id = {$domainID}\n {$workflowClause}\n {$modeClause}\n AND j.is_test = 0\n AND ( ( j.start_date IS null\n AND j.scheduled_date <= {$currentTime}\n AND j.status = 'Scheduled'\n AND j.end_date IS null ) )\n AND ((j.job_type is NULL) OR (j.job_type <> 'child'))\n ORDER BY j.scheduled_date,\n j.start_date"; $job->query($query); // For each of the "Parent Jobs" we find, we split them into // X Number of child jobs while ($job->fetch()) { // still use job level lock for each child job $lockName = "civimail.job.{$job->id}"; $lock = new CRM_Core_Lock($lockName); if (!$lock->isAcquired()) { continue; } // Re-fetch the job status in case things // changed between the first query and now // to avoid race conditions $job->status = CRM_Core_DAO::getFieldValue('CRM_Mailing_DAO_MailingJob', $job->id, 'status', 'id', TRUE); if ($job->status != 'Scheduled') { $lock->release(); continue; } $job->split_job($offset); // update the status of the parent job $transaction = new CRM_Core_Transaction(); $saveJob = new CRM_Mailing_DAO_MailingJob(); $saveJob->id = $job->id; $saveJob->start_date = date('YmdHis'); $saveJob->status = 'Running'; $saveJob->save(); $transaction->commit(); // Release the job lock $lock->release(); } }
/** * load the smart group cache for a saved search * * @param object $group - the smart group that needs to be loaded * @param boolean $force - should we force a search through * */ static function load(&$group, $force = FALSE) { $groupID = $group->id; $savedSearchID = $group->saved_search_id; if (array_key_exists($groupID, self::$_alreadyLoaded) && !$force) { return; } // grab a lock so other processes dont compete and do the same query $lockName = "civicrm.group.{$groupID}"; $lock = new CRM_Core_Lock($lockName); if (!$lock->isAcquired()) { // this can cause inconsistent results since we dont know if the other process // will fill up the cache before our calling routine needs it. // however this routine does not return the status either, so basically // its a "lets return and hope for the best" return; } self::$_alreadyLoaded[$groupID] = 1; // we now have the lock, but some other proces could have actually done the work // before we got here, so before we do any work, lets ensure that work needs to be // done // we allow hidden groups here since we dont know if the caller wants to evaluate an // hidden group if (!$force && !self::shouldGroupBeRefreshed($groupID, TRUE)) { $lock->release(); return; } $sql = NULL; $idName = 'id'; $customClass = NULL; if ($savedSearchID) { $ssParams = CRM_Contact_BAO_SavedSearch::getSearchParams($savedSearchID); // rectify params to what proximity search expects if there is a value for prox_distance // CRM-7021 if (!empty($ssParams)) { CRM_Contact_BAO_ProximityQuery::fixInputParams($ssParams); } $returnProperties = array(); if (CRM_Core_DAO::getFieldValue('CRM_Contact_DAO_SavedSearch', $savedSearchID, 'mapping_id')) { $fv = CRM_Contact_BAO_SavedSearch::getFormValues($savedSearchID); $returnProperties = CRM_Core_BAO_Mapping::returnProperties($fv); } if (isset($ssParams['customSearchID'])) { // if custom search // we split it up and store custom class // so temp tables are not destroyed if they are used // hence customClass is defined above at top of function $customClass = CRM_Contact_BAO_SearchCustom::customClass($ssParams['customSearchID'], $savedSearchID); $searchSQL = $customClass->contactIDs(); $searchSQL = str_replace('ORDER BY contact_a.id ASC', '', $searchSQL); $idName = 'contact_id'; } else { $formValues = CRM_Contact_BAO_SavedSearch::getFormValues($savedSearchID); $query = new CRM_Contact_BAO_Query($ssParams, $returnProperties, NULL, FALSE, FALSE, 1, TRUE, TRUE, FALSE, CRM_Utils_Array::value('display_relationship_type', $formValues), CRM_Utils_Array::value('operator', $formValues, 'AND')); $query->_useDistinct = FALSE; $query->_useGroupBy = FALSE; $searchSQL = $query->searchQuery(0, 0, NULL, FALSE, FALSE, FALSE, TRUE, TRUE, NULL, NULL, NULL, TRUE); } $groupID = CRM_Utils_Type::escape($groupID, 'Integer'); $sql = $searchSQL . " AND contact_a.id NOT IN (\n SELECT contact_id FROM civicrm_group_contact\n WHERE civicrm_group_contact.status = 'Removed'\n AND civicrm_group_contact.group_id = {$groupID} ) "; } if ($sql) { $sql = preg_replace("/^\\s*SELECT/", "SELECT {$groupID} as group_id, ", $sql); } // lets also store the records that are explicitly added to the group // this allows us to skip the group contact LEFT JOIN $sqlB = "\nSELECT {$groupID} as group_id, contact_id as {$idName}\nFROM civicrm_group_contact\nWHERE civicrm_group_contact.status = 'Added'\n AND civicrm_group_contact.group_id = {$groupID} "; $groupIDs = array($groupID); self::remove($groupIDs); $processed = FALSE; $tempTable = 'civicrm_temp_group_contact_cache' . rand(0, 2000); foreach (array($sql, $sqlB) as $selectSql) { if (!$selectSql) { continue; } $insertSql = "CREATE TEMPORARY TABLE {$tempTable} ({$selectSql});"; $processed = TRUE; $result = CRM_Core_DAO::executeQuery($insertSql); CRM_Core_DAO::executeQuery("INSERT IGNORE INTO civicrm_group_contact_cache (contact_id, group_id)\n SELECT DISTINCT {$idName}, group_id FROM {$tempTable}\n "); CRM_Core_DAO::executeQuery(" DROP TABLE {$tempTable}"); } self::updateCacheTime($groupIDs, $processed); if ($group->children) { //Store a list of contacts who are removed from the parent group $sql = "\nSELECT contact_id\nFROM civicrm_group_contact\nWHERE civicrm_group_contact.status = 'Removed'\nAND civicrm_group_contact.group_id = {$groupID} "; $dao = CRM_Core_DAO::executeQuery($sql); $removed_contacts = array(); while ($dao->fetch()) { $removed_contacts[] = $dao->contact_id; } $childrenIDs = explode(',', $group->children); foreach ($childrenIDs as $childID) { $contactIDs = CRM_Contact_BAO_Group::getMember($childID, FALSE); //Unset each contact that is removed from the parent group foreach ($removed_contacts as $removed_contact) { unset($contactIDs[$removed_contact]); } $values = array(); foreach ($contactIDs as $contactID => $dontCare) { $values[] = "({$groupID},{$contactID})"; } self::store($groupIDs, $values); } } $lock->release(); }
static function processQueue($mode = NULL) { $config =& CRM_Core_Config::singleton(); // CRM_Core_Error::debug_log_message("Beginning processQueue run: {$config->mailerJobsMax}, {$config->mailerJobSize}"); if ($mode == NULL && CRM_Core_BAO_MailSettings::defaultDomain() == "EXAMPLE.ORG") { CRM_Core_Error::fatal(ts('The <a href="%1">default mailbox</a> has not been configured. You will find <a href="%2">more info in the online user and administrator guide</a>', array(1 => CRM_Utils_System::url('civicrm/admin/mailSettings', 'reset=1'), 2 => "http://book.civicrm.org/user/advanced-configuration/email-system-configuration/"))); } // check if we are enforcing number of parallel cron jobs // CRM-8460 $gotCronLock = FALSE; if (property_exists($config, 'mailerJobsMax') && $config->mailerJobsMax && $config->mailerJobsMax > 1) { $lockArray = range(1, $config->mailerJobsMax); shuffle($lockArray); // check if we are using global locks $serverWideLock = CRM_Core_BAO_Setting::getItem(CRM_Core_BAO_Setting::MAILING_PREFERENCES_NAME, 'civimail_server_wide_lock'); foreach ($lockArray as $lockID) { $cronLock = new CRM_Core_Lock("civimail.cronjob.{$lockID}", NULL, $serverWideLock); if ($cronLock->isAcquired()) { $gotCronLock = TRUE; break; } } // exit here since we have enuf cronjobs running if (!$gotCronLock) { CRM_Core_Error::debug_log_message('Returning early, since max number of cronjobs running'); return TRUE; } } // load bootstrap to call hooks // Split up the parent jobs into multiple child jobs $mailerJobSize = property_exists($config, 'mailerJobSize') ? $config->mailerJobSize : NULL; CRM_Mailing_BAO_MailingJob::runJobs_pre($mailerJobSize, $mode); CRM_Mailing_BAO_MailingJob::runJobs(NULL, $mode); CRM_Mailing_BAO_MailingJob::runJobs_post($mode); // lets release the global cron lock if we do have one if ($gotCronLock) { $cronLock->release(); } // CRM_Core_Error::debug_log_message('Ending processQueue run'); return TRUE; }
/** * This api reloads all the smart groups. If the org has a large number of smart groups * it is recommended that they use the limit clause to limit the number of smart groups * evaluated on a per job basis. Might also help to increase the smartGroupCacheTimeout * and use the cache */ function civicrm_api3_job_group_rebuild($params) { $lock = new CRM_Core_Lock('civimail.job.groupRebuild'); if (!$lock->isAcquired()) { return civicrm_api3_create_error('Could not acquire lock, another EmailProcessor process is running'); } $limit = CRM_Utils_Array::value('limit', $params, 0); CRM_Contact_BAO_GroupContactCache::loadAll(null, $limit); $lock->release(); return civicrm_api3_create_success(); }
/** * Initiate all pending/ready jobs * * @return void * @access public * @static */ public static function runJobs($testParams = null) { $job =& new CRM_Mailing_BAO_Job(); $mailing =& new CRM_Mailing_DAO_Mailing(); $config =& CRM_Core_Config::singleton(); $jobTable = CRM_Mailing_DAO_Job::getTableName(); $mailingTable = CRM_Mailing_DAO_Mailing::getTableName(); if (!empty($testParams)) { $query = "\nSELECT *\n FROM {$jobTable}\n WHERE id = {$testParams['job_id']}"; $job->query($query); } else { $currentTime = date('YmdHis'); /* FIXME: we might want to go to a progress table.. */ $query = "\nSELECT j.*\n FROM {$jobTable} j,\n {$mailingTable} m\n WHERE m.id = j.mailing_id\n AND j.is_test = 0\n AND ( ( j.start_date IS null\n AND j.scheduled_date <= {$currentTime}\n AND j.status = 'Scheduled' )\n OR ( j.status = 'Running'\n AND j.end_date IS null ) )\nORDER BY j.scheduled_date,\n j.start_date"; $job->query($query); } require_once 'CRM/Core/Lock.php'; /* TODO We should parallelize or prioritize this */ while ($job->fetch()) { // fix for cancel job at run time which is in queue, CRM-4246 if (CRM_Core_DAO::getFieldValue('CRM_Mailing_DAO_Job', $job->id, 'status') == 'Canceled') { continue; } $lockName = "civimail.job.{$job->id}"; // get a lock on this job id $lock = new CRM_Core_Lock($lockName); if (!$lock->isAcquired()) { continue; } /* Queue up recipients for all jobs being launched */ if ($job->status != 'Running') { require_once 'CRM/Core/Transaction.php'; $transaction = new CRM_Core_Transaction(); $job->queue($testParams); /* Start the job */ // use a seperate DAO object to protect the loop // integrity. I think transactions messes it up // check CRM-2469 $saveJob = new CRM_Mailing_DAO_Job(); $saveJob->id = $job->id; $saveJob->start_date = date('YmdHis'); $saveJob->status = 'Running'; $saveJob->save(); $transaction->commit(); } $mailer =& $config->getMailer(); /* Compose and deliver */ $isComplete = $job->deliver($mailer, $testParams); require_once 'CRM/Utils/Hook.php'; CRM_Utils_Hook::post('create', 'CRM_Mailing_DAO_Spool', $job->id, $isComplete); if ($isComplete) { /* Finish the job */ require_once 'CRM/Core/Transaction.php'; $transaction = new CRM_Core_Transaction(); // use a seperate DAO object to protect the loop // integrity. I think transactions messes it up // check CRM-2469 $saveJob = new CRM_Mailing_DAO_Job(); $saveJob->id = $job->id; $saveJob->end_date = date('YmdHis'); $saveJob->status = 'Complete'; $saveJob->save(); $mailing->reset(); $mailing->id = $job->mailing_id; $mailing->is_completed = true; $mailing->save(); $transaction->commit(); } $lock->release(); if ($testParams) { return $isComplete; } } }
function civicrm_api3_job_fetch_activities($params) { require_once 'CRM/Utils/Mail/EmailProcessor.php'; require_once 'CRM/Core/Lock.php'; $lock = new CRM_Core_Lock('EmailProcessor'); if (!$lock->isAcquired()) { return civicrm_api3_create_error("Could not acquire lock, another EmailProcessor process is running"); } try { CRM_Utils_Mail_EmailProcessor::processActivities(); $values = array(); $lock->release(); return civicrm_api3_create_success($values, $params, 'mailing', 'activities'); } catch (Exception $e) { $lock->release(); return civicrm_api3_create_error("Process Activities failed"); } }
/** * Store an item in the DB cache * * @param object $data (required) A reference to the data that will be serialized and stored * @param string $group (required) The group name of the item * @param string $path (required) The path under which this item is stored * @param int $componentID The optional component ID (so componenets can share the same name space) * * @return void * @static * @access public */ static function setItem(&$data, $group, $path, $componentID = NULL) { if (self::$_cache === NULL) { self::$_cache = array(); } $dao = new CRM_Core_DAO_Cache(); $dao->group_name = $group; $dao->path = $path; $dao->component_id = $componentID; // get a lock so that multiple ajax requests on the same page // dont trample on each other // CRM-11234 $lockName = "civicrm.cache.{$group}_{$path}._{$componentID}"; $lock = new CRM_Core_Lock($lockName); if (!$lock->isAcquired()) { CRM_Core_Error::fatal(); } $dao->find(TRUE); $dao->data = serialize($data); $dao->created_date = date('YmdHis'); $dao->save(); $lock->release(); $dao->free(); // cache coherency - refresh or remove dependent caches $argString = "CRM_CT_{$group}_{$path}_{$componentID}"; $cache = CRM_Utils_Cache::singleton(); $data = unserialize($dao->data); self::$_cache[$argString] = $data; $cache->set($argString, $data); $argString = "CRM_CT_CI_{$group}_{$componentID}"; unset(self::$_cache[$argString]); $cache->delete($argString); }
public static function runJobs_pre($offset = 200) { $job = new CRM_Mailing_BAO_Job(); $config = CRM_Core_Config::singleton(); $jobTable = CRM_Mailing_DAO_Job::getTableName(); $mailingTable = CRM_Mailing_DAO_Mailing::getTableName(); $currentTime = date('YmdHis'); $mailingACL = CRM_Mailing_BAO_Mailing::mailingACL('m'); // add an additional check and only process // jobs that are approved $workflowClause = null; require_once 'CRM/Mailing/Info.php'; if (CRM_Mailing_Info::workflowEnabled()) { require_once 'CRM/Core/OptionGroup.php'; $approveOptionID = CRM_Core_OptionGroup::getValue('mail_approval_status', 'Approved', 'name'); if ($approveOptionID) { $workflowClause = " AND m.approval_status_id = {$approveOptionID} "; } } // Select all the mailing jobs that are created from // when the mailing is submitted or scheduled. $query = "\n\t\tSELECT j.*\n\t\t FROM {$jobTable} j,\n\t\t\t\t {$mailingTable} m\n\t\t WHERE m.id = j.mailing_id\n {$workflowClause}\n\t\t AND j.is_test = 0\n\t\t AND ( ( j.start_date IS null\n\t\t AND j.scheduled_date <= {$currentTime}\n\t\t AND j.status = 'Scheduled'\n\t\t AND j.end_date IS null ) )\n\t\t AND ((j.job_type is NULL) OR (j.job_type <> 'child'))\n\t\tORDER BY j.scheduled_date,\n\t\t\t\t j.start_date"; $job->query($query); require_once 'CRM/Core/Lock.php'; // For reach of the "Parent Jobs" we find, we split them into // X Number of child jobs while ($job->fetch()) { // still use job level lock for each child job $lockName = "civimail.job.{$job->id}"; $lock = new CRM_Core_Lock($lockName); if (!$lock->isAcquired()) { continue; } // refetch the job status in case things // changed between the first query and now // avoid race conditions $job->status = CRM_Core_DAO::getFieldValue('CRM_Mailing_DAO_Job', $job->id, 'status'); if ($job->status != 'Scheduled') { $lock->release(); continue; } $job->split_job($offset); // update the status of the parent job require_once 'CRM/Core/Transaction.php'; $transaction = new CRM_Core_Transaction(); $saveJob = new CRM_Mailing_DAO_Job(); $saveJob->id = $job->id; $saveJob->start_date = date('YmdHis'); $saveJob->status = 'Running'; $saveJob->save(); $transaction->commit(); // Release the job lock $lock->release(); } }