def process_timer(self, interval, tick, prev="", next=""): """ Call tic() every x seconds. x is defined in self.interval This method is called by TimerService in the interval given in zope.conf. The Default is every 5 seconds. """ if not last_tic_lock.acquire(0): return try: # make sure our skin is set-up. On CMF 1.5 it's setup by acquisition, # but on 2.2 it's by traversal, and our site probably wasn't traversed # by the timerserver request, which goes into the Zope Control_Panel # calling it a second time is a harmless and cheap no-op. # both setupCurrentSkin and REQUEST are acquired from containers. self.setupCurrentSkin(self.REQUEST) # only start when we are the alarmNode alarmNode = self.getAlarmNode() current_node = getCurrentNode() global _check_upgrade if alarmNode == '': self.setAlarmNode(current_node) alarmNode = current_node if alarmNode == current_node: global last_tic now = tick.timeTime() if now - last_tic >= self.interval: self.tic() last_tic = now elif _check_upgrade and self.getServerAddress() == alarmNode: # BBB: check (once per run) if our node was alarm_node by address, and # migrate it. _check_upgrade = False self.setAlarmNode(current_node) finally: last_tic_lock.release()
def getOtherZopeNode(self): from Products.CMFActivity.ActivityTool import getCurrentNode activity_tool = self.portal.portal_activities node_list = list(activity_tool.getProcessingNodeList()) node_list.remove(getCurrentNode()) assert node_list, "this unit test must be run with at least 2 Zope nodes" return node_list[0]
def tic(self, processing_node=1, force=0): processing_node_list = self.getProcessingNodeList() if len(processing_node_list) > 1 and \ getCurrentNode() == self.getDistributingNode(): # Sleep between each distribute. time.sleep(0.3) transaction.commit() transaction.begin() else: self._orig_tic(processing_node, force)
def getOtherZopeNodeList(self, node_count=2): """Wait for at least `node_count` (including the current node) to be registered on portal activities and return the list of their node ids. This aborts current transaction. """ for i in xrange(30): node_list = list( self.portal.portal_activities.getProcessingNodeList()) if len(node_list) >= node_count: node_list.remove(getCurrentNode()) return node_list self.abort() time.sleep(i * 0.1) self.fail("No other activity node registered, make sure you are using" " --activity_node=%s command line flag" % node_count)
def _registerNode(self, distributing, processing): """Register node to process and/or distribute activities""" try: activity_tool = self.portal.portal_activities except AttributeError: from Products.CMFActivity.ActivityTool import ActivityTool activity_tool = ActivityTool().__of__(self.app) currentNode = getCurrentNode() if distributing: activity_tool.manage_setDistributingNode(currentNode) elif currentNode == activity_tool.getDistributingNode(): activity_tool.manage_setDistributingNode('') if processing: activity_tool.manage_addToProcessingList((currentNode, )) else: activity_tool.manage_delNode((currentNode, ))
def testLateInvalidationFromZEO(self): ### Check unit test is run properly from ZEO.ClientStorage import ClientStorage from Products.CMFActivity.ActivityTool import getCurrentNode storage = self.portal._p_jar._storage activity_tool = self.portal.portal_activities node_list = list(activity_tool.getProcessingNodeList()) node_list.remove(getCurrentNode()) assert node_list and isinstance(storage, ClientStorage), \ "this unit test must be run with at least 2 ZEO clients" ### Prepare unit test, to minimize amount of work during critical section ## url to create some content using another zope new_content_url = "http://ERP5TypeTestCase:@%s%s/Folder_create" % ( node_list[0], self.portal.organisation_module.getPath()) ## prepare freeze/unfreeze of ZEO storage zeo_connection = storage._connection socket_map = zeo_connection._map freeze_lock = threading.Lock() freeze_lock.acquire() def unfreezeStorage(): socket_map[zeo_connection.fileno()] = zeo_connection # wake up asyncore loop to take the new socket into account zeo_connection.trigger.pull_trigger() # link to ZEO will be unfrozen 1 second after we read 'message' table unfreeze_timer = threading.Timer(1, unfreezeStorage) unfreeze_timer.setDaemon(True) ## prepare monkey-patches (with code to revert them) from Products.CMFActivity.Activity.SQLDict import SQLDict zeo_server = storage._server def unpatch(): storage._server = zeo_server SQLDict.getProcessableMessageList = SQLDict_getProcessableMessageList SQLDict_getProcessableMessageList = SQLDict.getProcessableMessageList def getProcessableMessageList(*args, **kw): result = SQLDict_getProcessableMessageList(*args, **kw) unpatch() unfreeze_timer.start() return result ### Perform unit test ## we should start without any pending activity self.assertNoPendingMessage() ## monkey-patch ... SQLDict.getProcessableMessageList = getProcessableMessageList try: # prevent nodes from processing activities automatically activity_tool.manage_removeFromProcessingList(node_list) self.commit() del socket_map[zeo_connection.fileno()] try: # wake up asyncore loop and wait we really woke up zeo_connection.trigger.pull_trigger(freeze_lock.release) freeze_lock.acquire() # make sure ZODB is not accessed until we get a message to process storage._server = None # ... monkey-patch done ## create object urllib.urlopen(new_content_url).read() ## validate reindex activity activity_tool.distribute() self.assertEqual(1, len(activity_tool.getMessageList())) ## reindex created object activity_tool.tic() finally: try: unfreeze_timer.join() except RuntimeError: unfreezeStorage() finally: unpatch() activity_tool.manage_addToProcessingList(node_list) self.commit() ## When the bug is not fixed, we get a -3 failed activity self.assertNoPendingMessage()