Testing Database Transactions in Go
marvinblum.de
marvinblum.de
It’s actually really hard to test that your DB is using transactions correctly, since the error cases tend to be races which don’t show up under low load.
We built a prototype tool in Python where you can monkeypatch the transaction context manager, and pause the primary thread’s execution just before committing, so that you can then do evil stuff like running a competing thread of the same or other DB operations to try to break invariants. But even this won’t catch everything; there is a combinatorial explosion of test points and you can’t compare them all in a large app.
I haven’t seen any folks writing about this, another approach would be to wire up some him thing smarter like Jepsen to direct the anomaly search, Kyle said some folks have reported doing this but nobody has published. I’m interested to know if anyone has had success with this sort of transaction/correctness testing.
IMO the standard library should provide something that wraps a `Tx` and a `Context` together, as usually I want every statement issued from a transaction bounded in lifetime by the same one I provided to `BeginTx`. This would provide even more incentive to pass around `Tx`s rather than `DB`s, since those functions also often need a context anyway.
go test -p 1
Also, regarding writing tests against real PostgreSQL databases, isolated per test and without losing speed, I can recommend to take a look at https://github.com/allaboutapps/integresql - disclaimer, we are the authors.
I'm limiting the open connections at the moment and using the default Goland linter. It won't show you these kind of errors though and I just remembered that I saw it somewhere else, but I don't remember where it was :D
It is quite similar to other Go concepts. For example, you don't want to have circular dependencies between channels. The difference is that such a thing would fail very quickly whereas dependent DB connections would only fail after the connections are exhausted.
I don't think there's necessarily anything "special" about this, I think it's just a case where there's a lot of people with years or even decades of experience in that context, and it simply doesn't immediately occur to them that in a higher-concurrency world they need to modify these skills.
I suspect there's also rather a lot of database + application combinations out in the real world that, by coincidence and a bit of hacking around problems as they arise, "just happen to work" with the highly characteristic access patterns used by the web pages that can access the DB and the transaction isolation settings in the DB. Using any more concurrent and looser access to the DB is likely to expose a lot of problems that in some sense existed all along, but were just never quite uncovered before with the old access patterns.
(It isn't really Go. Threading an old-school C program will raise the same issues, or going async in a scripting language.)
https://medium.com/@dekugelschieber/testing-database-transac...
I'm not a fan of Go because it encourages hacks like this. The language is not expressive enough to handle database transactions properly. So it's suggested to try to catch bad situations with a linter instead.
What a timeless Goland solution to the problem. They should put "fix it with a linter" on hats and sell them at Go rallies. You could make billions
I'm not sure about the word "interceptor" here because in Go and every other language I know which does this it is merely a wrapper, but Go does this. From some vantage, the problem only arises because Go does this - if it instead forced you to create a transaction the resource allocation, and so possibility of exhaustion, would be a lot more obvious.