Thursday, December 14, 2006

Not so smooth update to 6.0...

Today I've switched from db4o 5.5 to 6.0 ("ready for production") in a ongoing project. I'm using db4o since 2004 and updating the db4o-jars didn't give me any surprise in the past... this changed today.

I've put the db4o-6.0-jars into my project and called a little update-script:

Db4o.configure().allowVersionUpdates(true);
com.db4o.defragment.Defragment.defrag("database.yap");

1. BIG BANG... Ugly traceback poped up which told me that my database is corrupted. Corrupted? The database works fine with older versions, so corrupted is a harsh word which I really don't like to read when updating databases...

So I told to myself: don't try to do much in one step... I fetched a fresh copy of my database (always work on backups if you're working with databases!!!!), did issue the #allowVersionUpdates(), opened and closed the database and tried defragmenting afterwards. This time all went well. Big Puuuuhhh...

Now I started my application (at least tried of)...

2. BIG BANG... lots of ClassCastExceptions were thrown. ClassCastExceptions for ClassA in a query which was definitevly constrained on ClassB? What the heck is going on?

I dived into my code. I'm using a common superclass for all my persisted classes which holds a "deleted"-attribute. I'm not deleting objects directly, I just mark them as deleted, so I can "undo" deletions if I want to. I'm using a simple "QueryWrapper" (which worked for ages without problems in 5.5 and before) which returns only undeleted objects.

public List query() {
Query query = objectContainer.query();
query.constrain(this);
// The complicated true-not is needed,
// because 'deleted' is declared as Boolean in one application,
// so the expression works regardless of Boolean or boolean
query.descend("deleted").constrain(true).not();
return query.execute();
}

I did some tests and came up with the conclusion: If I constrain my query on an indexed field of a superclass, the constrain of the class itself was ignored. This CRITICAL bug is already reported here:

http://tracker.db4o.com/jira/browse/COR-34

But why the hell did my query work with 5.5?

Because of another bug:

http://tracker.db4o.com/jira/browse/COR-57
COR-34 is no problem if you don't use an index on the field of the superclass. COR-57 describes a problem that an index isn't used properly by older versions of db4o if the number of objects to examine by the query processor exceeds a certain level. Maybe the index on the field wasn't created for other reasons, don't know for sure... bad luck, but good to know what's going on.

To sum up:
  • My query did work with old versions of db4o, because the index on the field wasn't used properly.
  • My query was messed with db4o 6.0, because COR-57 hit me right into the face because of using the index.
I've now introduced the proposed instanceof-check (and additionally I'm checking even the status of the 'deleted'-field on my own now).

Now one of my application works fine again... but my confidence in the db4o-query-mechanism is a little bit smaller than before.

I'm already using lots of custom indices and caches for complicated comparisons to speed up queries which would take to much time for the db4o-query-processor. And now I'm using just another "workaround" for the db4o-query-processor for even a simple query. Maybe I'm going to use db4o as a "simple and reliable" persistence machinery in the future which gives me all instances of class with a query-by-example. For all the complicated evaluations I'm using my specialised data structures, which are fast and reliable.

After this not-so-nice-experience, I started to migrate a second application from 5.2 to 6.0... remember what I've said about db4o as a "simple and reliable persistence machinery"?

3. BIG BANG... after migrating the database file, all my java.util.Date-fields which were formerly set to null (denoting that a workflow wasn't activated at all) , became suddenly Sun Aug 17 08:12:55 CET 292278994. I'll guess that java, db4o or my application won't run until this year, but who knows.

There are good chances to run into real trouble if your application relies on checks like this (and my application does):

if(workflowDate==null) {
...doSomeSeriousWork...
}

And to be honest: your Unit-Test won't catch this problem, because newly created java.util.Date-fields will be stored correct again in the database.

Don't get me wrong: db4o rocks! And 6.0 is declared as "production", not as "stable", so its kind of my own fault to try to migrate applications at this "early" stage (for luck I'm only running in a sandbox).

But my experience in other open-source-projects is: If you fall too far behind the current "production" builds, you'll run into even more trouble. And if noone is going to stress current builds with "production" data, some errors won't be detected.

I'll know that the db4o-team is going to fix the issues soon... and I'll hope that "detecting critical errors" is helpful...

0 comments: