-
Notifications
You must be signed in to change notification settings - Fork 1
Mapped DboDatabases
AbstractDboDatabase is a class that maps boost::shared_ptr<...> objects in memory when the server is initialized. It may be accessed by multiple sessions and threads. The purpose of DboDatabase is to share session independent data between sessions without having to duplicate the data or re querying the SQL backend.
AbstractDboDatabase and it's thread safety is managed by DboDatabaseManager which handles the following functions
- Adding/Removing
AbstractDboDatabasein management - Loading/Reloading
- Read/Write locking
- Managing
Wt::Dbo::Sessionthread safely for eachAbstractDboDatabase
Wt::Dbo::ptr are not stored because they are thread unsafe, therefore a separate data structure has to be used to store Dbo data.
All DboDatabaseManager setter functions, Add/Remove/Load/Reload acquires an exclusive write lock and all read functions grab a shared read lock. A read lock can be acquired by using the RAII ReadLock class. All DboDatabase getter functions should acquire ReadLock. All DboDatabase setter functions are called initially with the write lock acquired therefore getter functions should not be used to avoid deadlock.
A DboDatabase have the following characteristics
- A name for the DboDatabase
- 1 or more containers to map
boost::shared_ptr<...>objects - A protected
FetchAll()function that reloads data from the backend. This function must have a strong exception safety to make sure that any changes are reverted in the case an exception is thrown. - A protected
Load()function which maps the Dbo types inWt::Dbo::Sessionand toFetchAll() - A protected
Reload()function toFetchAll() -
boost::shared_ptr<...>objects must not be modified from within DboDatabase nor from outside of DboDatabase. -
boost::shared_ptr<...>getter functions must return const protected contained class.boost::shared_ptr<const Class>
A common practice to create strong exception safety is the following
void DboDatabase::FetchAll()
{
ContainerType tempContainer; //Create a temporary object of the same type and swap data. We'll swap it back if any exception is caught
tempMap.swap(PtrMap) //PtrMap is a member of DboDatabase
try
{
//Fetch from database backend here
}
catch(...)
{
TempContainer.swap(tempContainer);
}
}
Data maybe reloaded at anytime, although a mutex would provide thread safety, however, the Application data synchronization may be affected if FetchAll() is called during a procedure such as initialization of an Application. For example if a default language configuration is used in a condition, suddenly after that FetchAll() is called from another thread. Another problem may occur if that language got deleted during that FetchAll(), the application would not find the default language in the database and all strings would printed as ??key??. Such a problem will be referred to as (DboDatabase) data synchronization problem.
Some DboDatabase may be able to solve this problem more easily than others. Some may not even require any further synchronization and some may require restarting all sessions. It depends on how the DboDatabase is being used.
Read locking the DboDatabase
This will hold up the DboDatabaseManage::Reload() call until lock is released or goes out of scope. For some cases this may be a complete solution however this should be used together with another solution if the synchronization problem may occur in at any time in a session.
Cached DboDatabase
If the changes in DboDatabase are not expected to be considered by a session immediately, a DboDatabaseCache class can be used to store all boost::shared_ptr<...> for the session, this DboDatabaseCache would not react to changes and will retain the old boost::shared_ptr<...> objects till the end of the session. Old sessions would keep the old data and new session will use the new data.
It is important to grab a read lock in the database by creating a RAII ReadLock class before copying the boost::shared_ptr<...> objects from the active database.
Post changes
If the changes do not affect the program's logic and only affects the user interface, it can simply be posted to all the sessions to update the data.
Restart applications
This is a catch all solution. Applications should be restarted after all events are handled(TODO check for a safe way to restart application).
Disable reloading
If there are no effective solution or reloading is simply not required, reloading may be disabled.
Reloading is disabled for modules database due to the facts
- If any new module is added or delete it would require a server restart to load the module library
- Module names/versions are not included in any part of the application except for control panel
All sessions cache configurations, therefore a reload would only affect future sessions. Control panel would also have a restart applications option in case the change of configuration is very important.
Posts the following changes to all sessions
- Style CssStyleSheet(cleared and repopulated)
- Template CssStyleSheets(cleared and repopulated)
- Dbo Templates(new
Dbo::ptr<...>replaces the old one)
If the selected style is deleted, non-cached version of ConfigurationsDatabase is used to check the default style and that style is set as the current style.
Posts the changes to all widgets that uses tr() or trn() in all sessions by replacing the old Dbo::ptr<...> with the new one.
If the selected language is deleted or uninstalled, non-cached version of ConfigurationsDatabase is used to check the default language access path. If the access path does not require change in hostname, the language is simply changed otherwise the language is not changed and the cached version of the deleted language is used. Future calls to tr() or trn() would translate to default language. A notification is given to the user to switch to the default language.
Posts the changes to all sessions. If a page loaded in a session is deleted, it is removed from . If the user is currently on that page, forms are disabled and the user is notified of the deletion.
To be determined.