Central Register of Data Model Entities
With any database, even one with a share nothing architecture, a central, authorative register of data object model entities is vital, if only so that entities can be unambiquously referenced in the application configs. In the HDB the central register is known as the Application Delta Profile (ADP), and hdbADP is the class that encapsulates it.
The name merrits disection. 'Application', as in Dissemino webapps, reflects the Dissemino influence on the HDB design. 'Profile' instead of 'Model' makes a certian amount of sense in the context of change control. It is also the case that hdbADP goes a step beyond a data model entity register, as it can include non-HDB data resources. The idea being that the ADP is then a register of all data resources used by a webapp. 'Delta' however is firmly the prevail of the HDB. The HDB is nothing if not all things delta. The Deltas generated on data state change within repositories, are written to delta files using delta notation, which uses delta ids, so-called exactly because they are used to write delta files. The data is backed up by mirroring deltas to other physical servers, via a middleware tool known as the delta server. Of course, the data object model entity register could have been called the data object model entity register, but it is instead called the Application Delta Profile.
Object Model Entity Delta ID Allocation
The entities of importantance to the HDB are the data classes, their members, and the data object and binary datum repositories. The ADP issues 16-bit unsigned integer delta ids to these entities, following the convention that 0 is NULL, undefined or invalid. Issued ids are thus, always greater than 0. In the general case, ids are issued in the order in which the applicable entities arise in the configs. However, not all data classes and repositories are defined or declared in the configs. Some are built-in, such as the subscriber class and repository used to authenticate users. It makes sense for these built-in classes and repositories, to always have the same ids. This necessitated a range of ids to be set aside for the built-in entities. Currently only six built-in classes exist, but as it is likely that more will be added in the future, ids 1-20 inclusive have been reserved this purpose. The same range has been set aside for the standard repository ids. Another 30 class and repository ids have been reserved for user classes. The class and repository ids for classes defined and repositories declared in the configs, both start at 51.
Member delta ids reflect the order in which members are added to classes, but they don't start from 1 in each class. Instead, member ids are unique across all classes. Accordingly, there are reserved id ranges for members of the built-in classes, and for members of user classes. These are respectively 1-500 and 501-1000. In summary, the reserved ranges are as follows:-
Class Ids 1-20 | Built-in classes. Class Ids 21-50 | User classes. Class Ids 51-1000 | Application classes. Class Ids 1001+: | Application classes when used as sub-classes (see Class Context below). |
Member Ids 1-500 | Built-in class members. Member Ids 501-1000 | Built-in class members. Member Ids 1001+ | Application class members. | |
Repos Ids 1-20 | Repositories of in-built classes. Repos Ids 21-50 | User class repositories. Repos Ids 51+ | Application sepecific respositories. |
Note that while data classes can be adopted by multiple repositories, this is only allowed for application specific classes.
Class Context
The context of a data class, simply means how it is used. Suppose we first define a postal address class PostAddr, with members of StAddr, City, State and Zipcode. Then we define a Person class having two PostAddr members of WorkAddr and HomeAddr. In this example, the Person class has only one deployed context, namely as a class in its own right. The PostAddr class could in theory be used as a class in its own right, because all data classes can. However in the example, PostAddr has only two deployed contexts, as Person.WorkAddr, and as Person.HomeAddr.
Assuming no other classes are defined earlier in the application configs, PostAddr is assigned a class id of 51, and StAddr, City, State and Zipcode, member ids of 1001, 1002, 1003 and 1004 respectively. Person is assigned a class id 52, and WorkAddr and HomeAddr, member ids of 1005 and 1006. These ids are the real ids of the classes and members, and are what would appear in the deltas of repositories that were using the classes in their own right. However as PostAddr is a subclass of Person, an alias class id is used instead.
The real delta id of a data class, is vested with the hdbClass instance. hdbClass has a member variable which states what the delta id is, and this does not change. Alias delta ids are not vested with hdbClass, but are instead the ids of the members that have the data class as their data type. The class id of PostAddr as WorkAddr is thus 1005 and PostAddr as HomeAddr is 2006. In the deltas written out by the Person repositories, these alias class ids are used instead of the real id of the PostAddr class.
Class delta id aliases were introduced to disambiguate subclass members in cases where the host class has two or more members of the same subclass. Without aliases, Person.WorkAddr and Person.HomeAddr would have the same class delta id, but they can be distinquished by their member ids. However Person.WorkAddr.City and Person.HomeAddr.City would have the same class id and member id and could not be distinquished. Facillitation of alias ids that depend on the class context, is the reason why the member ids are unique across all classes.
Note that the hdbADP class has maps to translate the text form of a class context back to the actual data class. The ADP 1:1 map m_mapClsCtxName, maps the context, to the alias id, while the ADP 1:1 map m_mapClsCtxDtId, maps alias ids back to the pure data class.
ADP Initialization Process and Config Changes
During program initialization, data object model entities are registered to the ADP by their respective constructors or initialization functions. These functions are called, either directly by the program code (embedded config), or by the standard config read functions in the library upon encountering applicable XML tags in the configs. Many programs use both methods. The DWE for example, uses embedded config for its in-built admin and stat reporting facility, but gets everything else from XML config files. In the virgin run, delta ids are allocated to object model entities in the order they arise, either by standard or embedded config. Once allocated, delta ids do not change.
Once the ADP has been populated, AppDeltaInit() is called. This compares the ADP to the last entry in a list of previously accepted ADPs. If the list is empty (virgin run), the ADP is accepted and added to the list. The scenario is then the same as having an ADP match, in which case the program is cleared to go into service. In the event that the ADP differs from the last, the new ADP is accepted providing the two do not actually contradict. Either or both ADPs can have classes or class members that do not appear in the other, but if a class member is common to both, both must exactly agree on the detail of that member. The program terminates if any contradictions arise, otherwise the new is added to the list and the program is cleared to go into service. Once the program goes into service, the ADP is authorative.
Data object models can be readily expanded but other changes are rarely straightforward, usually requiring offline, wholesale data translation. The change acceptance process of the ADP is no exception. The process allows entity deletion, but if an entitiy is deleted its delta id is also deleted, rendering data written using the delta id, inaccessible. Deleted delta ids are never reassigned so new entities are assigned entirely new delta ids - the highest issued so far plus 1. Other changes, such as adding an index requiring a repository that was not previously unique on the applicable member, to now be so, are simply rejected by AppDeltaInit().
hdbADP Anatomy
To avoid verbosity, Delta Notation refers to repositories, data objects and data object members, by delta id rather than name. Repository delta files have an XML header that states the native data class of the repository. This header gives the names and numerical ids of the repository, the host class and members be they sub-classes or otherwise. All names and ids in the repository header must be in exact agreement with that found in the ADP. The hdbADP class is the de-facto data object model of the service program. It is loaded from and exported to an XML file as follows:-
<appProfile appname="" exepath="" opargs=""> <repos id="" file="fullpath_of_deltafile"> <class name=""> <member id="" size="" oset="" min="" max="" sublass/datatype="" name=""/> ... </class> </repos> ... </appProfile>
The hdbADP class comprises several maps and an array as follows:-
hzMapS | <hzString,hdbDatatype*> | mapDatatypes | Complete set of names of fundamental data types by name, including data enums and data classes hzMapS | <hzString,hdbEnum*> | mapEnums | Total data enums (selectors) hzMapS | <hzString,hdbClass*> | mapClasses | All defined data classes hzMapS | <hzString,hdbObjRepos*> | mapRepositories | All declared data repositories hzMapS | <hzString,hdbBinRepos*> | mapBinDataCrons | All declared binary datum repositories hzArray | <hdbClass::Member*> | arrMembers | Array of data class members |
The ADP is the entire set of database entities and their delta assignments for an application, but please be aware that this is the limit of its scope. While in most cases programs will have only one ADP, in the case of Dissemino there can be multiple ADP instances. Dissemino is routinely configured to listen on the standard ports for HTTP and HTTPS on behalf of multiple hosts (web services), most of which require some form of database functionality. Any that do, get an ADP. Because of this, there is no _hzGlobal_ADP and programs must declare ADP instances as required.