Up Right RACE - Modifications and Extensions to ACE from Ravensburg

Main Ideas of RACE - Logging

Background and Requirements

We have Entities, Objects (Valuetypes in the CORBA-sense) which must be collected in a Logfile.
Since they may come in a burst or high frequence, the receiving of them mustn't block.
The writing on the disk must be as fast as possible.

If this Entities are (key,data)-pairs, and two Entities are logical equal, it the keys are equal, snapshots of a collection can be made.

If this Entities have a type, a two level snapshot is thinkable: For each type can be a snapshot, and all this snapshots are linked in an whole snapshot.

We abstract extensionally, we are not interessted the content (the intension). Thus, this Entities could be:

The Entities can be represented binary or in an text format like XML.

The Linking with RACE_LogFileOffsets

The linking in those logfiles in the snapshot are like the relative Pointers of RAMS. Since we expect very long logfiles, a logfile consists of multiple parts. Thus, the RACE_LogfileOffset consists of a (part,offset)-pair.

The Role of the RACE_Dumpables

The Entities and different Snapshots which should be written in the logfile are put into subclasses of RACE_Dumpables.
They encapsule pieces of memory to be dumped in a Logfile.
A RACE_Dumpable enhances a RACE_Message_Block with an extra state, and the possibility to enhance the header is a main invention of RACE vers. ACE.

Like in the Persistence-Concept related with RAMS-Relativ Pointers we destinguish between

the persistent State on the Disk
this is the data in the RACE_Data_Blocks
Transistent Accessor Operations at Runtime in the mainstorage
The subclass of a RACE_Dumpable can implement comfortable access operations

The workings of the logger

The RACE_Logger is an active object with a thread for writing in parallel with the other work of the application.

It is most efficient to write 4096 Byte blocks on disk. Thus, the RACE_Logger has a RACE_Accumulator which collects RACE_Dumpables until it can write a block. It collects the IO-Vectors for the gather-scatter-Pattern.

For snapshots, we have to know where the RACE_Dumpable is written. Thus, the InformOffset callback method of a RACE_Dumpable is called after the offset is fixed.

Subclasses of RACE_Dumpables have a state, and overwrite/implement the InformOffset-Method.

The RACE_Accumulator does not copy the data. Rather, it uses the writev(2) operation which gathers the block together from multiple RACE_Message_Blocks without copying.
The reference counting of RACE_Data_Blocks in the RACE_Message_Block avoids copying whenever possible.

Big dumpables (for snapshots) may have mutiple parts; in that case we call them fragmented.

The Queue with a strategy

If there is too heavy load, some RACE_Dumpables which are marked as discardable (a virtual function which can be overwritten) can be discarded. If the load still is too heavy, the enqueueing is inhibited. The blocking has a timeout and the Logger could react at this situation - for instance terminate the Logging.

The policy for when a queue is regarded as full, as well as the timeout times, are controlled by a QueueLevelStrategy which may be overwritten.

The overwriting is very useful: The InformOffsets are executed from the LoggerThread.
For example, consider the case that a snapshot is triggered and should be enqueued while the queue is full. This is done by the LoggerThread, but the LoggerThread is the only one who consumes the queue and can solve the situation - thus we have a deadlock.
The solution is a special QueueLevelStategy to make the queue appear non-full for the LoggerThread.

The Linking in the Logfile

There may be Snapshots of keyed Entities. The snapshot may be a collection of (key,RACE_LogFileOffset)-Pairs, where the Offset points the last instance of the entity in the logfile.

When the RACE_Accumulator has determined the offset in the Logfile, it calls the Inform_Offset-Callback.

In the management part of the concrete Dumpable derivation should be a association with the Snapshot Collector. This snapshot collector implements the access into the Shapshot dumpable.
The concrete Inform_Offset-Implementaion triggers the insertion the (key,Offset)-pair into the Snapshot.

The Snapshot must be written, when the last outstanding Dumpable is written. This is controlled with the strategie RACE_Snapshot_Trigger.

The RACE_Snapshottrigger

When the time comes to pull a snapshot, there may be RACE_Dumpables in the RACE_Message_Queue or in the RACE_Accumulator (which also behaves as a queue).
Thus we have to wait for committing the Dumpables in the queue and the accumulator and trigger the snapshots.

We have discovered two kinds of Snapshots:

Why Snapshots may occur out-of-sequence

If we have a 2 level snapshot with different kinds of snapshots at the first level and a whole snapshot at the 2nd level which has links to the snapshot at the first level, the snapshot may occur out of sequence.

We think at the following:

When it is time for triggering the 2nd level snapshot,the last Offset of a periodic snapshot can simply inserted into the whole snapshot.
On low traffic and small Entities, it could be, that the last Snapshot isn't ready yet. Thus after beginning the new snapshot, the triggering of a (key,offest)-Snapshot is outstanding.

The Logfile

The logfiles have parts (_number.log). The RACE_LogfileOffset consists of a pair (part number, offset in part).

Note, the layouts of data structures in the dumpables may the natural binary representations chosen by the compiler. This must be considered when porting an RACE to a different architecture.
To be portable, the derivations of the RACE_Dumpable should use marshalling/demarshalling.
A better idea is to convert the logfile to ASCII (logfile2ascii) or to build a special converter.

The main idea is that the logfile consists of variable length entries.

The header of this entry must contain the length and a type identifier. After that, the special data is dumped.

The RACE_LogSwitchingStrategy

The switching of the Logfile is controlled by the RACE_LogSwitchingStrategy. The Logfile can be switched The RACE_Logger informs the RACE_LogSwitchingStrategy after putting the RACE_Dumpable into the RACE_Accumulator.

Problems to solve:

The Logger should only know the class RACE_Dumpable (and no more application depended class).
The switching of the Logfile must be done in an Inform_Offset-Callback of a snapshot
The Snapshot should be the last Entry in a logfile-Part
After queuing the Snapshot, a Flush-Command-Dumpable is queued. The InformOffset-Callback of the Snapshot triggers the SnapshotWritten-Callback-in the RACE_LogSwitchingStrategy, which triggers the switching.

The Index

The reader of the logfile may needs to position itself at a specific time in the logfile.

Thus, the index file contains a sequence of the pair (timestamp, RACE_LogFileOffset). See RACE_LogIndexLayout.h
There is only one index file for all parts in the logfile.

Management of the index is implemented in the RACE class framework since it is independent/reusable
The writing is implemented by RACE_LogIndexWriter, the reading and positioning is handled by RACE_LogIndexPositioner.

Reference Counter Pattern

Two threads, the main thread and the logger thread, have access to an object.
An object may be deleted only when its no longer used. For example, think of a Snapshot which waits on InformOffset callbacks triggered by the dumpables. There may be a method CheckRightToExist which checks whether anyone needs an object.

Reading of the Logfile

The interpretion of a logfile depends on the application. Here a few stripped remarks from an industrial project using RACE:

LogfileReader and LogSemantics

The LogfileReader reads the Logfiles and interprets them with a LogfileSemantic.

The logfile reader has a special buffer which grows as needed for a LogfileEntry. Another possibility could be working the RACE_Message_Blocks.

logfile2ascii has a special semantic to convert the binary logfile data to human readable text strings.

Using the RACE_LogIndexPositioner we can position ourselves within the Logfile.

The RecoverState is a LogFileSemantic which reads a snapshot. It has an iteration semantic (cf. logfile2ascii). With this combination logfile2ascii can list a snapshot and then the logfile.


Computerscience and Networkassocation Ravensburg e.V Rudolf Weber and friends