/*
* 这个示例程序演示了使用Berkeley DB的replication功能的方法。
*
* Berkeley DB提供了一套基本API和一套replication manager API来使用
* 它的replication功能。
* 前者有更大的灵活性,用户可以选择各种网络协议来实现数据传输,并且使用各种
* 线程库来进行多线程编程。同时由于使用最基本的API, 用户可以定制选举和
* replication系统的内部消息处理方式,非常灵活。所以这也意味着用户需要更多
* 的代码来使用Berkeley DB replication功能,开发难度相对后者较大;
* 后者基于TCP/IP协议和pthread线程库(Windows上面使用win32线程库),并且按照通常
* 的需求,对选举和内部消息处理进行了通用的处理,并且通过让用户配置
* 策略(policy)和参数的方式提供一定的灵活性和可定制性。
*
* 本程序基于Berkeley DB的replication manager。
*
*
* 名词术语:
*
* site:一个(数据库环境, IP地址, 端口(port))的三元组。所以,多个site可以处于
* 不同的计算机上面,也可以在同一个计算机上面,只要配置了不同的端口并使用不同
* 的数据库环境目录。
*
* replication group:由若干个site组成的集合,其中有且只有一个site 可以写入
* 数据到数据库当中, 称为master;其余的site只可以从中读取数据,称为replica。
*
* master:接受读写请求。如果写入数据,那么这些写入的数据不仅会更新master
* 自己的数据库,还会通过Berkeley DB的
* replication功能,被传播到replication group当中的replica上面。一个
* replication group通过选举或者显式指定的方式,得到master。
*
* replica:接受读请求。从master写入的数据会自动传播到每一个replica上面,从而
* 将replica上面的数据更新。任何一个replica在选举的时候,都有可能成为新的master
*
* 一个replication group作为一个整体来看的话,它比一个单机有
* 更大的数据读写能力,因为所有的site都可以提供数据,自然读数据的吞吐量更大;
* 并且master可以被配置为只写入数据而不必理会读数据请求,由replica提供数据,
* 这样,写入的吞吐量也更大。
*
* replication group比单机的另一个优势,就是高可靠性(high availability),因为,
* 其中如果任何一个site脱离(断电,网络故障,应用程序错误等),这个组都可以
* 继续服务:如果一个replica脱离,那么
* 还有其他replica可以提供数据,并且这个脱离的replica可以重新加入该组,其数据
* 被自动更新到最新;如果master脱离,那么该组的所有replica举行选举,从中选
* 出新的master。
*/
int
main(argc, argv)
int argc;
char *argv[];
{
DB_ENV *dbenv;
SETUP_DATA setup_info;
repsite_t *site_list;
APP_DATA my_app_data;
thread_t ckp_thr, lga_thr;
supthr_args sup_args;
u_int32_t start_policy;
int i, ret, t_ret;

memset(&setup_info, 0, sizeof(SETUP_DATA));
setup_info.progname = progname;
memset(&my_app_data, 0, sizeof(APP_DATA));
dbenv = NULL;
ret = 0;
/*
 * 默认使用选举的方法选出master。替代方案就是指定某一个site是master,
 * 其他site是replica。
 */
start_policy = DB_REP_ELECTION;

/* 创建一个拥有replication功能的数据库环境,但还不打开。 */
if ((ret = create_env(progname, &dbenv)) != 0)
 goto err;
dbenv->app_private = &my_app_data;

/*
 * 配置replication消息处理回调函数。
 * 有任何replication消息,Berkeley DB都会调用该函数来处理。
 */
(void)dbenv->set_event_notify(dbenv, event_callback);

/* Parse command line and perform common replication setup. */
if ((ret = common_rep_setup(dbenv, argc, argv, &setup_info)) != 0)
 goto err;

/*
 * Perform repmgr-specific setup based on command line options.
 * 如果显式指定了master,那么就使用DB_REP_MASTER把本site设置为master,
 * 并用DB_REP_CLIENT把其他site设置为replica。
 */
if (setup_info.role == MASTER)
 start_policy = DB_REP_MASTER;
else if (setup_info.role == CLIENT)
 start_policy = DB_REP_CLIENT;
/*
 * 设置本地site信息,也就是IP地址和端口信息。因为site是一个
 * (数据库环境,IP地址,port)的三元组,而数据库环境我们已经创建好
 * 了。
 */
if ((ret = dbenv->repmgr_set_local_site(dbenv, setup_info.self.host,
    setup_info.self.port, 0)) != 0) {
 fprintf(stderr, "Could not set listen address (%d)./n", ret);
 goto err;
}
site_list = setup_info.site_list;
/*
 * 添加其他site的信息到本site。如果显式指定master,那么一个replica
 * 一定要知道master的信息;如果选举,那么每一个site要知道其他所有
 * site的信息。
 *
 * 两个site可以成为peer关系,通过在下面的函数中设置
 * DB_REPMGR_PEER来指定。一个replica可以被它的peer更新到最新,这样
 * 就为master去掉了更新过时的replica的负担。
 */
for (i = 0; i < setup_info.remotesites; i++) {
 if ((ret = dbenv->repmgr_add_remote_site(dbenv,
     site_list[i].host, site_list[i].port, NULL,
     site_list[i].peer ? DB_REPMGR_PEER : 0)) != 0) {
  dbenv->err(dbenv, ret,
      "Could not add site %s:%d", site_list[i].host,
      (int)site_list[i].port);
  goto err;
 }
}

/*
 * Configure heartbeat timeouts so that repmgr monitors the
 * health of the TCP connection.  Master sites broadcast a heartbeat
 * at the frequency specified by the DB_REP_HEARTBEAT_SEND timeout.
 * Client sites wait for message activity the length of the
 * DB_REP_HEARTBEAT_MONITOR timeout before concluding that the
 * connection to the master is lost.  The DB_REP_HEARTBEAT_MONITOR
 * timeout should be longer than the DB_REP_HEARTBEAT_SEND timeout.
 */
if ((ret = dbenv->rep_set_timeout(dbenv, DB_REP_HEARTBEAT_SEND,
    5000000)) != 0)
 dbenv->err(dbenv, ret,
     "Could not set heartbeat send timeout./n");
if ((ret = dbenv->rep_set_timeout(dbenv, DB_REP_HEARTBEAT_MONITOR,
    10000000)) != 0)
 dbenv->err(dbenv, ret,
     "Could not set heartbeat monitor timeout./n");

/*
 * The following repmgr features may also be useful to your
 * application.  See Berkeley DB documentation for more details.
 *  - Two-site strict majority rule - In a two-site replication
 *    group, require both sites to be available to elect a new
 *    master.
 *  - Timeouts - Customize the amount of time repmgr waits
 *    for such things as waiting for acknowledgements or attempting
 *    to reconnect to other sites.
 *  - Site list - return a list of sites currently known to repmgr.
 */
/* 打开数据库环境。*/
if ((ret = env_init(dbenv, setup_info.home)) != 0)
 goto err;

/* Start checkpoint and log archive threads. */
sup_args.dbenv = dbenv;
sup_args.shared = &my_app_data.shared_data;
if ((ret = start_support_threads(dbenv, &sup_args, &ckp_thr,
    &lga_thr)) != 0)
 goto err;

if ((ret = dbenv->repmgr_start(dbenv, 3, start_policy)) != 0)
 goto err;

if ((ret = doloop(dbenv, &my_app_data.shared_data)) != 0) {
 dbenv->err(dbenv, ret, "Client failed");
 goto err;
}

/* Finish checkpoint and log archive threads. */
if ((ret = finish_support_threads(&ckp_thr, &lga_thr)) != 0)
 goto err;

/*
 * We have used the DB_TXN_NOSYNC environment flag for improved
 * performance without the usual sacrifice of transactional durability,
 * as discussed in the "Transactional guarantees" page of the Reference
 * Guide: if one replication site crashes, we can expect the data to
 * exist at another site.  However, in case we shut down all sites
 * gracefully, we push out the end of the log here so that the most
 * recent transactions don't mysteriously disappear.
 */
if ((ret = dbenv->log_flush(dbenv, NULL)) != 0) {
 dbenv->err(dbenv, ret, "log_flush");
 goto err;
}

err:
if (dbenv != NULL &&
    (t_ret = dbenv->close(dbenv, 0)) != 0) {
 fprintf(stderr, "failure closing env: %s (%d)/n",
     db_strerror(t_ret), t_ret);
 if (ret == 0)
  ret = t_ret;
}

return (ret);
}

