| 
					
				 | 
			
			
				@@ -12,6 +12,53 @@ import random 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 SERVER_DRIFT = timedelta(seconds=random.vonmisesvariate(1, 4)) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				  
			 | 
		
	
		
			
				 | 
				 | 
			
			
				  
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+class TableLock(object): 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+   """Base class for database table locks. Also works as a NOOP lock.""" 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+   
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+   def __init__(self, table, type="read"): 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        self.table = table 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        self.type = type 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        self.cursor = None 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+   def lock_table(self): 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+       """Lock the table.""" 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+       pass 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+   def unlock_table(self): 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+       """Release previously locked tables.""" 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+       pass 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+   @classmethod 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+   def acquire(cls, table, type=None): 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+       """Acquire table lock.""" 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+       lock = cls(table, type) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+       lock.lock_table() 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+       return lock 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+   def release(self): 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+       """Release the lock.""" 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+       self.unlock_table() 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+       if self.cursor: 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+           self.cursor.close() 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+           self.cursor = None 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+class MySQLTableLock(TableLock): 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    """Table lock support for MySQL.""" 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    def lock_table(self): 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        """Lock MySQL table.""" 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        self.cursor = connection.cursor() 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        self.cursor.execute("LOCK TABLES %s %s" % (self.table, self.type.upper())) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    def unlock_table(self): 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        """Unlock MySQL table.""" 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        self.cursor.execute("UNLOCK TABLES") 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+TABLE_LOCK_FOR_ENGINE = {"mysql": MySQLTableLock} 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+table_lock = TABLE_LOCK_FOR_ENGINE.get(settings.DATABASE_ENGINE, TableLock) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 class TaskManager(models.Manager): 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				     """Manager for :class:`celery.models.Task` models.""" 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				  
			 | 
		
	
	
		
			
				| 
					
				 | 
			
			
				@@ -57,26 +104,6 @@ class TaskManager(models.Manager): 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 class PeriodicTaskManager(models.Manager): 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				     """Manager for :class:`celery.models.PeriodicTask` models.""" 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				  
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-    def lock(self): 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-        """Lock the periodic task table for reading.""" 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-        if settings.DATABASE_ENGINE != "mysql": 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-            return 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-        cursor = connection.cursor() 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-        table = self.model._meta.db_table 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-        cursor.execute("LOCK TABLES %s READ" % table) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-        row = cursor.fetchone() 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-        return row 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				- 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-    def unlock(self): 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-        """Unlock the periodic task table.""" 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-        if settings.DATABASE_ENGINE != "mysql": 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-            return 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-        cursor = connection.cursor() 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-        table = self.model._meta.db_table 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-        cursor.execute("UNLOCK TABLES") 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-        row = cursor.fetchone() 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-        return row 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				- 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				     def init_entries(self): 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				         """Add entries for all registered periodic tasks. 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				  
			 | 
		
	
	
		
			
				| 
					
				 | 
			
			
				@@ -107,6 +134,7 @@ class PeriodicTaskManager(models.Manager): 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				         :returns: list of :class:`celery.models.PeriodicTaskMeta` objects. 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				         """ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				         periodic_tasks = tasks.get_all_periodic() 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        db_table = self.model._meta.db_table 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				  
			 | 
		
	
		
			
				 | 
				 | 
			
			
				         # Find all periodic tasks to be run. 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				         waiting = [] 
			 | 
		
	
	
		
			
				| 
					
				 | 
			
			
				@@ -117,7 +145,7 @@ class PeriodicTaskManager(models.Manager): 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				                 if self.is_time(task_meta.last_run_at, run_every): 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				                     # Get the object again to be sure noone else 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				                     # has already taken care of it. 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                    self.lock() 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                    lock = table_lock.acquire(db_table, "write") 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				                     try: 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				                         secure = self.get(pk=task_meta.pk) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				                         if self.is_time(secure.last_run_at, run_every): 
			 | 
		
	
	
		
			
				| 
					
				 | 
			
			
				@@ -125,5 +153,5 @@ class PeriodicTaskManager(models.Manager): 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				                             secure.save() 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				                             waiting.append(secure) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				                     finally: 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                        self.unlock() 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                        lock.release() 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				         return waiting 
			 |