def mergeJoinDispatch(self, event, obj): """ Performs a merge join on the pending fan-in dispatches. @param event: an event that is being merge joined (destination state must be a fan in) @return: a list (possibly empty) of FSMContext instances """ # this assertion comes from _queueDispatchFanIn - we never want fan-out info in a fan-in context assert not self.get(constants.GEN_PARAM) assert not self.get(constants.FORK_PARAM) # the work package index is stored in the url of the Task/FSMContext index = self.get(constants.INDEX_PARAM) self.logger.debug('Index: %s', index) taskNameBase = self.getTaskName(event, fanIn=True) # see comment (***) in self._queueDispatchFanIn # # in the case of failing to acquire a read lock (due to failed release of write lock) # we have decided to keep retrying raiseOnFail = False if self._getTaskRetryLimit() is not None: raiseOnFail = (self._getTaskRetryLimit() > self.__obj[constants.RETRY_COUNT_PARAM]) rwlock = ReadWriteLock(taskNameBase, self) rwlock.acquireReadLock(index, raiseOnFail=raiseOnFail) # and return the FSMContexts list class FSMContextList(list): """ A list that supports .logger.info(), .logger.warning() etc.for fan-in actions """ def __init__(self, context, contexts, guarded=False): """ setup a self.logger for fan-in actions """ super(FSMContextList, self).__init__(contexts) self.logger = Logger(context) self.instanceName = context.instanceName self.guarded = guarded # see comment (A) in self._queueDispatchFanIn(...) time.sleep(constants.DATASTORE_ASYNCRONOUS_INDEX_WRITE_WAIT_TIME) # the following step ensure that fan-in only ever operates one time over a list of data # the entity is created in State.dispatch(...) _after_ all the actions have executed # successfully khash = knuthHash(index) self.logger.debug('knuthHash of index: %s', khash) workIndex = '%s-%d' % (taskNameBase, khash) if obj[constants.RETRY_COUNT_PARAM] > 0: semaphore = RunOnceSemaphore(workIndex, self) if semaphore.readRunOnceSemaphore(payload=self.__obj[constants.TASK_NAME_PARAM]): self.logger.info("Fan-in idempotency guard for workIndex '%s', not processing any work items.", workIndex) return FSMContextList(self, [], guarded=True) # don't operate over the data again # fetch all the work packages in the current group for processing query = _FantasmFanIn.all(namespace='') \ .filter('workIndex =', workIndex) \ .order('__key__') # construct a list of FSMContexts contexts = [self.clone(replaceData=r.context) for r in query] return FSMContextList(self, contexts)
def test_acquireReadLock_before_acquireWriteLock(self): lock = ReadWriteLock('foo', self.context) index = lock.currentIndex() self.assertEqual(None, memcache.get(lock.lockKey(index))) lock.acquireReadLock(index) self.assertEqual(None, memcache.get(lock.lockKey(index))) self.assertEqual([], self.loggingDouble.messages['debug']) self.assertEqual([], self.loggingDouble.messages['critical'])
def test_acquireWriteLock_failure(self): lock = ReadWriteLock('foo', self.context) index = lock.currentIndex() self.assertEqual(None, memcache.get(lock.lockKey(index))) lock.acquireWriteLock(index) # need to call before acquireReadLock self.assertEqual(65537, memcache.get(lock.lockKey(index))) lock.acquireReadLock(index) self.assertEqual(32769, memcache.get(lock.lockKey(index))) self.assertRaises(FanInWriteLockFailureRuntimeError, lock.acquireWriteLock, index)
def test_acquireWriteLock_failure(self): lock = ReadWriteLock('foo', self.context) index = lock.currentIndex() self.assertEqual(None, memcache.get(lock.lockKey(index))) lock.acquireWriteLock(index) # need to call before acquireReadLock self.assertEqual('65537', memcache.get(lock.lockKey(index))) lock.acquireReadLock(index) self.assertEqual('32769', memcache.get(lock.lockKey(index))) self.assertRaises(FanInWriteLockFailureRuntimeError, lock.acquireWriteLock, index)
def test_acquireReadLock_gave_up(self): lock = ReadWriteLock('foo', self.context) index = lock.currentIndex() self.assertEqual(None, memcache.get(lock.lockKey(index))) lock.acquireWriteLock(index) self.assertEqual(65537, memcache.get(lock.lockKey(index))) lock.acquireReadLock(index) self.assertEqual(32769, memcache.get(lock.lockKey(index))) self.assertEqual(["Tried to acquire read lock 'foo-lock-3626764237' 1 times...", "Tried to acquire read lock 'foo-lock-3626764237' 2 times..."], self.loggingDouble.messages['debug']) self.assertEqual(["Gave up waiting for all fan-in work items with read lock 'foo-lock-3626764237'."], self.loggingDouble.messages['critical'])
def test_acquireReadLock_gave_up(self): lock = ReadWriteLock('foo', self.context) index = lock.currentIndex() self.assertEqual(None, memcache.get(lock.lockKey(index))) lock.acquireWriteLock(index) self.assertEqual('65537', memcache.get(lock.lockKey(index))) lock.acquireReadLock(index) self.assertEqual('32769', memcache.get(lock.lockKey(index))) self.assertEqual([ "Tried to acquire read lock 'foo-lock-3626764237' 1 times...", "Tried to acquire read lock 'foo-lock-3626764237' 2 times..." ], self.loggingDouble.messages['debug']) self.assertEqual([ "Gave up waiting for all fan-in work items with read lock 'foo-lock-3626764237'." ], self.loggingDouble.messages['critical'])
def test_acquireReadLock_one_wait_iter(self): lock = ReadWriteLock('foo', self.context) index = lock.currentIndex() self.assertEqual(None, memcache.get(lock.lockKey(index))) lock.acquireWriteLock(index) self.assertEqual(65537, memcache.get(lock.lockKey(index))) def sleepAndRelease(seconds): # pylint: disable=W0613 lock.releaseWriteLock(index) mock('time.sleep', returns_func=sleepAndRelease, tracker=None) lock.acquireReadLock(index) self.assertEqual(32768, memcache.get(lock.lockKey(index))) self.assertEqual(["Tried to acquire read lock 'foo-lock-3626764237' 1 times..."], self.loggingDouble.messages['debug']) self.assertEqual(["Gave up waiting for all fan-in work items with read lock 'foo-lock-3626764237'."], self.loggingDouble.messages['critical'])
def test_acquireReadLock_one_wait_iter(self): lock = ReadWriteLock('foo', self.context) index = lock.currentIndex() self.assertEqual(None, memcache.get(lock.lockKey(index))) lock.acquireWriteLock(index) self.assertEqual('65537', memcache.get(lock.lockKey(index))) def sleepAndRelease(seconds): # pylint: disable-msg=W0613 lock.releaseWriteLock(index) mock('time.sleep', returns_func=sleepAndRelease, tracker=None) lock.acquireReadLock(index) self.assertEqual('32768', memcache.get(lock.lockKey(index))) self.assertEqual( ["Tried to acquire read lock 'foo-lock-3626764237' 1 times..."], self.loggingDouble.messages['debug']) self.assertEqual([ "Gave up waiting for all fan-in work items with read lock 'foo-lock-3626764237'." ], self.loggingDouble.messages['critical'])