40
edits
No edit summary |
No edit summary |
||
| Line 120: | Line 120: | ||
Let's first discuss what happens in case of a failure to perform the requested transaction. The original approach passes an optional listener object as argument to modifyItem(). The listener never gets called if the operation couldn't be performed and calendar observers play the same role, they actually never get called. Clients still have a valid view of the model, since they still have there unmodifed events. Exactly the same happens with the transaction system, either a transaction can be performed or not, changes to the items won't be reflected unless the storage operation was successful. And the commit() method could also acceppt the same listener than modifyItem() did. | Let's first discuss what happens in case of a failure to perform the requested transaction. The original approach passes an optional listener object as argument to modifyItem(). The listener never gets called if the operation couldn't be performed and calendar observers play the same role, they actually never get called. Clients still have a valid view of the model, since they still have there unmodifed events. Exactly the same happens with the transaction system, either a transaction can be performed or not, changes to the items won't be reflected unless the storage operation was successful. And the commit() method could also acceppt the same listener than modifyItem() did. | ||
Now for the tricky part called undo/redo. The previous approach can handle undo/redo because it's easy to just keep old items and jump back to a previous generation if requested. Since with the transaction system in place we don't provide copies of the items this approach is no longer possible. But undo/redo is essentially nothing more than a history log of changes, which exactly is what the transaction objects provide. | Now for the tricky part called undo/redo. The previous approach can handle undo/redo because it's easy to just keep old items and jump back to a previous generation if requested. Since with the transaction system in place we don't provide copies of the items this approach is no longer possible. But undo/redo is essentially nothing more than a history log of changes, which exactly is what the transaction objects provide. So a naiive approach would be to keep all transaction objects along the original item and upon request for some specific attribute travel along the changes. Of course this would be not optimal, and the solution is simple. The item reflects the most up-to-date version at all times, but we keep a negative version of the transaction along with the item. This allows for a generic and elegant undo/redo functionality. | ||
While thinking for possible drawbacks of the transaction system as a whole we came up with the question 'what happens if several clients want to get a new transaction object while there are maybe others not yet committed?'. After some thinking we decided that it wouldn't be a problem to hand out several transaction objects, even if previously created ones were still not committed. But generally we would need to add a generation-stamp to each transaction object. Afterwards, during commit() the transaction handler could merge non-conflicting transactions and therefore even allow for parallel transactions but still ensure that transactions itself are truly atomic operations. | |||
== Comments are welcome == | |||
As this is just a proposal for an architectural shift of the calendar core, it is open for discussion. Feel free to comment on this. | |||
edits