| ▲ | throwaway2ge5hg 10 hours ago | ||||||||||||||||
Postgres has SERIALIZABLE transaction isolation level. Just use it and then you never have to worry about any of these race conditions. And if for some reason you refuse to, then this "barrier" or "hooks" approach to testing will in practice not help. It requires you to already know the potential race conditions, but if you are already aware of them then you will already write your code to avoid them. It is the non-obvious race conditions that should scare you. To find these, you should use randomized testing that runs many iterations of different interleavings of transaction steps. You can build such a framework that will hook directly into your individual DB query calls. Then you don't have to add any "hooks" at all. But even that won't find all race condition bugs, because it is possible to have race conditions surface even within a single database query. You really should just use SERIALIZABLE and save yourself all the hassle and effort and spending hours writing all these tests. | |||||||||||||||||
| ▲ | LgWoodenBadger 5 hours ago | parent | next [-] | ||||||||||||||||
“because it is possible to have race conditions surface even within a single database query.” This brought back awful memories of MS SQLServer and JDBC. Way back when, maybe Java 1.5 or so, SQLServer would deadlock between connections when all they were doing was executing the exact same statement. Literally. Not the same general statement with different parameters. | |||||||||||||||||
| ▲ | danielheath 8 hours ago | parent | prev | next [-] | ||||||||||||||||
SERIALIZABLE is really quite hard to retrofit to existing apps; deadlocks, livelocks, and “it’s slow” show up all over the place when you switch it on. Definitely recommend starting new codebases with it enabled everywhere. | |||||||||||||||||
| |||||||||||||||||
| ▲ | lirbank 10 hours ago | parent | prev | next [-] | ||||||||||||||||
Good call, SERIALIZABLE is a strong option - it eliminates a whole class of bugs at the isolation level. The trade-off is your app needs to handle serialization failures with retry logic, which introduces its own complexity. That retry logic itself needs testing, and barriers work for that too. On randomized testing - that actually has the same limitation you mentioned about barriers: you need to know where to point it. And without coordination, the odds of two operations overlapping at exactly the wrong moment are slim. You'd need enormous pressure to trigger the race reliably, and even then a passing run doesn't prove much. Barriers make the interleaving deterministic so a pass actually means something. | |||||||||||||||||
| ▲ | theptip 6 hours ago | parent | prev [-] | ||||||||||||||||
Not a silver bullet. When you use serializable you have more opportunities for deadlocks (cases which would otherwise be logic errors at weaker isolation levels). Serializable just means that within the transaction your logic can naively assume it’s single threaded. It doesn’t magically solve distributed system design for you. “Just use random testing” isn’t really an answer. Some race conditions only show up with pathological delays on one thread. | |||||||||||||||||