A Two Month Debugging Story
kev.inburke.com
kev.inburke.com
The proposed solution to manually nuke the database state seems crazy to me. Some alternatives:
1. Run the entire test in a transaction, do flushes and assert as normal. At the end, ROLLBACK instead of COMMIT and now you have a pristine database again. [1]
2. Setup pristine DB state once and then use `CREATE DATABASE ... WITH TEMPLATE ...` to create a temporary database. Not sure what the perf hit is, but it's probably worth trying. [2]
[1] http://alextechrants.blogspot.ca/2013/08/unit-testing-sqlalc...
[2] https://www.postgresql.org/docs/9.4/static/manage-ag-templat... CREATE DATABASE actually works by copying an existing database. By default, it copies the standard system database named template1.
We wrote our own transaction library and have been shifting queries over to it/a saner ORM. https://github.com/shyp/pg-transactions.
I've never heard of database templates, I'll have to take a look.
You even mention in the Waterline section of that blog post that you've written your own transaction library, so this shouldn't be too hard.
> Waterline queries are case insensitive; that is, Users.find().where(name: 'FOO') will turn into SELECT * FROM users WHERE name = LOWER('FOO');. There's no way to turn this off. If you ask Sails to generate an index for you, it will place the index on the uppercased column name, so your queries will miss it. If you generate the index yourself, you pretty much have to use the lowercased column value & force every other query against the database to use that as well.
What the actual fuck!
If it were me I'd want to take the problem somewhere I _could_ get root access and debug it properly. So I'd be interested to know what value this (unnamed) CI platform provides to make it worth a wild goose chase.
Test technology surely doesn't need that level of secret sauce that you need a hosted service - after two months I'd definitely want another way of doing it.
If you insist on tests that touch the DB, the speed can be improved by chaining tests instead of resetting the DB everytime (this has its trade offs).
If I need to test a piece of code that generates a dynamic SQL statement, I would simply assert that the correct SQL is generated. I would not need to actually execute the SQL to see that it is correct. The point is to test that my logic generated the correct SQL, not to test that my database vendor implements SQL correctly. The latter would just be caught in manual testing. I like the BDD school of thought that you are writing specs, not tests.
We have a large suite of tests that integrate with Postgres. Maybe one out of 50,000 database queries would fail. It was hard/impossible to predict which query would fail, since the problem wasn't with any individual query. By your logic, we should throw out our entire test suite. I'm not sure what we're supposed to replace it with.
That's why I figured I was invoking the wrath of the testing fanatics... that is where my logic leads. :-) I have no idea what your tests look like, maybe they're super useful, but I've worked at places in the past where there were thousands of tests that were frankly pretty useless, and were actually a net negative for a variety of reasons, but to point that out got you labeled as a "cowboy". (To be clear I'm not against automated testing, just automated testing done badly.) If you're running them in a context where you can't debug it, at the very least I would move it in house onto a machine where you can hook up a debugger.
I've been working on applications with javascript for over 10 years.
Not once can I remember a javascript bug where someone passed a value as the wrong argument and it made it into production.
Maybe it has happened, but it's just not a common bug. I just don't remember it.
Like, you can easily write it as a mistake as you're developing but it's obvious as soon as you run it. But if that bug gets in production your dev didn't even bother to run the code to see if it worked as intended.
I had found waterline's load time to have a LOT of things that I hadn't expected and take a long time... I'm just using knex now. Every time I try for magical solutions, I keep regretting it and end up using less magic.
We run our own CI/CD infrastructure, on top of virtualized infrastructure; if you use a third party that doesn't give you that, you might want to look for alternatives.
False positives are also super costly for us, as everyone works on trunk (by design, to avoid skew) and deploys all ckeckins directly to production. "Best out of two" is sufficient for the old tests, and if someone creates new intermittent tests, we follow up with education so that isn't a persistent problem.
Where possible, I like to have data that can exist independent of the other data on the system. I can make a separate 'tennant' for that test - and just ensure it's wiped before I proceed. Sort of a multi-tennant approach. Works great. I don't bother with a 'teardown', but do any cleanup before the test runs. I also ensure the tests are written to not make assumptions about global state.
Instead of dropping all constraints as the article suggested (that sounds hacky), I use ON DELETE CASCADE constraints. If I miss some, the tests fail. Seems easy enough to maintain.
With the above approach, DB testing is approachable and still pretty quick.
I realize this wasn't their solution, but it's worth noting that generally speaking, disabling auto-vacuum isn't recommended. Even if you do, it will still force vacuum jobs to prevent transaction ID wraparound.
* https://www.postgresql.org/docs/9.5/static/routine-vacuuming...
If vacuums are hanging/causing locks and these truly are just test tables, it might be beneficial to use temporary tables instead as the autovacuum daemon ignores them. Additionally, unlogged tables that reside on a memory disk can be insanely fast - might cut some additional time down depending on your data.
It was easy and quick to drop schemas.
If the above works, automate it.