Restructuring the database objects: Part II
As mentioned in the previous article, I’ve been working on restructuring the database objects to remove obsolete code and refactor the objects by functional area. One key area that I mentioned before has to do with managing the undo/redo capability.
In the main gramps window under “Edit”, there are three items that may or may not be grayed out: Undo, Redo and Undo History. These rely on methods in the database objects that manage a separate database where transactions are kept in case the user makes a mistake and wishes to undo (or redo) some change. These methods work closely with (but are separate from) the transactions discussed in part I. When you change something, the process from a high level is:
- Start a new transaction
- Write the specifics of each record change (old and new data) to the undo database
- Commit the transaction, which causes:
- Records to be added, deleted or updated
- A reference to the transaction added to a special transaction list
The transaction list is what drives the undo/redo menu. The index of the current entry is kept in a special variable, “undoindex.” When a user wishes to undo or redo a transaction, the program takes the transaction pointed to by the undoindex (or the undoindex + 1 for redo) and reverses it — changing an add to a delete, a delete to an add, and replacing new data with old data for updates (the reverse for a redo operation).
Today, the undo database is a BSDDB recno database managed in part by the transaction processor and in part by methods in the main database class. I’ve been able to consolidate this work in a new undoredo module, which also contains a level of abstraction so that we can use any kind of storage for the undo database, as long as it supports methods like append and get. This means that it is trivial (and my test code does this) to use a simple Python list for the same thing. So, why use a database at all? It’s a matter of size and scalability.
Currently, there is a hard-coded limit of 1000 entries in the undo transaction list. However, a single transaction can comprise many records in the undo database, with people and families and events and sources and references between them all requiring updating, so 1000 entries can easily mean several thousand records in the undo database. Considering that a single record is usually between 250 and 500 bytes (though it can be much larger), the amount of storage to hold everything in memory can be a little much. (I suppose this is a matter for discussion, since RAM is generally cheap these days.)
The new undoredo module is about ready to go, but a problem remains. In fact, it is a problem today. If we reach our limit of 1000 entries, the program starts overwriting the undo data, which not only means that you can’t reliably undo transactions anymore, but also that you can’t use the “Abandon Changes and Quit” option from the main menu. I’m not aware of any complaints, but that may be just because 1000 is a lot of updates — probably more than the average user will make during a single session.
The question is, “Should we worry about this?” We can ignore the problem — after all, this is the way things work today — or we can try to design a better way. Some options I can think of:
1. Do away with the 1000 transaction limit. As long as the updates are kept on disk, the transactions referred to by the list are usually under 50 bytes. Do the math and you can see that this does not grow too quickly.
2. Keep some limit but enhance the undo/redo functionality to work on a ring — possibly even keeping the transaction list as a linked list where the tail points back to the head (thus making a ring) with an attribute that identifies the current entry.
3. Do nothing since apparently no one has hit this problem so far.
4. Something else?
Here’s where I would like your input. Please do some thinking and try to work out some answers to the above problem. Let’s try to solve this together!
Benny
15 August 2009 @ 1:52 am
How about setting the limit to 1000 – 1100. If the number reaches 1100, the oldest 100 undo operations are removed, ….
But really, will somebody press undo 1000 times. I don’t think so.
So the only issue is the abandon changes and quit selection. We can change that text to ‘Abandon last 1000 changes of this session’ and do that if we want to be logically correct.
lcc
16 August 2009 @ 2:25 am
It’d be nice if it were possible to undo imports too. Then it’s not about pressing undo many times, as it’s a single operation which counts for many.
Roger
22 August 2009 @ 12:50 pm
If there’s a limit, I suggest you inform the user when they hit it. Pop up a warning telling them that if they make any further changes, they won’t be able to undo earlier transactions (or whatever the case may be). Good warning boxes are somewhat of an art, but silently violating the assumptions made by users is bad form.
Brian Matherly
23 September 2009 @ 9:06 am
I agree with Roger. A simple warning dialog when the user hits 999 would be appropriate.