diff --git a/src/engine/design.txt b/src/engine/design.txt index 0b645dac52..ad2e3af29c 100644 --- a/src/engine/design.txt +++ b/src/engine/design.txt @@ -48,8 +48,8 @@ The engine includes support for non-currency-denominated assets, such as stocks, bonds, mutual funds, inventory. This is done with two values in the Split structure: -double share_price; -double damount; + double share_price; + double damount; "damount" is the number of shares/items. It is an "immutable" quantity, in that it cannot change except by transfer (sale/purchase). It is the @@ -67,17 +67,83 @@ Currency accounts should use a share price of 1.0 for all splits. To maintain the double-entry consistency, one must have the following hold true: -(source_split->damount) * (source_split->share_price) == -sum of all ((dest_split->damount) * (dest_split->share_price)) + 0.0 == + (source_split->damount) * (source_split->share_price) + + sum of all ((dest_split->damount) * (dest_split->share_price)) Thus, for example, the purchase of shares can be represented as: -source: -debit ABC Bank for $1045 (1045 dollars * dollar "price" of 1.00) + source: + debit ABC Bank for $1045 (1045 dollars * dollar "price" of 1.00) + + destination: + credit PQR Stock for $1000 (100 shares at $10 per share) + credit StockBroker category $45 in fees -destination: -credit PQR Stock for $1000 (100 shares at $10 per share) -credit StockBroker category $45 in fees + +Error Reporting +--------------- +The error reporting architecture (partially implemented), uses a globally +visible subroutine to return an error. In the naivest possible implementation, +the error reporting mechanism would look like this: + + int error_num; /* global error number */ + + int xaccGetError (void) { return error_num; } + + void xaccSomeFunction (Args *various_args) { + if (bad_thing_happened) error_num = 42; + } + +Many programmers are used to a different interface, e.g. + + int xaccSomeFunction (Args *various_args) { + if (bad_thing_happened) return (42); + } + +Because of this, it is important to explain why the former design was choosen over +the latter. Let us begin by listing how the choosen design is as good as, and in +many ways can be better to the later design. + + (1) Allows programmer to check for errors asynchronously, e.g. outside + of a performance critical loop, or far away, after the return of + several subroutines. + (2) (with the right implementation) Allows reporting of multiple, complex + errors. For example, it can be used to implement a trace mechanism. + (3) (with the right implementation) Can be thread safe. + (4) Allows errors that occurred deep in the implementation to be reported + up to much higher levels without requireing bagagge inthe middle. + +The right implementation for (2) is to implement not a single variable, but a stack +or a ring (circular queue) on which error codes are placed, and from which error +codes can be retreived. The right implementation for (3) is the use +pthread_getspecific() to define a per-thread global and/or ring/queue. + + +Engine Isolation +---------------- +Some half-finished thoughts about the engine API: + +-- The engine structures should not be accessible to any code outside of the engine. + Thus, the engine structures have been moved to AccountP.h, TransactionP.h, etc. + The *P.h files should not be included by code outside of the engine. + +-- The goal of hiding the engine internals behind an API is to allow the engine + to be replaced by a variety of back ends, e.g. an SQL and/or CORBA back-end. + +-- The down-side of hiding is that it can hurt performance. Even trivial data + accesses require a subroutine call. Maybe a smarter idea would be to leave + the structures exposed, allow direct manipulation, and then "copy-in" and + "copy-out" the structures into parallel structures when a hidden back end + needs to be invoked. + +-- the upside of hiding behind an API is that the engine can be instrumented + with extension language (perl, scheme, tcl, python) hooks for pre/post processing + of the data. To further enable such hooks, we should probably surround all + operations on structures with "begin-edit" and "end-edit" calls. + +-- begin/end braces could potentially be useful for two-phase commit schemes. + where "end-edit" is replaced by "commeit-edit" or "reject-edit". @@ -89,4 +155,4 @@ big banks use this for various consistency reasons. - +March 1998