/*
* 事件通知函数。当使用replication manager的时候,该事件已经被
* replication manager 适当地处理好了,所以在这个回调函数当中,
* 我们只需要简单地更新状态或者打印消息即可。
*/
static void
event_callback(dbenv, which, info)
DB_ENV *dbenv;
u_int32_t which;
void *info;
{
APP_DATA *app = dbenv->app_private;
SHARED_DATA *shared = &app->shared_data;

info = NULL;    /* Currently unused. */

switch (which) {
case DB_EVENT_REP_CLIENT:
 shared->is_master = 0;
 shared->in_client_sync = 1;
 break;

case DB_EVENT_REP_MASTER:
 shared->is_master = 1;
 shared->in_client_sync = 0;
 break;

case DB_EVENT_REP_NEWMASTER:
 shared->in_client_sync = 1;
 break;

case DB_EVENT_REP_PERM_FAILED:
 /*
  * Did not get enough acks to guarantee transaction
  * durability based on the configured ack policy.  This
  * transaction will be flushed to the master site's
  * local disk storage for durability.
  */
 printf(
   "Insufficient acknowledgements to guarantee transaction durability./n");
 break;

case DB_EVENT_REP_STARTUPDONE:
 shared->in_client_sync = 0;
 break;

default:
 dbenv->errx(dbenv, "ignoring event %d", which);
}
}


未完,下文见 Berkeley DB示例程序详解(3.2)