def test_noWorkDoneWhenConcurrentlyDeleted(self): """ When a L{WorkItem} is concurrently deleted by another transaction, it should I{not} perform its work. """ # Provide access to a method called 'concurrently' everything using original = self.store.newTransaction def decorate(*a, **k): result = original(*a, **k) result.concurrently = self.store.newTransaction return result self.store.newTransaction = decorate def operation(txn): return txn.enqueue(DummyWorkItem, a=30, b=40, workID=5678, deleteOnLoad=1, notBefore=datetime.datetime.utcnow()) proposal = yield inTransaction(self.store.newTransaction, operation) yield proposal.whenExecuted() # Sanity check on the concurrent deletion. def op2(txn): return Select([schema.DUMMY_WORK_ITEM.WORK_ID], From=schema.DUMMY_WORK_ITEM).on(txn) rows = yield inTransaction(self.store.newTransaction, op2) self.assertEquals(rows, []) def op3(txn): return Select([schema.DUMMY_WORK_DONE.WORK_ID, schema.DUMMY_WORK_DONE.A_PLUS_B], From=schema.DUMMY_WORK_DONE).on(txn) rows = yield inTransaction(self.store.newTransaction, op3) self.assertEquals(rows, [])
def deschema(): @inlineCallbacks def deletestuff(txn): for stmt in dropSQL: yield txn.execSQL(stmt) return inTransaction(lambda *a: self.store.newTransaction(*a), deletestuff)
def test_noWorkDoneWhenConcurrentlyDeleted(self): """ When a L{WorkItem} is concurrently deleted by another transaction, it should I{not} perform its work. """ # Provide access to a method called "concurrently" everything using original = self.store.newTransaction def decorate(*a, **k): result = original(*a, **k) result.concurrently = self.store.newTransaction return result self.store.newTransaction = decorate def operation(txn): return txn.enqueue(DummyWorkItem, a=30, b=40, workID=5678, deleteOnLoad=1, notBefore=datetime.datetime.utcnow()) proposal = yield inTransaction(self.store.newTransaction, operation) yield proposal.whenExecuted() # Sanity check on the concurrent deletion. def op2(txn): return Select([schema.DUMMY_WORK_ITEM.WORK_ID], From=schema.DUMMY_WORK_ITEM).on(txn) rows = yield inTransaction(self.store.newTransaction, op2) self.assertEquals(list(rows), []) def op3(txn): return Select([ schema.DUMMY_WORK_DONE.WORK_ID, schema.DUMMY_WORK_DONE.A_PLUS_B, ], From=schema.DUMMY_WORK_DONE).on(txn) rows = yield inTransaction(self.store.newTransaction, op3) self.assertEquals(list(rows), [])
def test_currentNodeInfo(self): """ There will be two C{NODE_INFO} rows in the database, retrievable as two L{NodeInfo} objects, once both nodes have started up. """ @inlineCallbacks def check(txn): self.assertEquals(len((yield self.node1.activeNodes(txn))), 2) self.assertEquals(len((yield self.node2.activeNodes(txn))), 2) return inTransaction(self.store.newTransaction, check)
def test_enqueueHappyPath(self): """ When a L{WorkItem} is scheduled for execution via L{PeerConnectionPool.enqueueWork} its C{doWork} method will be invoked by the time the L{Deferred} returned from the resulting L{WorkProposal}'s C{whenExecuted} method has fired. """ # TODO: this exact test should run against LocalQueuer as well. def operation(txn): # TODO: how does 'enqueue' get associated with the transaction? This # is not the fact with a raw t.w.enterprise transaction. Should # probably do something with components. return txn.enqueue(DummyWorkItem, a=3, b=4, workID=4321, notBefore=datetime.datetime.utcnow()) result = yield inTransaction(self.store.newTransaction, operation) # Wait for it to be executed. Hopefully this does not time out :-\. yield result.whenExecuted() def op2(txn): return Select([schema.DUMMY_WORK_DONE.WORK_ID, schema.DUMMY_WORK_DONE.A_PLUS_B], From=schema.DUMMY_WORK_DONE).on(txn) rows = yield inTransaction(self.store.newTransaction, op2) self.assertEquals(rows, [[4321, 7]])
def setUp(self): """ L{PeerConnectionPool} requires access to a database and the reactor. """ self.store = yield buildStore(self, None) def doit(txn): return txn.execSQL(schemaText) yield inTransaction(lambda: self.store.newTransaction("bonus schema"), doit) def indirectedTransactionFactory(*a): """ Allow tests to replace 'self.store.newTransaction' to provide fixtures with extra methods on a test-by-test basis. """ return self.store.newTransaction(*a) def deschema(): @inlineCallbacks def deletestuff(txn): for stmt in dropSQL: yield txn.execSQL(stmt) return inTransaction(lambda *a: self.store.newTransaction(*a), deletestuff) self.addCleanup(deschema) from twisted.internet import reactor self.node1 = PeerConnectionPool( reactor, indirectedTransactionFactory, 0, schema) self.node2 = PeerConnectionPool( reactor, indirectedTransactionFactory, 0, schema) class FireMeService(Service, object): def __init__(self, d): super(FireMeService, self).__init__() self.d = d def startService(self): self.d.callback(None) d1 = Deferred() d2 = Deferred() FireMeService(d1).setServiceParent(self.node1) FireMeService(d2).setServiceParent(self.node2) ms = MultiService() self.node1.setServiceParent(ms) self.node2.setServiceParent(ms) ms.startService() self.addCleanup(ms.stopService) yield gatherResults([d1, d2]) self.store.queuer = self.node1
def test_inTransactionSuccess(self): """ L{inTransaction} invokes its C{transactionCreator} argument, and then returns a L{Deferred} which fires with the result of its C{operation} argument when it succeeds. """ class faketxn(object): def __init__(self): self.commits = [] self.aborts = [] def commit(self): self.commits.append(Deferred()) return self.commits[-1] def abort(self): self.aborts.append(Deferred()) return self.aborts[-1] createdTxns = [] def createTxn(): createdTxns.append(faketxn()) return createdTxns[-1] dfrs = [] def operation(t): self.assertIdentical(t, createdTxns[-1]) dfrs.append(Deferred()) return dfrs[-1] d = inTransaction(createTxn, operation) x = [] d.addCallback(x.append) self.assertEquals(x, []) self.assertEquals(len(dfrs), 1) dfrs[0].callback(35) # Commit in progress, so still no result... self.assertEquals(x, []) createdTxns[0].commits[0].callback(42) # Committed, everything's done. self.assertEquals(x, [35])
def generateSchedulingResponses(self): def failForRecipient(recipient): err = HTTPError(ErrorResponse( responsecode.FORBIDDEN, (caldav_namespace, "recipient-failed"), "iMIP request failed", )) self.responses.add( recipient.cuaddr, Failure(exc_value=err), reqstatus=iTIPRequestStatus.SERVICE_UNAVAILABLE, suppressErrorLog=True ) # Generate an HTTP client request try: # We do not do freebusy requests via iMIP if self.freebusy: raise ValueError("iMIP VFREEBUSY requests not supported.") method = self.scheduler.calendar.propertyValue("METHOD") if method not in ( "PUBLISH", "REQUEST", "REPLY", "ADD", "CANCEL", "DECLINE_COUNTER", ): log.info("Could not do server-to-imip method: %s" % (method,)) for recipient in self.recipients: err = HTTPError(ErrorResponse( responsecode.FORBIDDEN, (caldav_namespace, "recipient-failed"), "iMIP method not allowed: %s" % (method,), )) self.responses.add( recipient.cuaddr, Failure(exc_value=err), reqstatus=iTIPRequestStatus.NO_USER_SUPPORT ) returnValue(None) caldata = str(self.scheduler.calendar) for recipient in self.recipients: try: toAddr = str(recipient.cuaddr) if not toAddr.lower().startswith("mailto:"): raise ValueError("ATTENDEE address '%s' must be mailto: for iMIP operation." % (toAddr,)) fromAddr = str(self.scheduler.originator.cuaddr) log.debug("Submitting iMIP message... To: '%s', From :'%s'\n%s" % (toAddr, fromAddr, caldata,)) def enqueueOp(txn): return txn.enqueue(IMIPInvitationWork, fromAddr=fromAddr, toAddr=toAddr, icalendarText=caldata) yield inTransaction( lambda: self.scheduler.txn.store().newTransaction( "Submitting iMIP message for UID: %s" % ( self.scheduler.calendar.resourceUID(),)), enqueueOp ) except Exception, e: # Generated failed response for this recipient log.debug("iMIP request %s failed for recipient %s: %s" % (self, recipient, e)) failForRecipient(recipient) else: self.responses.add( recipient.cuaddr, responsecode.OK, reqstatus=iTIPRequestStatus.MESSAGE_SENT ) except Exception, e: # Generated failed responses for each recipient log.debug("iMIP request %s failed: %s" % (self, e)) for recipient in self.recipients: failForRecipient(recipient)
def thunk(operation): return inTransaction(transactionCreator, operation)
def setUp(self): """ L{PeerConnectionPool} requires access to a database and the reactor. """ self.store = yield buildStore(self, None) @inlineCallbacks def doit(txn): for statement in splitSQLString(nodeSchema + schemaText): yield txn.execSQL(statement) yield inTransaction(lambda: self.store.newTransaction("bonus schema"), doit) def indirectedTransactionFactory(*a): """ Allow tests to replace "self.store.newTransaction" to provide fixtures with extra methods on a test-by-test basis. """ return self.store.newTransaction(*a) def deschema(): @inlineCallbacks def deletestuff(txn): for stmt in dropSQL: yield txn.execSQL(stmt) txn.execSQL("drop table node_info") return inTransaction(lambda *a: self.store.newTransaction(*a), deletestuff) self.addCleanup(deschema) from twisted.internet import reactor self.node1 = PeerConnectionPool(reactor, indirectedTransactionFactory, 0, schema) self.node2 = PeerConnectionPool(reactor, indirectedTransactionFactory, 0, schema) class FireMeService(Service, object): def __init__(self, d): super(FireMeService, self).__init__() self.d = d def startService(self): self.d.callback(None) d1 = Deferred() d2 = Deferred() FireMeService(d1).setServiceParent(self.node1) FireMeService(d2).setServiceParent(self.node2) ms = MultiService() self.node1.setServiceParent(ms) self.node2.setServiceParent(ms) ms.startService() @inlineCallbacks def _clean(): yield ms.stopService() self.flushLoggedErrors(CancelledError) self.addCleanup(_clean) yield gatherResults([d1, d2]) self.store.queuer = self.node1