> Is it possible that the 10.1 vs 10.3 database is a red herring here,
> and this is simply the same "non-determinism" that I was observing
> in my initial tests?
Good point, very well could be. That didn't occur to me.
> My guess is that if you created 10.1 database A, and ran 10.3 against
> it, and then created 10.1 database B, and ran 10.3 against it, you
> might see dramatic differences even in just those two runs.
Have you yourself seen this variance with 10.1 databases, as well? I was limiting myself to the details in the description of this issue, which only mention the variance with 10.2 and trunk. If you are also seeing the variance in 10.1, then can we assume that the non-deterministic variance aspect of this issue is not a regression, but is an existing problem with Derby since who-knows-when? That might correlate with Mike's earlier comments about that being a potential issue in the past (hash codes based on DDL)...
> I'm wondering if there is something going on, correlated to the DDL
> statements executed by the repro script, which sometimes results in
> tables that return "reasonable" estimates, and sometimes results in
> tables that return "infinite" estimates.
This is great question. A follow-up question would be whether or not this DDL-based variance is a regression from 10.1 or has been there from the "beginning".
As you can tell, my primary focus here has been on potentially regressed behavior. As I've been looking at this problem I have been asking myself "what changed from 10.1 to 10.2 to cause this sudden slow-down?" So that's where my mind (and all of my questions) have been leading...
And on that point, I mentioned in my last comment that the JUMP behavior "does not take effect" for 10.3, but it does for 10.1. I spent some time tracing through the code and quickly realized that this change in behavior is an indirect result of
DERBY-1357. Here's why.
As explained on the wiki page that describes jumping (http://wiki.apache.org/db-derby/JoinOrderPermutations), the optimizer will find the first complete join order for a query before attempting to jump. In the repro query the first join order is simply "0 1 2 3 4 5 6 7". The associated cost is 5.607E62. Using the row counts calculated for this join order, the "target" join order then becomes "1 4 2 6 7 0 3 5". This means that the next 8 calls to "getNextPermutation()" will return partial join orders that build up to this target. i.e.
1 -1 -1 -1 -1 -1 -1 -1
1 4 -1 -1 -1 -1 -1 -1
1 4 2 -1 -1 -1 -1 -1
DERBY-1357 the optimizer "short-circuit" logic was not working, so even if a partial join order is more expensive than the best complete join order so far, the optimizer will continue to waste time optimizing join orders that are always going to return an estimate that is higher than the best so far. So in 10.1 (which does not have the fix for DERBY-1357) we'll merrily continue on our way from [1 4 2 ... ] all the way up until we either timeout or find a join order that is cheaper than 5.607E62. We eventually find a cheaper join order after about 2500 permutations, and that's the point at which optimization stops (see details from my previous comment).
With 10.2 and trunk, though, the
DERBY-1357 short-circuit logic has been corrected. And as it happens, the estimated cost for the partial join order "1 4 2 6 7 0 3" is "Infinity"; since that's greater than 5.607E62, we do not advance the join order and thus we effectively skip the rest of it (because we know it's going to return a cost that's worse than 5.607E62). That part is, I think, correct.
But having chosen to NOT advance the join order, now we come to the following block of code in OptimizerImpl:
if (permuteState == JUMPING && !joinPosAdvanced && joinPosition >= 0)
//not feeling well in the middle of jump
// Note: we have to make sure we reload the best plans
// as we rewind since they may have been clobbered
// (as part of the current join order) before we gave
// up on jumping.
reloadBestPlan = true;
permuteState = NO_JUMP; //give up
Since we're in the middle of jumping and since we did not advance the join order, we execute this if block--i.e. we rewind the join order back to the beginning and start all over. Or put another way, we gave up on the "JUMP" and went back to normal processing. From there we then have to go through thousands and thousands of permutations before we find a plan that's cheaper than 5.607E62. Depending on which database is in use, that can be ten thousand permutations or it could be forty thousand, which explains the variance in the time taken to complete optimization.
I think the changes for
DERBY-1357 are correct; what's not clear is whether the "if" block shown above is really necessary. The idea sort of makes sense to me: if we broke away from normal processing and jumped to a bad join order then we want to abort the jump as quickly as possible and return to normal processing. However, now that the optimizer "short-circuit" logic is fixed, I'm not so sure we need to revert back to "normal" processing when we jump to a bad join order. If the join order is bad then the short-circuit logic will make sure that we do not waste "too much" (yes, that's an awfully subjective term) time trying out bad join orders. So would it make sense to just "iterate" the target join order as usual without defaulting back to square one?
As a sanity check I commented the above "if" block out in the trunk codeline and ran the repro; when I did so, the script executed in about the same time as it does on the latest 10.1 branch. So this seems to confirm my findings. FAR more testing is needed, though, to see if this simple removal of the "if" block is a viable solution.
All of that said, I still think the BIGGER problems we are seeing are 1) DERBY-1905 and 2) as Bryan has said, the apparent variance in cost estimates based on DDL. DERBY-1905 is definitely playing a huge role here, what with the Infinity cost estimates followed by NaN. These kinds of estimates render the vast majority of the join order estimates useless, even if we do remove the "if" block mentioned above.
Also note: when I remove the "if" block as mentioned above, I found that sometimes the cost estimate for the "best" join order ("2 0 1 3 4 5 6 7") was "14116", and sometimes it was "247". This was on the same database and so does not appear to be the result of DDL statements. I'm still looking into this little tidbit to see what might be going on there...
As usual, some of this may be useful, some of it may not be. Please excuse me if I'm babbling...