Duuuude... Coding is trippy.
I figured it out.
It goes back to this:
...I sidestepped some other steps entirely, but there's one database call that has an unfortunate combination of attributes: We *do* need it to happen, and we care about what it returns, and it sometimes takes a long time to execute.
So I realized there was only one thing it could possibly be doing in that time, since it's not connecting to a new datasource and it doesn't know it needs to connect to a new database yet. The only thing it was doing was getting a connection from the pool...
I looked up the connection pool behavior and found that it does NOT do look-ahead, but I was reminded of something that sent me in the right direction: the connection pool has to create and destroy connections as they're needed; most of the time when I'm just
testing stuff by myself, I have the luxury of thinking of it as a single connection object that gets passed around, because I'm the only user using the project, so that's more or less how it *behaves*. But that isn't what the connection pool is; it stores a single Connection *Pool*, from which *multiple* connections are drawn, so if you ask it for a connection while one is in use, it has to make a new one, and from then on, unless it determines that there isn't enough activity to warrant having the second one around, it'll get rid of the second one.
Earlier on in the code, I decided to run a separate query in a separate
thread because it was ok to run that query asynchronously. The method that runs that query is executed *directly* before the getImportantNumber method, so I figured it probably wasn't done running yet, so when it tried to run that query, it had to create a new connection and add it to the pool. So to test that, I told it to run the separate thread method 3 times instead of 1;
The first time it runs the separate thread query, it takes 0 milliseconds to get the connection.
The second time, it took 14 milliseconds to get the connection
The third time, it took 15 milliseconds to get the connection
And when it ran the importantNumber query after that, it also took 14 seconds to get the connection, and I couldn't find a way to log it and it goes by too fast to check in the debugger, but I'd be willing to bet that at that moment there were 4 Connections stored in the connection pool. So apparently it takes about 14-15 milliseconds to create a database connection and add it to the pool.
This also explains why the delay appears to only show up the first time you run that method; it's not about that method, it's about the current size of the connection pool and the threaded method that runs immediately before it. If I wait a while and let the connection pool decide it doesn't need all those connections any more, then try it again, it'll probably have that delay again the next time I call that method even if I don't restart the program.
Man, this stuff is just really cool...