Sunday, March 14, 2010

Afraid to COMMIT;

Going through some old documents, I found this little gem I had uncovered doing some analysis of the source code.

I found this crazy enough to save, so I hope you enjoy it.

I found 14 or 15 commits in a 115 line procedure. I was shocked and stunned. It was wrong on so many different levels. I share with you the pain I went through.

We'll start off with a call to the logging table (just a quick note, this was not a stored procedure but a INSERT statement).
log_something;
Creating a record in the THIS_TABLE, it will have a status of A. Everything in there has a status of A.
INSERT INTO this_table (my_id, start_date) 
VALUES ( l_my_id, sysdate)
RETURNING table_id INTO l_table_id;
I know some could argue for COMMITs being inside stored procedure, but it was hammered into my head at an early age that the calling application should perform the COMMIT.
COMMIT;
THIS_TABLE had multiple "rules" tables. I understood the concept, but the implementation was not so good. This is the first "rules" table.
UPDATE rule_tab
SET status = 'D'
WHERE my_id = l_my_id
AND status = 'A';
Just so you are aware, that STATUS column had no constraint on it other than the size VARCHAR2(1).

Guess what time it is?
COMMIT;
Here is the second
UPDATE other_rules_table
SET status='D'
WHERE my_id = l_my_id
AND status='A';
Guess what?
COMMIT;
OK...here comes the other awesome part...to switch the status back to A (Active), we're going to create a job...in 30 minutes
dbms_job.submit
( job => ln_jobno,
what => 'update rule_table set status=''A'' where my_id = '||l_my_id||' AND status = ''D'';',
next => sysdate+(.5/24));
COMMIT;
I kind of understand that one...for the job to go into the queue you have to issue the COMMIT.

For some reason, it's now cool to use a nested block.
BEGIN
dbms_job.submit
( job => ln_jobno,
what => 'update other_rule_table set status=''A'' where my_id = '||l_my_id||' AND status = ''D'';',
next => sysdate+(.5/24) );

COMMIT;
EXCEPTION
WHEN OTHERS THEN
insert into errors (name,error_date, text)
values ('it broke', sysdate, ' MY_ID '||l_my_id||' creating job to set to a ');
COMMIT;
END;
Now we have 2 jobs created, one to update THIS_TABLE and one to update my first "rules" table. -10 for using DML in a job. -10 for not putting them into a single job that could fail together. -30 for not creating a stored procedure to do this.

But wait, it's not over yet. We're getting to the very best part I think.
BEGIN
dbms_job.submit
( job => ln_jobno,
what => 'insert into errors (name,error_date, text)
values (''wow'', sysdate, ''error turning it back on') ;',
next => sysdate+(.5/24));

EXCEPTION
WHEN others THEN
insert into errors (name,error_date, text)
values ('doing stuff', sysdate, ' MY_ID '||l_my_id||' creating job to log errors');

COMMIT;
END;
Did I catch a "niner" in there?

Did you catch what that final job did? It "logged" an error from the previous 2 jobs. Really? Do jobs work like that? I'm not really sure that they do.

This little snippet is just a snapshot into my life over the last few years. This kind of thing is everywhere. (I know everywhere!). I've mentioned before, but I've been reading The Daily WTF since 2005. Daily. I've learned more from that site than perhaps any other because you learn what not to do...which is just as important as what to do. It takes years to gain the necessary experience (read: screwing up) to know what not to do, The Daily WTF speeds that up significantly by allowing you to witness others mistakes. We've all made them, to be sure. It's whether we learn from them that is important.

No comments: