1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92
|
/** \page backenderrors Handling Backend Communications Errors
Architectural Discussion
December 2001
Proposed/Reviewed, Linas Vepstas, Dave Peticolas
Updated and adapted for general QOF usage, May 2005
Neil Williams <linux@codehelp.co.uk>
\section backendproblem Problem:
What to do if a serious error occurs in a backend while
QOF is being used? For example, what happens if the connection
to a SQL server is lost, because the SQL server has died, and/or
because there is a network problem (unplugged ethernet cable, etc.)
With the QSF backend, what happens if the write operation fails?
(disk full, permission failure, etc.)
\section backendgeneric The "Generic Handler, Report it to the User" idea:
Go ahead and close the connection / clean up, but then return to
QOF in some nice way, use qof_session_get_error to report the error
to any GUI using program-specific handlers and then
allow the user to initiaite a new session (or maybe try to do it
automatically): and do all this without deleting any data.
I like this for several reasons:
- its generic, it can handle any backend error anywhere in the code.
You don't have to second-guess based on whether some recent query
may or might not have completed.
- I believe that reconnect will be quicker, because you won't need
reload piles of accounts and transactions.
- If the user can't reconnect, then they can always save to a file.
This can be a double bonus if done right: e.g. user works on laptop,
saves to file, takes laptop to airport, works off-line, and then
syncs her changes back up when she goes on-line again.
\section backendresults Discussion:
Should the backend try reconnecting first, or just go ahead and
return an error condition immediately? If the latter, then the
current backend error-handling can just stay as it is and the gui
codes need to add checks in several places, right?
The problem with automatic reconnect from within the backend is that you
don't know quite where to restart... or rather, you have trouble getting
to the right place to restart.
You can't just re-login, and reissue the commit. You really need
to rewind to the begining of the subroutine. How can you do this?
Alternative 1) wrap the routine and retry three times.
Alternative 2) throw an error, let some much higher layer catch it.
Well, approach 1) seems reasonable... until you think about what happens
if three retries doesn't cut it: then you have to throw an error
anyway, and hope the higher layer deals with it. So even if you
implement 1), you *still* have to implement 2) anyway.
So my attitude is to skip doing 1 for now (maybe we can add it later)
and just make sure that when we "throw" the error, it really does behave
like a throw should behave, and short-cuts its way up to where its
caught. The catcher should probably be a few strategic places in the
GUI, like wherever a QofQuery() is issued, and wherever an
object is edited.
What's the point of doing 2 cleanly? Because I suspect that most
network / filesystem errors won't be automatically recoverable.
Most likely, either someone tripped over an ethernet cable, or the
server crashed or the disc is full and you gotta call the sysadmin on
the phone or clear out some files, etc. The goal is not to crash the
client when the backend reports an error, but rather let the user
continue to work.
\section errorreport How to Report Errors to the GUI
How would the engine->GUI error reporting happen? A direct callback?
Or having the GUI always check for session errors?
We should use the session error mechanism for reporting these errors.
Note that the API allows a simple 'try-throw-catch' style error
handling in C. Because we don't/can't unwind the stack as a true
'throw' would, we need to make sure that when we "throw" the error,
it emulates this as best it can: it short-cuts its way up and out of
the engine, to where its caught in the GUI.
If there are a *lot* of places where these calls are
issued, simplify things by implementing your own callback mechanism.
*/
============================== END OF DOCUMENT =====================
|