I am using adbapi on a cyclone web server. First, my handler writes some things to the SQL database, and then makes an HTTP request to another web server. If this HTTP request failed, I want the database transaction to be rolled back. However, I do not get such an effect. Reviewing the documentation, he says
The function will be called in a thread with twisted.enterprise.adbapi.Transaction, which basically mimics the DB-API cursor. In all cases, the database transaction will be completed after your use of the database is completed, unless the exception is in which case it will be rolled back.
This is not an exact statement, as we would like. At what point does my "database usage" exactly end? This is when the self.finish () method of the handler is called? When is the method passed to ConnectionPool.runInteraction () completed?
Here is my code
class AccountCreationHandler(BaseRequestHandler): @cyclone.web.asynchronous def post(self, accessKey, *args, **kwargs): try: d = connPool.runInteraction(self.saveStuffToDatabase) d.addCallback(self.callWebServer) d.addCallback(self.formatResult) d.addErrback(self.handleFailure) except Exception, e: self.handleException(e) def saveStuffToDatabase(self, txn): txn.execute("INSERT INTO Table1 (f1) VALUES ('v1')") def callWebServer(self): agent = Agent(reactor) hdrs = Headers({ "Content-type": ["application/json"] }) values = json.dumps({ "someField": 123 }) body = SimpleProducer(values) url = "http://somewebserver.com" d = agent.request("POST", url, hdrs, body) d.addCallback(self.handleWebResponse) return d def handleWebResponse(self, response): if response.code == 200: d = Deferred() receiver = SimpleReceiver(d) response.deliverBody(receiver) d.addCallback(self.saveWebServerResults) return d else: raise Exception("web server failed with http status code %d" % response.code) def saveWebServerResults(self, body): self.results = body def formatResult(self): self.finish(self.results) class SimpleProducer(object): implements(IBodyProducer) def __init__(self, body): self.body = body self.length = len(body) def startProducing(self, consumer): consumer.write(self.body) return succeed(None) def pauseProducing(self): pass def stopProducing(self): pass class SimpleReceiver(Protocol): def __init__(self, d): self.buf = '' self.d = d def dataReceived(self, data): self.buf += data def connectionLost(self, reason): if type(reason.value) == ResponseDone: self.d.callback(self.buf) else: self.d.errback(reason)
If the web server displays an error message or the connection to it expires, or mainly if the code passes the saveStuffToDatabase method, nothing is rolled back when an error occurs.
I assume that this means that the transaction was completed when the method passed to ConnectionPool.runInteraction () completed without exception. If in this case, I think, then I would have to include everything all the time, including calling the web server inside saveStuffToDatabase ()?