{"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

>>> 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

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

>>> 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

Finally, call undo and hope you don’t get a conflict upon committing:<\/p>\n

>>> db.undo(info['id'])\r\n>>> import transaction\r\n>>> transaction.commit()<\/pre>\n","protected":false},"excerpt":{"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. You might be lucky, though, since the ZODB offers transaction-level undo. This comes with a lot of … Continue reading “How-To: Undo a transaction with the ZODB”<\/span><\/a><\/p>\n","protected":false},"author":10315341,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"_coblocks_attr":"","_coblocks_dimensions":"","_coblocks_responsive_height":"","_coblocks_accordion_ie_support":"","advanced_seo_description":"","jetpack_seo_html_title":"","jetpack_seo_noindex":false,"jetpack_post_was_ever_published":false,"_jetpack_newsletter_access":"","_jetpack_newsletter_tier_id":0,"footnotes":"","jetpack_publicize_message":"","jetpack_is_tweetstorm":false,"jetpack_publicize_feature_enabled":true,"jetpack_social_post_already_shared":false,"jetpack_social_options":{"image_generator_settings":{"template":"highway","enabled":false}}},"categories":[10221],"tags":[],"jetpack_publicize_connections":[],"jetpack_featured_media_url":"","jetpack_likes_enabled":true,"jetpack_sharing_enabled":true,"jetpack_shortlink":"https:\/\/wp.me\/pFP3y-1c","jetpack-related-posts":[{"id":3334,"url":"https:\/\/blog.gocept.com\/2019\/06\/14\/undo-transactions-by-truncating-zodb-data-fs\/","url_meta":{"origin":74,"position":0},"title":"Undo transactions by truncating ZODB Data.fs","author":"Michael Howitz","date":"June 14, 2019","format":false,"excerpt":"truncate can be used to permanently set back a ZODB Data.fs to a certain point in transaction history.","rel":"","context":"In "en"","block_context":{"text":"en","link":"https:\/\/blog.gocept.com\/category\/en\/"},"img":{"alt_text":"","src":"https:\/\/i0.wp.com\/blog.gocept.com\/wp-content\/uploads\/2019\/06\/cord-4088055_1920.jpg?fit=1200%2C800&ssl=1&resize=350%2C200","width":350,"height":200,"srcset":"https:\/\/i0.wp.com\/blog.gocept.com\/wp-content\/uploads\/2019\/06\/cord-4088055_1920.jpg?fit=1200%2C800&ssl=1&resize=350%2C200 1x, https:\/\/i0.wp.com\/blog.gocept.com\/wp-content\/uploads\/2019\/06\/cord-4088055_1920.jpg?fit=1200%2C800&ssl=1&resize=525%2C300 1.5x, https:\/\/i0.wp.com\/blog.gocept.com\/wp-content\/uploads\/2019\/06\/cord-4088055_1920.jpg?fit=1200%2C800&ssl=1&resize=700%2C400 2x, https:\/\/i0.wp.com\/blog.gocept.com\/wp-content\/uploads\/2019\/06\/cord-4088055_1920.jpg?fit=1200%2C800&ssl=1&resize=1050%2C600 3x"},"classes":[]},{"id":3210,"url":"https:\/\/blog.gocept.com\/2018\/02\/23\/zope-undo-transactions-via-debug-console\/","url_meta":{"origin":74,"position":1},"title":"Zope \u2013 undo transactions via debug console","author":"Michael Howitz","date":"February 23, 2018","format":false,"excerpt":"If you cannot access the root level in Zope 2 via the browser but you are able to use the debug console you have enough to undo transactions. Start the debug console: $ bin\/zinstance debug List the transaction descriptions, user names and ids of the last 10 transactions: >>> from\u2026","rel":"","context":"In "en"","block_context":{"text":"en","link":"https:\/\/blog.gocept.com\/category\/en\/"},"img":{"alt_text":"","src":"","width":0,"height":0},"classes":[]},{"id":1540,"url":"https:\/\/blog.gocept.com\/2016\/09\/02\/zodb-py3migrate-1\/","url_meta":{"origin":74,"position":2},"title":"zodb.py3migrate: Migrate an existing ZODB Data.fs to be used with Python 3","author":"Michael Howitz","date":"September 2, 2016","format":false,"excerpt":"At\u00a0Berlin Strategic sprint 2016\u00a0we\u00a0developed a tool to analyze a ZODB Filestorage to find\u00a0Python 2 string objects. If\u00a0they are in an encoding besides ASCII this is\u00a0preventing using this Filestorage with Python 3 because of decoding errors arising on loading the pickles. The tool is even able to convert those strings either\u2026","rel":"","context":"In "en"","block_context":{"text":"en","link":"https:\/\/blog.gocept.com\/category\/en\/"},"img":{"alt_text":"","src":"","width":0,"height":0},"classes":[]},{"id":171,"url":"https:\/\/blog.gocept.com\/2012\/05\/22\/dont-stop-postgresqls-autovacuum-with-your-application\/","url_meta":{"origin":74,"position":3},"title":"Don’t stop PostgreSQL’s autovacuum with your application","author":"","date":"May 22, 2012","format":false,"excerpt":"The problem Some weeks ago, we received a complaint from a customer about bad PostgreSQL performance for a specific application. I took a look into the database and found strange things going on: the query planner was executing \"interesting\" query plans, tables were bloated with lots of dead rows (one\u2026","rel":"","context":"In "en"","block_context":{"text":"en","link":"https:\/\/blog.gocept.com\/category\/en\/"},"img":{"alt_text":"","src":"","width":0,"height":0},"classes":[]},{"id":3229,"url":"https:\/\/blog.gocept.com\/2018\/06\/07\/migrate-a-zope-zodb-data-fs-to-python-3\/","url_meta":{"origin":74,"position":4},"title":"Migrate a Zope ZODB Data.fs to Python 3","author":"Michael Howitz","date":"June 7, 2018","format":false,"excerpt":"TL;DR Use\u00a0zodbupdate. Problem A ZODB\u00a0Data.fs\u00a0which was created under Python 2 cannot be opened under Python 3. This is prevented by using a different magic code in the first bytes of the file. This is done on purpose because str\u00a0has a different meaning for the two Python versions: Under Python 2\u2026","rel":"","context":"In "en"","block_context":{"text":"en","link":"https:\/\/blog.gocept.com\/category\/en\/"},"img":{"alt_text":"","src":"https:\/\/i0.wp.com\/blog.gocept.com\/wp-content\/uploads\/2018\/06\/spring-3383890_1280.jpg?fit=1200%2C797&ssl=1&resize=350%2C200","width":350,"height":200,"srcset":"https:\/\/i0.wp.com\/blog.gocept.com\/wp-content\/uploads\/2018\/06\/spring-3383890_1280.jpg?fit=1200%2C797&ssl=1&resize=350%2C200 1x, https:\/\/i0.wp.com\/blog.gocept.com\/wp-content\/uploads\/2018\/06\/spring-3383890_1280.jpg?fit=1200%2C797&ssl=1&resize=525%2C300 1.5x, https:\/\/i0.wp.com\/blog.gocept.com\/wp-content\/uploads\/2018\/06\/spring-3383890_1280.jpg?fit=1200%2C797&ssl=1&resize=700%2C400 2x, https:\/\/i0.wp.com\/blog.gocept.com\/wp-content\/uploads\/2018\/06\/spring-3383890_1280.jpg?fit=1200%2C797&ssl=1&resize=1050%2C600 3x"},"classes":[]},{"id":2789,"url":"https:\/\/blog.gocept.com\/2017\/10\/25\/testing-zope-4beta2\/","url_meta":{"origin":74,"position":5},"title":"Migrating from Zope 2.13 to 4.0b2","author":"Michael Howitz","date":"October 25, 2017","format":false,"excerpt":"Beta-Testing Zope 4 together with PerFact Innovation","rel":"","context":"In "en"","block_context":{"text":"en","link":"https:\/\/blog.gocept.com\/category\/en\/"},"img":{"alt_text":"","src":"","width":0,"height":0},"classes":[]}],"_links":{"self":[{"href":"https:\/\/blog.gocept.com\/wp-json\/wp\/v2\/posts\/74"}],"collection":[{"href":"https:\/\/blog.gocept.com\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/blog.gocept.com\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/blog.gocept.com\/wp-json\/wp\/v2\/users\/10315341"}],"replies":[{"embeddable":true,"href":"https:\/\/blog.gocept.com\/wp-json\/wp\/v2\/comments?post=74"}],"version-history":[{"count":4,"href":"https:\/\/blog.gocept.com\/wp-json\/wp\/v2\/posts\/74\/revisions"}],"predecessor-version":[{"id":339,"href":"https:\/\/blog.gocept.com\/wp-json\/wp\/v2\/posts\/74\/revisions\/339"}],"wp:attachment":[{"href":"https:\/\/blog.gocept.com\/wp-json\/wp\/v2\/media?parent=74"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/blog.gocept.com\/wp-json\/wp\/v2\/categories?post=74"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/blog.gocept.com\/wp-json\/wp\/v2\/tags?post=74"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}