
Notes on requirements for transactions, dynamically loadable modules,
language independant methods.

This is a reasonably complex set of systems, and a little planning will help
make sure they can all be implemented once.



for modules, there needs to be a simple way to associate a file with a
particular class, and even with a particular version of a class. a likely
option is to require a filename to include the fully qualified name of the
class, and an extra version identifier.



All GCD files will be stored in a single directory, and all will be
processed. This means that a class may only be specified once, but
extended multiple times. 

On server startup, the system will have a consistent view of the currently
available classes. 





Module contents:
  GCD file(s) - describing classes provided by this module
              - extending classes provided by other required modules

  Method file(s) - Code for methods in a specific class in this module
                 - Code for methods added to other classes

  Config files - Describes which code files serve which particular class/
                 extend in a GCD file



Installing modules:
  Simply add the required new files to the appropriate directories.
Overwriting files is not permitted: if a file already exists, the new module
can not be installed.



Uninstalling modules:
  Determine if other modules depend on the one being removed. If so, then
abort the uninstall, or uninstall the additional modules.

  If the uninstall is permitted, flag the appropriate files as 'to be
deleted'. GCD files can be removed immediately, and method files can be
removed once the server no longer needs them for transactions currently in
progress.


Patching/updating modules:
  GCD files can be patched immediately. 








Managing class definitions

A class definition has a class ID attached as well. a particular object
reference will include a class name, a class ID, and an object ID. this is
used to determine which variant of a class was used when the object was
accessed. when a new class definition is loaded (including an 'extend' to
add fields) an entirely new class definition with a new ID is stored.

A class definition can be removed when there is a more recent version
available, and the older version is not in use anywhere.




Object cache

Objects will be keyed by classid and object id (rather than classname.)




Transactions and locking

An operation on a given object reference will come with a userid attached.
(currently describes the user who gained a specific reference - eventually
will be the user who authenticated for a particular client.) 

For that user, a write accesses a private copy with a different object ID,
and a read first accesses that, and if it fails can access the main copy
(ie, a copy-on-write technique.)

If an instance of a given class is currently locked, the transaction must
continue to use the previous version, but new instances of the object may
use the new version immediately. however, any new not-null fields (which
must have a default value) 







Server starts:
  Load current class definitions, everything is fine.
  Process install and remove sections as appropriate for table changes


Install new module:
  Update table definitions
  Update data in existing tables with new columns


Remove module:
  Report tables/columns that need removed





class definitions:


the class definitions are stored in a relatively complex structure, but can
be easily placed in a hash table.

a fully qualified name looks like this: item::item::item

eg: modulename::classname::enumname

all names visible in the current scope or a parent scope can be used
directly. all other items must have a qualified name, where the first name
in the FQN is visible. applications in general will use FQNs in all cases,
but may specify a specific scope to assume - ie, you can say "everything I'm
using is in module XYZ, so just assume that everything I use starts with
that, unless otherwise specified."

module whatever
{
    enum firstkind { A,B,C };

    class aclassname
    {
        enum kind { X,Y,Z }

        kind      k1;
	firstkind k2;
    }

    class anotherone
    {
        enum firstkind { Q,W,E,R,T,Y } // hides previous firstkind
    }
}



for storing this, all items are stored with a fully qualified name, in the
form item::item[::item[::item]...]  and accessed in that way. accissing with
a name starting with :: means "use the global scope, ignore any previous
'import scope' directives" otherwise each import is prepended to the
requested name to create a possible FQN, then tried. eventually, one will
find a match, or all will fail, and the absent name can be reported.

in order to allow multiple versions to be active, each version needs a
unique ID to be assigned. the hash table stores a list of items, although
most entries will have only one item.

a transaction will maintain a list of classes/objects in use, and which
version of a class is in use. when requesting a class definition, the system
must first check with the transaction system which version to use.

the first time an instance of a class is accessed in a transaction, its
usage count will be incremented, and when the transaction ends, each
referenced class will have its usage count decremented once.

non-transactional access will always use the latest definition of the class.




multiple servers

With multiple servers, all servers need to maintain a consistent view of the
available classes. this is really only of significance when an object is
copied to another server - the receiving server needs to be able to
determine exactly what data fields are being provided, and what other
details the class should have.

In a transactional system, this will be horribly nasty and complicated  :)




user created classes at run time

these can be placed in a 'user::<username>' module, with that module being
implicitly loaded for that user only. for resource usage, one option is to
mmap() this data to disk.  another option is to store these classes in the
normal database, and when a class can't be found, then attempt to find it in
the user::<username> module with a database search.


libODL should be the new name for object definitions (multiple different
object definition languages in a program are unlikely) so an ODL_ prefix for
functions is realistic.

GEAS will have its own 'locate item by name' that uses the normal ODL
functions, loops through appropriate imported module IDs, and then attempts
to load from the database.

if libODL understands temporary classes, then a class should have a usecount
incremented when accessed, and this should be decremented when the class is
not needed (ie each successful find operation has a matching decrement use
count operation) then a set number of temporary classes can be cached and
returned immediately. this will be a speed boost, as temp classes can be
used repeatedly without additional effort to track them, and with reduced
databases accesses.


