{"id":74,"date":"2011-05-04T12:30:37","date_gmt":"2011-05-04T10:30:37","guid":{"rendered":"http:\/\/blog.gocept.com\/?p=74"},"modified":"2012-12-03T15:11:59","modified_gmt":"2012-12-03T14:11:59","slug":"how-to-undo-a-transaction-with-the-zodb","status":"publish","type":"post","link":"https:\/\/blog.gocept.com\/2011\/05\/04\/how-to-undo-a-transaction-with-the-zodb\/","title":{"rendered":"How-To: Undo a transaction with the ZODB"},"content":{"rendered":"
Suppose you’ve written a script to “fix something real quick” and unleashed it upon your live database. Five minutes later, you discover your script had a bug, and now you’ve wrecked quite a bit of production data. Ouch.<\/p>\n
You might be lucky, though, since the ZODB offers transaction-level undo. This comes with a lot of caveats, though, the biggest being that if something else<\/em> was changed in the meantime that causes the undo to conflict, it won’t work. (Before transaction X, some value was A which X changed to B, but later something changes it to C. If I now want to undo transaction X to get back to A, it will conflict. Catalogs and other shared state are prime candidates).<\/p>\n But you still might be lucky and there won’t be a conflict. So, how do you undo a transaction? First, you need to find the transaction. In my case, I knew an object that had been changed by my script. So I asked the ZODB for the history of that object, i.e. the last transaction(s) that changed it:<\/p>\n Now I have the offending transaction’s ID. However, the undo() API does not work with transaction ids but needs a special (storage-specific) identifier. And, since as far as I can tell there is no way to map a transaction id to an “undo id”, I had to make to by matching the time stamp:<\/p>\n Finally, call undo and hope you don’t get a conflict upon committing:<\/p>\n>>> db = root._p_jar.db()\r\n>>> hist = db.history(my_changed_object._p_oid)\r\n>>> hist\r\n[{'tid': '\\x03...', 'size': 123, 'user_name': '', 'description': '', 'time': 1304493667.320477}]<\/pre>\n
>>> info = db.undoInfo(specification=dict(time=hist['time']))\r\n>>> info\r\n[{'id': 'A44XmR876bs=', 'time': 1304493667.320477, 'user_name': '', 'description': '', 'size': 315893}]<\/pre>\n
>>> db.undo(info['id'])\r\n>>> import transaction\r\n>>> transaction.commit()<\/pre>\n","protected":false},"excerpt":{"rendered":"