Synchronous Replication

Synchronous replication using the time-cognizant, two-phase commit protocol is the default mode. So it is sufficient for C/C++ master applications to call:

     
    mco_HA_master_params_t MasterParams;
    mco_HA_master_params_init( &MasterParams );
    mco_HA_set_master_params( db, &MasterParams );
     

In C# and Java applications:

 
    MasterConnection.Parameters MParams = new MasterConnection.Parameters();
    con.SetReplicationMode( MParams );
     

The eXtremeDB High Availability runtime does the initial synchronization in C/C++ master applications during the mco_HA_attach_replica() call and all other replication happens during the call to mco_trans_commit(); in C# and Java applications during the MasterConnection.AttachReplica() and MasterConnection.CommitTransaction() method.

On the replica side the eXtremeDB High Availability runtime manages initial replication for C/C++ applications within mco_HA_attach_master(). When mco_HA_attach_master() is called, control is passed to the eXtremeDB High Availability runtime which manages the initial synchronization of the replica database and then waits for and processes transaction data sent from the master during the master call to mco_trans_commit(). In C# and Java replica applications initial synchronization and subsequent transaction replication is managed by the eXtremeDB High Availability runtime during the ReplicaConnection.AttachMaster() method.

Implementation

Most C/C++ applications using eXtremeDB High Availability will employ just the following functions from the protocol layer API:

     
    MCO_RET mco_HA_start();
    void mco_HA_master_params_init();
    MCO_RET mco_HA_set_master_params();
    MCO_RET mco_HA_attach_replica();
    void mco_HA_replica_params_init();
    MCO_RET mco_HA_attach_master();
    MCO_RET mco_HA_stop();
     

Equivalently, most C# and Java applications using eXtremeDB High Availability will employ just the following API methods:

     
    Database db = new Database();
    db.Open();
    MasterConnection con = new MasterConnection(db);
    con.SetReplicationMode();
    con.AttachReplica();
    con.StopReplication();
    ReplicaConnection con = new ReplicaConnection(db);
    con.AttachMaster();
    con.Disconnect();
    db.Close();
     

Using the HA “interface” API on the master side:

To initiate replication on the master side the application must:

