This is the second version of the backup proposal. It's quite
a bit different than the first. Thanks to everyone who commented
on the first one, both on the list and in private.


Proposed Backup Strategy for GnuCash v2
=======================================

Goals
-----

GnuCash's method for backing up its database files should protect
against three kinds of failures:

1. A crash during the operation of GnuCash that doesn't affect the
   database file, but which prevents newly-entered data from being
   saved.

2. An error during the writing of a GnuCash database file that
   prevents the file being written correctly, resulting in an obviously
   incorrect file. This could be caused by, e.g., a power or hardware
   failure, running out of disk space, the user hitting ctrl-c or killing
   the process in some other way, or a bug in the GnuCash file i/o code.

3. The creation of a GnuCash database file that is a readable but
   incorrect in a non-obvious way. Such a problem may not be discovered
   for some time. Such a problem could be caused by a bug in GnuCash or
   by the user accidentally entering wrong data or performing an operation
   that was later determined to be undesirable (like deleting an account).


Current Status
--------------

GnuCash only writes the main database file when requested by the user.

Currently, when saving, GnuCash writes the main database file and then
writes a time-stamped duplicate of that file. Thus, the user will have
a 'save history' of database files that record the state of the database
at each point the user requested a save. This 'save history' will
protect against failure #3, as you can revert to an earlier, correct
version of the database.

GnuCash also creates an ascii log file which records changes made to
GnuCash entities during operation. Currently, this log file can be
used to manually recover from failure #1, but there is no automated
way to do so, or any other support for protecting against this kind
of error.


Problems
--------

 + Recovering manually from a log is tedious and thus failure #1 is a
   real pain.

 + None of the current backup methods will protect against failure #2.

 + Creating a 'save history' will generate lots of large files, especially
   for users who save a lot (like me).


Proposed Solution
-----------------

+ To better handle failure #1, GnuCash should be able to automatically
  recover from log files.

  As changes are made to the database, they will be logged to the
  filename "<original name>.log", where <original name> is the name
  of the database file.

  When GnuCash starts, it should check for the presence of the log file
  and, if it exists and has a later modification time than the main
  file, GnuCash should prompt the user to load the log file in addition
  to the main file (since the log file will have the changes to the
  main file that occurred after the last save).

+ To handle failure #2, GnuCash should change the way it saves the main
  database file. Saving should be done by:

    1. Writing the new database to the temporary file.
       The temporary file name will be "<original name>.tmp"
    2. Moving or copying the old database to a backup file,
       overwriting any existing backup file. The backup file
       name will be "<original name>~"
    3. Moving the temporary file to the main database file.

  Thus, after successful completion of a save, the original database will
  be saved in a backup file.

  If there is a writing failure during step 1, the original database is
  untouched.

+ GnuCash should maintain log histories, rather than save histories, to
  handle failure #3.

  After saving, the current log file should be renamed to be the first
  log history file, and a new log file should be started.

  Log history files should be named as "<original name>.log.I" where I
  is an integer and means the log history file is the Ith file in the
  history.

  Thus, loading the database <original name> should be equivalent to
  loading (in order) <original name>.log.1 ... <original name>.log.N
  where N is the number of log history files.


Example
=======

The files in the directory where the main database name 'gnucash.gnc'
is located will be as follows:

gnucash.gnc       - main database file
gnucash.gnc~      - backup file from last save
gnucash.gnc.tmp   - temporary file created during save
                    This file will only existing during
                    saving, unless there is an i/o error.
gnucash.gnc.log   - The current log file. This file will
                    only exist while gnucash.gnc is open,
                    unless there is an i/o error.
gnucash.gnc.log.1 - These are the log history files.
gnucash.gnc.log.2
...
gnucash.gnc.log.N


Pseudo-Code Implementation
--------------------------

Saving: /* Save to temporary file */
        save_database_to_file("<original name>.tmp");
        if (error) {
          report_error_to_gui();
          return;
        }

        /* Make backup file */
        rename("<original name>", "<original name>~");
        if (error) {
          report_error_to_gui();

          /* At this point, the current database file was
           * written, but there was an error creating the
           * backup file. Should we ask the user whether
           * to proceed with overwriting the original, or
           * should we just tell them where the temporary
           * file was written and return? */

          return;
        }

        /* Move temporary onto original */
        rename("<original name>.tmp", "<original name>");
        if (error) {
          report_error_to_gui();

          /* We should tell the user that the current database
           * was saved to the temporary file, so they know where
           * to find it. */

          return;
        }

        I = find_next_log_index();
        rename("<original name>.log.I");
        if (error)
          report_error_to_gui();

        start_new_log("<original name>.log");
        if (error)
          report_error_to_gui();


I would appreciate any comments you have.

thanks,
dave

--
Gnucash Developer's List
To unsubscribe send empty email to: [EMAIL PROTECTED]


Reply via email to