It turns out that transactions by themselves do not guarantee atomicity. This blog post has some great info: https://blog.2ndquadrant.com/postgresql-anti-patterns-read-modify-write-cycles/