Using eXtremeDB Database Events and Time-To-Live Features in C#

As explained in the introduction page, eXtremeDB provides the capability to automatically delete obsolete database objects through the Time-To-Live feature, and to manage database object new, delete, update and checkpoint events. The C# APIs for these features are explained in the sections below.

Time-To-Live

The Time-To-Live (TTL) mechanism facilitates automatic deletion of objects according to TTL policies. Two TTL policies are supported: MaxCount and MaxTime. The former sets an object count threshold, while the latter sets an object age threshold. Both policies can be set for a single class at the same time.

TTL policies are set using class annotations, just as for other eXtremeDB properties. (Please note that object age threshold in C# is always specified in microseconds.)

 
    [TTL(MaxCount=10, MaxTime=5000000)]
    class A
    {
    // ...
    }
     

Note that the MaxTime policy relies on the current system time. Changing the system clock will affect this policy. This can have important consequences in a distributed database as explained in the following section.

Side effects in a distributed environment

For distributed databases in a network (using eXtremeDB Cluster) it is important to note that clocks need to be carefully synchronized between machines participating in a cluster when the MaxTime policy is used. Bear in mind the following:

1. In the cluster environment, the TTL clock is verified on the transaction initiator side only in the beginning of the commit phase 1. On the remote side the clock time is not verified and the transaction is applied regardless of the actual clock on that node. If the transaction is applied successfully on the remote nodes, the notifications are sent back and the initiator commits the transaction. The databases are kept consistent regardless of the clock on each node (i.e. their content is the same on every node as long as the transactions are committed).

2. It is possible that a node will have some data that violates the node’s TTL requirements — in the example above the clock on the remote node could be far ahead of the clock on the local node where the transaction had been initiated. By the remote node clock the record should’ve been removed, but it is going to be kept in the node’s database regardless.

3. It is also possible that the object would be removed from the node’s database even if the TTL on that node is not expired, because it has expired on a different node. For example, suppose that node1 clock is set to 1 pm and node2 clock is set to 2 pm. The transaction is initiated on the node1 and gets successfully committed to both nodes despite the fact that the TTL for the record expired on the node2. Shortly after the transaction is committed, node2 initiates another transaction and after verifying the TTL condition by its own clock, removes the object just inserted, naturally propagating the delete to the entire cluster. The record is thus short-lived (shorter than expected).

Event Interfaces

The Event attribute defines what events will trigger application notifications. How the application handles the events is determined at run-time by the event handlers. For C# applications only asynchronous events are possible. In asynchronous event handling, the application spawns a separate thread to handle each type of event. The event thread calls the Connection method WaitEvent(). When the event occurs, eXtremeDB releases the thread. Upon releasing the thread, the runtime continues normal processing, so the handler thread runs in parallel with other threads, until it completes its processing and again calls WaitEvent().

There is a small window of possibility for another instance of the event to occur before the event handler has completed its task and calls WaitEvent() again to wait on the event (events are not queued). This window can be minimized if the handler delegates the processing of the event to yet another thread, allowing the handler thread to immediately wait on the event again. If this risk of an unhandled event cannot be tolerated, the application can maintain a separate table of unhandled events. Asynchronous events are activated after the transaction commits. If, within the scope of a single transaction, several objects are added, or deleted, or several fields are updated which have event handlers waiting, all the handlers will be activated simultaneously.

The individual threads to handle each event will have a ThreadProc like the following:

 
    private class ThreadParams
    {
        public Connection con;
        public string event_name;
        public ThreadParams(Connection con, string EventName)
        {
            this.con = con;
            this.event_name = EventName;
        }
    }
     
    private static void ThreadProc(object param)
    {
        ThreadParams tp = (ThreadParams)param;
        try
        {
            while (!exit)
            {
                tp.con.WaitEvent(tp.event_name);
            }
        }
        catch (DatabaseError x)
        {
            if (x.errorCode >= 50)
            {
                // Errors
            }
            else
            {
                // Normal return codes including MCO_S_EVENT_RELEASED
            }
        }
    }
     

The main application thread will sleep for some milliseconds, then proceed with normal database processing. When terminating, the application will call the Connection method ReleaseAllEvents() and then stop the event handler threads. The event handlers will catch an exception with error code MCO_S_EVENT_RELEASED when the event is released. (See the SDK sample samples/csharp/events/basic for further implementation details.)

C# applications can also handle asynchronous events through the Delegates mechanism by adding handlers to the Connection properties AsynchEvent and AsyncEventError and then calling method StartEventListeners(). (See the SDK sample samples/csharp/events/AsyncDelegate for further implementation details.)