Kaynağa Gözat

#805 Use a mutex to turn the insertion of a new ticket into an atomic operation

git-svn-id: http://svn.code.sf.net/p/itop/code/trunk@2953 a333f486-631f-4898-b8df-5754b55c2be0
dflaven 11 yıl önce
ebeveyn
işleme
3772f10ee7

+ 1 - 0
core/dbobject.class.php

@@ -26,6 +26,7 @@
 
 require_once('metamodel.class.php');
 require_once('deletionplan.class.inc.php');
+require_once('mutex.class.inc.php');
 
 
 /**

+ 16 - 1
datamodels/2.x/itop-tickets/datamodel.itop-tickets.xml

@@ -148,7 +148,22 @@
           <count_max>0</count_max>
         </field>
       </fields>
-      <methods/>
+      <methods>
+        <method id="DBInsertNoReload">
+          <static>false</static>
+          <access>public</access>
+          <type>Overload-DBObject</type>
+          <code><![CDATA[    public function DBInsertNoReload()
+    {
+      $oMutex = new iTopMutex('ticket_insert');
+      $oMutex->Lock();
+      $iKey = parent::DBInsertNoReload();
+      $oMutex->Unlock();
+      return $iKey;
+    }
+        ]]></code>
+        </method>
+      </methods>
       <presentation>
         <details>
           <items>

+ 1 - 1
readme.txt

@@ -96,7 +96,7 @@ Flash version 8 or higher is required.
      - data
      - env-production
      - log
-3) Point your web browser to the URL corresponding to the directory where the files
+4) Point your web browser to the URL corresponding to the directory where the files
    have been unzipped and follow the indications on the screen.
   
 If you wish to re-launch the installation process (for example in order to install

+ 52 - 36
webservices/cron.php

@@ -40,13 +40,27 @@ if (!file_exists($sConfigFile))
 require_once(APPROOT.'/application/startup.inc.php');
 
 
+function LogError($oP, $sErrorMessage, $sSeverity = 'ERROR')
+{
+	$bModeCLI = utils::IsModeCLI();
+
+	if ($bModeCLI)
+	{
+		error_log(ITOP_APPLICATION." cron.php $sSeverity: ".$sErrorMessage);
+	}
+	else
+	{
+		$oP->p("$sSeverity: $sMessage");
+	}
+	
+}
 
 function ReadMandatoryParam($oP, $sParam, $sSanitizationFilter = 'parameter')
 {
 	$sValue = utils::ReadParam($sParam, null, true /* Allow CLI */, $sSanitizationFilter);
 	if (is_null($sValue))
 	{
-		$oP->p("ERROR: Missing argument '$sParam'\n");
+		LogError($oP, "Missing argument '$sParam'");
 		UsageAndExit($oP);
 	}
 	return trim($sValue);
@@ -59,7 +73,7 @@ function UsageAndExit($oP)
 	if ($bModeCLI)
 	{
 		$oP->p("USAGE:\n");
-		$oP->p("php cron.php --auth_user=<login> --auth_pwd=<password> [--param_file=<file>] [--verbose=1] [--debug=1] [--status_only=1]\n");		
+		$oP->p("php cron.php --auth_user=<login> --auth_pwd=<password> [--param_file=<file>] [--verbose=1] [--status_only=1]\n");		
 	}
 	else
 	{
@@ -69,7 +83,7 @@ function UsageAndExit($oP)
 	exit -2;
 }
 
-function RunTask($oProcess, BackgroundTask $oTask, $oStartDate, $iTimeLimit)
+function RunTask($oP, $oProcess, BackgroundTask $oTask, $oStartDate, $iTimeLimit)
 {
 	try
 	{
@@ -111,7 +125,7 @@ function RunTask($oProcess, BackgroundTask $oTask, $oStartDate, $iTimeLimit)
 	}
 	catch(Exception $e)
 	{
-		$sMessage = 'Processing failed, the following exception occured: '.$e->getMessage();
+		LogError($oP, 'Processing failed, the following exception occured: '.$e->getMessage());
 	}
 	return $sMessage;	
 }
@@ -199,7 +213,7 @@ function CronExec($oP, $aProcesses, $bVerbose)
 				{
 					$oP->p(">> === ".$oNow->format('Y-m-d H:i:s').sprintf(" Starting:%-'=40s", ' '.$sTaskClass.' '));
 				}
