Alicia recently inherited a whole suite of home-grown enterprise applications. Like a lot of these kinds of systems, it needs to do batch processing. She went tracking down a mysterious IllegalStateException
only to find this query causing the problem:
select * from data_import where id > 10000
The query itself is fine, but the code calling it checks to see if this query returned any rows- if it did, the code throws the IllegalStateException
.
First, of course, this should be a COUNT(*)
query- no need to actually return rows here. But also… what? Why do we fail if there are any transactions with an ID greater than 10000? Why on Earth would we care?
Well, the next query it runs is this:
update data_import set id=id+10000
Oh. Oh no. Oh nooooo. Are they… are they using the ID to also represent some state information about the status of the record? It sure seems like it!
The program then starts INSERT
ing data, using a counter which starts at 1. Once all the new data is added, the program then does:
delete from data_import where id > 10000
All this is done within a single method, with no transactions and no error handling. And yes, this is by design. You see, if anything goes wrong during the inserts, then the old records don't get deleted, so we can see that processing failed and correct it. And since the IDs are sequential and always start at 1, we can easily find which row caused the problem. Who needs logging or any sort of exception handling- just check your IDs.
The underlying reason why this started failing was because the inbound data started trying to add more than 10,000 rows, which meant the INSERT
s started failing (since we already had rows there for this). Alicia wanted to fix this and clean up the process, but too many things depended on it working in this broken fashion. Instead, her boss implemented a quick and easy fix: they changed "10000" to "100000".