#include "db_config.h"
#include "db_int.h"
#include "dbinc/log.h"
static void __rep_find_entry __P((ENV *, REP *, int, REP_LEASE_ENTRY **));
int
__rep_update_grant(env, ts)
ENV *env;
db_timespec *ts;
{
DBT lease_dbt;
DB_LOG *dblp;
DB_REP *db_rep;
LOG *lp;
REP *rep;
__rep_grant_info_args gi;
db_timespec mytime;
u_int8_t buf[__REP_GRANT_INFO_SIZE];
int master, ret;
size_t len;
db_rep = env->rep_handle;
rep = db_rep->region;
dblp = env->lg_handle;
lp = dblp->reginfo.primary;
timespecclear(&mytime);
__os_gettime(env, &mytime, 1);
timespecadd(&mytime, &rep->lease_duration);
REP_SYSTEM_LOCK(env);
if (IN_ELECTION(rep)) {
REP_SYSTEM_UNLOCK(env);
return (0);
}
if (timespeccmp(&mytime, &rep->grant_expire, >))
rep->grant_expire = mytime;
F_CLR(rep, REP_F_LEASE_EXPIRED);
REP_SYSTEM_UNLOCK(env);
gi.msg_sec = (u_int32_t)ts->tv_sec;
gi.msg_nsec = (u_int32_t)ts->tv_nsec;
if ((ret = __rep_grant_info_marshal(env, &gi, buf,
__REP_GRANT_INFO_SIZE, &len)) != 0)
return (ret);
DB_INIT_DBT(lease_dbt, buf, len);
if ((master = rep->master_id) != DB_EID_INVALID && rep->priority > 0)
(void)__rep_send_message(env, master, REP_LEASE_GRANT,
&lp->max_perm_lsn, &lease_dbt, 0, 0);
return (0);
}
int
__rep_islease_granted(env)
ENV *env;
{
DB_REP *db_rep;
REP *rep;
db_timespec mytime;
db_rep = env->rep_handle;
rep = db_rep->region;
timespecclear(&mytime);
__os_gettime(env, &mytime, 1);
return (timespeccmp(&mytime, &rep->grant_expire, <=) ? 1 : 0);
}
int
__rep_lease_table_alloc(env, nsites)
ENV *env;
u_int32_t nsites;
{
REGENV *renv;
REGINFO *infop;
REP *rep;
REP_LEASE_ENTRY *le, *table;
int *lease, ret;
u_int32_t i;
rep = env->rep_handle->region;
infop = env->reginfo;
renv = infop->primary;
MUTEX_LOCK(env, renv->mtx_regenv);
if (rep->lease_off != INVALID_ROFF) {
__env_alloc_free(infop,
R_ADDR(infop, rep->lease_off));
rep->lease_off = INVALID_ROFF;
}
ret = __env_alloc(infop, (size_t)nsites * sizeof(REP_LEASE_ENTRY),
&lease);
MUTEX_UNLOCK(env, renv->mtx_regenv);
if (ret != 0)
return (ret);
else
rep->lease_off = R_OFFSET(infop, lease);
table = R_ADDR(infop, rep->lease_off);
for (i = 0; i < nsites; i++) {
le = &table[i];
le->eid = DB_EID_INVALID;
timespecclear(&le->start_time);
timespecclear(&le->end_time);
ZERO_LSN(le->lease_lsn);
}
return (0);
}
int
__rep_lease_grant(env, rp, rec, eid)
ENV *env;
__rep_control_args *rp;
DBT *rec;
int eid;
{
DB_REP *db_rep;
REP *rep;
__rep_grant_info_args gi;
REP_LEASE_ENTRY *le;
db_timespec msg_time;
int ret;
db_rep = env->rep_handle;
rep = db_rep->region;
if ((ret = __rep_grant_info_unmarshal(env,
&gi, rec->data, rec->size, NULL)) != 0)
return (ret);
timespecset(&msg_time, gi.msg_sec, gi.msg_nsec);
le = NULL;
REP_SYSTEM_LOCK(env);
__rep_find_entry(env, rep, eid, &le);
DB_ASSERT(env, le != NULL);
VPRINT(env, (env, DB_VERB_REP_LEASE,
"lease_grant: grant msg time %lu %lu",
(u_long)msg_time.tv_sec, (u_long)msg_time.tv_nsec));
if (le->eid == DB_EID_INVALID ||
timespeccmp(&msg_time, &le->start_time, >)) {
le->eid = eid;
le->start_time = msg_time;
le->end_time = le->start_time;
timespecadd(&le->end_time, &rep->lease_duration);
VPRINT(env, (env, DB_VERB_REP_LEASE,
"lease_grant: eid %d, start %lu %lu, end %lu %lu, duration %lu %lu",
le->eid, (u_long)le->start_time.tv_sec, (u_long)le->start_time.tv_nsec,
(u_long)le->end_time.tv_sec, (u_long)le->end_time.tv_nsec,
(u_long)rep->lease_duration.tv_sec, (u_long)rep->lease_duration.tv_nsec));
}
if (LOG_COMPARE(&rp->lsn, &le->lease_lsn) > 0) {
le->lease_lsn = rp->lsn;
VPRINT(env, (env, DB_VERB_REP_LEASE,
"lease_grant: eid %d, lease_lsn [%lu][%lu]",
le->eid, (u_long)le->lease_lsn.file,
(u_long)le->lease_lsn.offset));
}
REP_SYSTEM_UNLOCK(env);
return (0);
}
static void
__rep_find_entry(env, rep, eid, lep)
ENV *env;
REP *rep;
int eid;
REP_LEASE_ENTRY **lep;
{
REGINFO *infop;
REP_LEASE_ENTRY *le, *table;
u_int32_t i;
infop = env->reginfo;
table = R_ADDR(infop, rep->lease_off);
for (i = 0; i < rep->config_nsites; i++) {
le = &table[i];
if (le->eid == eid || le->eid == DB_EID_INVALID) {
*lep = le;
return;
}
}
return;
}
int
__rep_lease_check(env, refresh)
ENV *env;
int refresh;
{
DB_LOG *dblp;
DB_LSN lease_lsn;
DB_REP *db_rep;
LOG *lp;
REGINFO *infop;
REP *rep;
REP_LEASE_ENTRY *le, *table;
db_timespec curtime;
int max_tries, ret, tries;
u_int32_t i, min_leases, valid_leases;
infop = env->reginfo;
tries = 0;
db_rep = env->rep_handle;
rep = db_rep->region;
dblp = env->lg_handle;
lp = dblp->reginfo.primary;
LOG_SYSTEM_LOCK(env);
lease_lsn = lp->max_perm_lsn;
LOG_SYSTEM_UNLOCK(env);
#ifdef HAVE_STATISTICS
rep->stat.st_lease_chk++;
#endif
max_tries = (int)(rep->lease_timeout / (LEASE_REFRESH_USEC / 2));
if (max_tries < LEASE_REFRESH_MIN)
max_tries = LEASE_REFRESH_MIN;
retry:
REP_SYSTEM_LOCK(env);
min_leases = rep->config_nsites / 2;
ret = 0;
__os_gettime(env, &curtime, 1);
VPRINT(env, (env, DB_VERB_REP_LEASE,
"%s %d of %d refresh %d min_leases %lu curtime %lu %lu, maxLSN [%lu][%lu]",
"lease_check: try ", tries, max_tries, refresh,
(u_long)min_leases, (u_long)curtime.tv_sec,
(u_long)curtime.tv_nsec,
(u_long)lease_lsn.file,
(u_long)lease_lsn.offset));
table = R_ADDR(infop, rep->lease_off);
for (i = 0, valid_leases = 0;
i < rep->config_nsites && valid_leases < min_leases; i++) {
le = &table[i];
if (le->eid != DB_EID_INVALID) {
VPRINT(env, (env, DB_VERB_REP_LEASE,
"lease_check: valid %lu eid %d, lease_lsn [%lu][%lu]",
(u_long)valid_leases, le->eid,
(u_long)le->lease_lsn.file,
(u_long)le->lease_lsn.offset));
VPRINT(env, (env, DB_VERB_REP_LEASE,
"lease_check: endtime %lu %lu",
(u_long)le->end_time.tv_sec,
(u_long)le->end_time.tv_nsec));
}
if (le->eid != DB_EID_INVALID &&
timespeccmp(&le->end_time, &curtime, >=) &&
LOG_COMPARE(&le->lease_lsn, &lease_lsn) >= 0)
valid_leases++;
}
REP_SYSTEM_UNLOCK(env);
VPRINT(env, (env, DB_VERB_REP_LEASE, "valid %lu, min %lu",
(u_long)valid_leases, (u_long)min_leases));
if (valid_leases < min_leases) {
#ifdef HAVE_STATISTICS
rep->stat.st_lease_chk_misses++;
#endif
if (!refresh || tries > max_tries)
ret = DB_REP_LEASE_EXPIRED;
else {
if (((tries % 10) == 5 &&
(ret = __rep_lease_refresh(env)) == 0) ||
(tries % 10) != 5) {
if (tries > 0)
__os_yield(env, 0, LEASE_REFRESH_USEC);
tries++;
#ifdef HAVE_STATISTICS
rep->stat.st_lease_chk_refresh++;
#endif
goto retry;
}
}
}
if (ret == DB_REP_LEASE_EXPIRED)
RPRINT(env, (env, DB_VERB_REP_LEASE,
"lease_check: Expired. Only %lu valid",
(u_long)valid_leases));
return (ret);
}
int
__rep_lease_refresh(env)
ENV *env;
{
DBT rec;
DB_LOGC *logc;
DB_LSN lsn;
int ret, t_ret;
if ((ret = __log_cursor(env, &logc)) != 0)
return (ret);
memset(&rec, 0, sizeof(rec));
memset(&lsn, 0, sizeof(lsn));
if ((ret = __rep_log_backup(env, logc, &lsn, REP_REC_PERM)) != 0) {
if (ret == DB_NOTFOUND)
ret = 0;
goto err;
}
if ((ret = __logc_get(logc, &lsn, &rec, DB_CURRENT)) != 0)
goto err;
(void)__rep_send_message(env, DB_EID_BROADCAST, REP_LOG, &lsn,
&rec, REPCTL_LEASE, 0);
err: if ((t_ret = __logc_close(logc)) != 0 && ret == 0)
ret = t_ret;
return (ret);
}
int
__rep_lease_expire(env)
ENV *env;
{
DB_REP *db_rep;
REGINFO *infop;
REP *rep;
REP_LEASE_ENTRY *le, *table;
int ret;
u_int32_t i;
ret = 0;
db_rep = env->rep_handle;
rep = db_rep->region;
infop = env->reginfo;
if (rep->lease_off != INVALID_ROFF) {
table = R_ADDR(infop, rep->lease_off);
for (i = 0; i < rep->config_nsites; i++) {
le = &table[i];
le->end_time = le->start_time;
}
}
return (ret);
}
db_timeout_t
__rep_lease_waittime(env)
ENV *env;
{
DB_REP *db_rep;
REP *rep;
db_timespec exptime, mytime;
db_timeout_t to;
db_rep = env->rep_handle;
rep = db_rep->region;
exptime = rep->grant_expire;
to = 0;
RPRINT(env, (env, DB_VERB_REP_LEASE,
"wait_time: grant_expire %lu %lu lease_to %lu",
(u_long)exptime.tv_sec, (u_long)exptime.tv_nsec,
(u_long)rep->lease_timeout));
if (!timespecisset(&exptime)) {
if (!F_ISSET(rep, REP_F_LEASE_EXPIRED))
to = rep->lease_timeout;
} else {
__os_gettime(env, &mytime, 1);
RPRINT(env, (env, DB_VERB_REP_LEASE,
"wait_time: mytime %lu %lu, grant_expire %lu %lu",
(u_long)mytime.tv_sec, (u_long)mytime.tv_nsec,
(u_long)exptime.tv_sec, (u_long)exptime.tv_nsec));
if (timespeccmp(&mytime, &exptime, <=)) {
timespecsub(&exptime, &mytime);
DB_TIMESPEC_TO_TIMEOUT(to, &exptime, 1);
}
}
return (to);
}