-				$sMessage = RunTask($oProcess, $aTasks[$sTaskClass], $oNow, $iTimeLimit);
+				$sMessage = RunTask($oP, $oProcess, $aTasks[$sTaskClass], $oNow, $iTimeLimit);
 				if ($bVerbose)
 				{
 					if(!empty($sMessage))
@@ -291,7 +305,7 @@ if (utils::IsModeCLI())
 	}
 	else
 	{
-		$oP->p("Access wrong credentials ('$sAuthUser')");
+		LogError($oP, "Access wrong credentials ('$sAuthUser')");
 		$oP->output();
 		exit -1;
 	}
@@ -305,11 +319,19 @@ else
 
 if (!UserRights::IsAdministrator())
 {
-	$oP->p("Access restricted to administrators");
-	$oP->Output();
+	LogError($oP, 'Access restricted to administrators');
+	$oP->output();
+	exit -1;
+}
+
+if (!MetaModel::DBHasAccess(ACCESS_ADMIN_WRITE))
+{
+	LogError($oP, 'A database maintenance is ongoing (read-only mode even for admins)', 'WARNING');
+	$oP->output();
 	exit -1;
 }
 
+
 // Enumerate classes implementing BackgroundProcess
 //
 $aProcesses = array();
@@ -329,7 +351,6 @@ foreach(get_declared_classes() as $sPHPClass)
 
 
 $bVerbose = utils::ReadParam('verbose', false, true /* Allow CLI */);
-$bDebug = utils::ReadParam('debug', false, true /* Allow CLI */);
 
 if ($bVerbose)
 {
@@ -348,44 +369,39 @@ if (utils::ReadParam('status_only', false, true /* Allow CLI */))
 	exit(0);
 }
 
-require_once(APPROOT.'core/mutex.class.inc.php');
+// Compute the name of a lock for mysql
+// The name is server-wide
+$oConfig = utils::GetConfig();
+$sLockName = 'itop.cron.'.$oConfig->GetDBName().'_'.$oConfig->GetDBSubname();
+
 $oP->p("Starting: ".time().' ('.date('Y-m-d H:i:s').')');
 
-try
+// CAUTION: using GET_LOCK anytime on the same connexion will RELEASE the lock
+// Todo: invoke GET_LOCK from a dedicated session (encapsulate that into a mutex class)
+$res = CMDBSource::QueryToScalar("SELECT GET_LOCK('$sLockName', 1)");// timeout = 1 second (see also IS_FREE_LOCK)
+if (is_null($res))
+{
+	LogError($oP, "Failed to acquire the lock '$sLockName'");
+}
+elseif ($res === '1')
 {
-	$oConfig = utils::GetConfig();
-	$oMutex = new iTopMutex('cron.'.$oConfig->GetDBName().'_'.$oConfig->GetDBSubname());
-	if ($oMutex->TryLock())
+	// The current session holds the lock
+	try
 	{
-		// Note: testing this now in case some of the background processes forces the read-only mode for a while
-		//       in that case it is better to exit with the check on reentrance (mutex)
-		if (!MetaModel::DBHasAccess(ACCESS_ADMIN_WRITE))
-		{
-			$oP->p("A database maintenance is ongoing (read-only mode even for admins).");
-			$oP->Output();
-			exit -1;
-		}
-
 		CronExec($oP, $aProcesses, $bVerbose);
-
-		$oMutex->Unlock();
 	}
-	else
+	catch(Exception $e)
 	{
-		// Exit silently
-		$oP->p("Already running...");
+		LogError($oP, $e->getMessage()."\n".$e->getTraceAsString());
 	}
+	$res = CMDBSource::QueryToScalar("SELECT RELEASE_LOCK('$sLockName')");
 }
-catch (Exception $e)
+else
 {
-	$oP->p("ERROR: '".$e->getMessage()."'");
-	if ($bDebug)
-	{
-		// Might contain verb parameters such a password...
-		$oP->p($e->getTraceAsString());
-	}
+	// Lock already held by another session
+	// Exit silently
+	$oP->p("Already running...");
 }
-
 $oP->p("Exiting: ".time().' ('.date('Y-m-d H:i:s').')');
 
 $oP->Output();