The following code fragment illustrates a simple C/C++ master implementation.

 
    void* ListenToReplicas ( void *p )
    {
        while ((! stop_flag ) {
            ret = mco_HA_attach_replica(...);
             
            if ( MCO_S_OK != ret )
            // something went wrong, examine the return code
            ...
            else
            // replica is connected successfully
        }
     
    }
     
    int main(int argc, char* argv[])
    {
        mco_HA_master_params_t MasterParams;
        …
        /* initialize HA subsystem */
        mco_HA_start();
        /* create the database */
        rc = mco_db_open_dev( dbName, dbname_get_dictionary(),°
        dev, n_dev, &db_params);
        …
         
        /* connect to the database, obtain a database handle */
        rc = mco_db_connect( dbName, &db );
        …
        /* set MASTER mode */
        mco_HA_master_params_init( &MasterParams );
        mco_HA_set_master_params( db, &MasterParams );
        /* create a ‘listener’ thread */
        rc = pthread_create (&ConnThread, NULL,
        (void*)ListenToReplicas, &ha);
         
        /* Continue with the regular database processing */
        while (master_has_some_work_to_do) {
        rc = mco_trans_start( db, MCO_READ_WRITE,
        MCO_TRANS_FOREGROUND, &t );
        ...
        rc = mco_trans_commit(t);
    }
     
    /* Detach and close connections to replicas */
    mco_HA_stop(db);
    }
     

Note that mco_HA_attach_replica() is called in the context of a separate “listener” task. Upon receiving a connection request, this function performs the following actions to “activate” the replica:

The mco_HA_attach_replica() function is described in detail in the API Reference section, but briefly its prototype is:

 
    mco_HA_attach_replica( mco_db_h db, char* masterport,
                timer_unit timeout);
                 

The timeout argument represents the “accept connection” timeout.

Note that If the master database is a shared-memory database and several processes access it, only one process (called the primary master) handles connections to replicas. So only the primary master process can call network-related functions such as mco_HA_keep_alive(), mco_HA_attach_replica(),etc.

 

A similar simple C# master implementation would look like the following:

     
    public void Listen() {
    MasterConnection con = new MasterConnection(db);
         
        while (listening) {
        if (con.AttachReplica(PORT, ATTACH_TIMEOUT)) {
            // Replica connected…
            }
        }
    con.Disconnect();
    }
     
    Master()
    {
        Database.Parameters parameters = new Database.Parameters();
        db = new Database(new ExtremedbWrapper(),
        Database.Mode.HighAvailabilitySupport);
        db.Open( dbName, parameters, DATABASE_SIZE);
        MasterConnection con = new MasterConnection(db);
         
        // set HA mode
        MasterConnection.Parameters MParams = newMasterConnection.Parameters(
        MasterConnection.MCO_HAMODE_HOTSYNCH );
        con.SetReplicationMode( MParams );
        MParams.listening = true;
         
        // start listen thread
        Thread listenThread = new Thread(new ThreadStart(Listen));
        listenThread.Start();
     
        // continue with the regular database processing
        while (master_has_some_work_to_do) {
        con.StartTransaction(Database.Transaction.ReadWrite);
        ...
        con.CommitTransaction();
    }
     
    // stop thread
    listening = false;
    listenThread.Join();
     
    con.Disconnect();
    db.Close();
    }
     

Using the HA “interface” API on the replica side:

To initiate replication on the replica-side the application must:

The following code fragment illustrates a simple C/C++ replica implementation.

 
    int main(int argc, char* argv[])
    {
        int stopReason = 0;
        mco_HA_replica_params_t ReplicaParams;
        ...
     
        /* initialize HA subsystem */
        mco_HA_start();
         
        /* create the database */
        rc = mco_db_open_dev( dbName, dbname_get_dictionary(),°
                    dev, n_dev, &db_params);
        …
        /* connect to the database, obtain a database handle */
        rc = mco_db_connect( dbName, &db );
        …
        /* set replica params */
        mco_HA_replica_params_init( &ReplicaParams );
        rc = mco_HA_attach_master( &db, mastername,
                        &ReplicaParams,
                        &stopReason,
                        TM_CONNECTION_TIMEOUT);
        return stopReason;
    }
     

Note that mco_HA_attach_master() is a blocking call. Essentially all control is turned over at this point to the eXtremeDB High Availability runtime which manages the initial synchronization and then commits transactions sent from the master. The function is described in detail in the API Reference section, but briefly its prototype is:

     
    MCO_RET mco_HA_attach_master( mco_db_h db,
    const char* conn_string,
    const mco_HA_replica_params_t* params,
    MCO_E_HA_REPLICA_STOP_REASON* stop_reason,
    timer_unit timeout);
     

The stop_reason indicates why the runtime has returned control to the application (include/ha/mcoha.h). When control is returned to the replica application, it should examine the stop_reason and make a decision on the course of further action. It could, for example, become the master application itself, or simply terminate. The timeout represents the connection timeout (the period of time the replica waits while attempting to make a connection to the master). The params structure contains a set of replica control flags (include/ha/mcoha.h). For example MCO_HAMODE_REPLICA_NOTIFICATION, etc.

A similar simple C# replica implementation would look like the following:

 
    public Slave()
    {
        Database.Parameters parameters = new Database.Parameters();
        parameters.MemPageSize = PAGE_SIZE;
        db = new Database(new ExtremedbWrapper(),
        Database.Mode.HighAvailabilitySupport);
        db.Open( dbName, parameters, DATABASE_SIZE );
         
        // start working thread
        running = true;
        Thread inspectThread = new Thread(new ThreadStart(Run));
        inspectThread.Start();
 
        ReplicaConnection con = new ReplicaConnection(db);
        ReplicaConnection.Parameters RParams = new ReplicaConnection.Parameters();
         
        if (!con.AttachMaster("localhost:" + PORT, replParams, CONNECT_TIMEOUT)) {
            Console.WriteLine("Failed to connect to master");
        }
 
        //stop working thread
        running = false;
        inspectThread.Join();
         
        Console.WriteLine("Replica is terminated");
        con.Disconnect();
        db.Close();
    }