[krbdev.mit.edu #7519] krb5-1.11: patch 3 of 4: allow slaves to capture ulog events
Richard Basch via RT
rt-comment at krbdev.mit.edu
Thu Dec 27 16:15:51 EST 2012
Allow slaves to capture ulog updates. This facilitates promoting a slave as
a master without having to have all other slaves do a full resync.
diff -ru src.02/kadmin/dbutil/dump.c src.03/kadmin/dbutil/dump.c
--- src.02/kadmin/dbutil/dump.c 2012-12-17 21:47:04.000000000 -0500
+++ src.03/kadmin/dbutil/dump.c 2012-12-27 04:57:22.521069453 -0500
@@ -2906,6 +2906,12 @@
log_ctx->ulog->kdb_last_time.useconds =
last_useconds;
+ log_ctx->ulog->kdb_first_sno = last_sno;
+ log_ctx->ulog->kdb_first_time.seconds =
+ last_seconds;
+ log_ctx->ulog->kdb_first_time.useconds =
+ last_useconds;
+
/*
* Sync'ing the header is not necessary on any OS and
* filesystem where the filesystem and virtual memory block
diff -ru src.02/lib/kdb/kdb_log.c src.03/lib/kdb/kdb_log.c
--- src.02/lib/kdb/kdb_log.c 2012-12-21 19:58:17.450702557 -0500
+++ src.03/lib/kdb/kdb_log.c 2012-12-27 04:51:46.720904259 -0500
@@ -208,9 +208,11 @@
* We need to overflow our sno, replicas will do full
* resyncs once they see their sno > than the masters.
*/
- if (cur_sno == (kdb_sno_t)-1)
+ if (cur_sno == (kdb_sno_t)-1 || cur_sno <= 0 ||
+ ulog->kdb_num > ulogentries) {
+ ulog->kdb_num = 0;
cur_sno = 1;
- else
+ } else
cur_sno++;
/*
@@ -240,29 +242,27 @@
if ((retval = ulog_sync_update(ulog, indx_log)))
return (retval);
- if (ulog->kdb_num < ulogentries)
- ulog->kdb_num++;
-
ulog->kdb_last_sno = cur_sno;
ulog->kdb_last_time = ktime;
- /*
- * Since this is a circular array, once we circled, kdb_first_sno is
- * always kdb_entry_sno + 1.
- */
- if (cur_sno > ulogentries) {
- i = upd->kdb_entry_sno % ulogentries;
+ /* This is a circular array; when we wrap, we have to adjust first_sno
*/
+
+ if (ulog->kdb_num < ulogentries)
+ ulog->kdb_num++;
+ else {
+ i = cur_sno % ulogentries;
indx_log = (kdb_ent_header_t *)INDEX(ulog, i);
ulog->kdb_first_sno = indx_log->kdb_entry_sno;
ulog->kdb_first_time = indx_log->kdb_time;
- } else if (cur_sno == 1) {
- ulog->kdb_first_sno = 1;
- ulog->kdb_first_time = indx_log->kdb_time;
}
- ulog_sync_header(ulog);
+ if (ulog->kdb_num == 1) {
+ ulog->kdb_first_sno = cur_sno;
+ ulog->kdb_first_time = ktime;
+ }
- return (0);
+ retval = ulog_finish_update(context, upd);
+ return (retval);
}
/*
@@ -299,19 +299,6 @@
}
/*
- * Set the header log details on the slave and sync it to file.
- */
-static void
-ulog_finish_update_slave(kdb_hlog_t *ulog, kdb_last_t lastentry)
-{
-
- ulog->kdb_last_sno = lastentry.last_sno;
- ulog->kdb_last_time = lastentry.last_time;
-
- ulog_sync_header(ulog);
-}
-
-/*
* Delete an entry to the update log.
*/
krb5_error_code
@@ -324,7 +311,7 @@
}
/*
- * Used by the slave or master (during ulog_check) to update it's hash db
from
+ * Used by the slave or master (during ulog_check) to update its hash db
from
* the incr update log.
*
* Must be called with lock held.
@@ -344,25 +331,24 @@
INIT_ULOG(context);
+ if (log_ctx && log_ctx->iproprole == IPROP_SLAVE) {
+ if ((retval = ulog_lock(context, KRB5_LOCKMODE_EXCLUSIVE)))
+ return (retval);
+ }
+
no_of_updates = incr_ret->updates.kdb_ulog_t_len;
upd = incr_ret->updates.kdb_ulog_t_val;
fupd = upd;
- /*
- * We reset last_sno and last_time to 0, if krb5_db2_db_put_principal
- * or krb5_db2_db_delete_principal fail.
- */
- errlast.last_sno = (unsigned int)0;
- errlast.last_time.seconds = (unsigned int)0;
- errlast.last_time.useconds = (unsigned int)0;
-
if ((retval = krb5_db_open(context, db_args,
KRB5_KDB_OPEN_RW|KRB5_KDB_SRV_TYPE_ADMIN)))
goto cleanup;
for (i = 0; i < no_of_updates; i++) {
+#if 0
if (!upd->kdb_commit)
continue;
+#endif
if (upd->kdb_deleted) {
dbprincstr = malloc((upd->kdb_princ_name.utf8str_t_len
@@ -417,6 +403,94 @@
goto cleanup;
}
+ if (log_ctx && log_ctx->iproprole == IPROP_SLAVE &&
+ upd->kdb_entry_sno >= 1 &&
+ ulog->kdb_hmagic == KDB_ULOG_HDR_MAGIC &&
+ ulog->kdb_state == KDB_STABLE) {
+
+ uint32_t ulogentries, indx, upd_size, recsize;
+ int ulogfd;
+ kdb_ent_header_t *indx_log;
+ XDR xdrs;
+
+ ulogfd = log_ctx->ulogfd;
+
+ ulogentries = log_ctx->ulogentries;
+ indx = (upd->kdb_entry_sno - 1) % ulogentries;
+
+ upd_size = xdr_sizeof((xdrproc_t)xdr_kdb_incr_update_t, upd);
+ recsize = sizeof(kdb_ent_header_t) + upd_size;
+
+ if (recsize > ulog->kdb_block) {
+ if ((retval = ulog_resize(ulog, ulogentries, ulogfd,
recsize)))
+ goto cleanup;
+ ulog_sync_header(ulog);
+ }
+
+ indx_log = (kdb_ent_header_t *)INDEX(ulog, indx);
+ (void) memset(indx_log, 0, ulog->kdb_block);
+
+ indx_log->kdb_umagic = KDB_ULOG_MAGIC;
+ indx_log->kdb_entry_size = upd_size;
+ indx_log->kdb_entry_sno = upd->kdb_entry_sno;
+ indx_log->kdb_time = upd->kdb_time;
+ indx_log->kdb_commit = TRUE;
+
+ xdrmem_create(&xdrs, (char *)indx_log->entry_data,
+ indx_log->kdb_entry_size, XDR_ENCODE);
+
+ if (!xdr_kdb_incr_update_t(&xdrs, upd)) {
+ retval = KRB5_LOG_CONV;
+ goto cleanup;
+ }
+
+ if ((retval = ulog_sync_update(ulog, indx_log)))
+ goto cleanup;
+
+ if (ulog->kdb_num < 0 || ulog->kdb_num > ulogentries)
+ (void) ulog_init_header(context, recsize);
+
+ ulog->kdb_last_sno = upd->kdb_entry_sno;
+ ulog->kdb_last_time = upd->kdb_time;
+
+ /*
+ * Allow the first entry to not reside in the ulog.
+ * As the first update is received, populate the first entry
+ * with the last serial info received from the full resync.
+ */
+ if (ulog->kdb_first_sno == 0 && ulog->kdb_num == 0) {
+ ulog->kdb_first_sno = upd->kdb_entry_sno;
+ ulog->kdb_first_time = upd->kdb_time;
+ }
+
+ if (ulog->kdb_num < ulogentries)
+ ulog->kdb_num++;
+ else {
+ /*
+ * The ulog is full, so we should update the first_sno
+ * based on the first entry in the circular buffer.
+ */
+ ulog->kdb_first_sno = ulog->kdb_last_sno - ulogentries + 1;
+ indx = (ulog->kdb_first_sno - 1) % ulogentries;
+ indx_log = (kdb_ent_header_t *)INDEX(ulog, indx);
+
+ if (indx_log->kdb_umagic == KDB_ULOG_MAGIC &&
+ indx_log->kdb_entry_sno == ulog->kdb_first_sno) {
+ ulog->kdb_first_time = indx_log->kdb_time;
+ } else {
+ /*
+ * The individual entry is corrupt; just reset the
+ * ulog to the last entry rather than disable the log.
+ * Hopefully, this won't ever occur.
+ */
+ ulog->kdb_first_sno = ulog->kdb_last_sno;
+ ulog->kdb_first_time = ulog->kdb_last_time;
+ ulog->kdb_num = 1;
+ }
+ (void) ulog_sync_header(ulog);
+ }
+ }
+
upd++;
}
@@ -425,10 +499,8 @@
ulog_free_entries(fupd, no_of_updates);
if (log_ctx && (log_ctx->iproprole == IPROP_SLAVE)) {
- if (retval)
- ulog_finish_update_slave(ulog, errlast);
- else
- ulog_finish_update_slave(ulog, incr_ret->lastentry);
+ (void) ulog_sync_header(ulog);
+ (void) ulog_lock(context, KRB5_LOCKMODE_UNLOCK);
}
return (retval);
@@ -445,15 +517,32 @@
{
XDR xdrs;
krb5_error_code retval = 0;
- unsigned int i;
+ unsigned int i, j, ulogentries, start_sno;
kdb_ent_header_t *indx_log;
kdb_incr_update_t *upd = NULL;
kdb_incr_result_t *incr_ret = NULL;
ulog->kdb_state = KDB_STABLE;
+ ulogentries = context->kdblog_context->ulogentries;
+
+ /*
+ * Edge condition/optimization.
+ * Reset ulog->kdb_first_* to equal the last entry if the log is empty.
+ */
+ if (ulog->kdb_num == 0 && ulog->kdb_first_sno == 0 &&
+ ulog->kdb_last_sno > 0) {
+
+ ulog->kdb_first_sno = ulog->kdb_last_sno;
+ ulog->kdb_first_time = ulog->kdb_last_time;
+ }
+
+ /* On a slave, the circular buffer may not begin at index 0. */
+ start_sno = ulog->kdb_last_sno - ulog->kdb_num + 1;
+
for (i = 0; i < ulog->kdb_num; i++) {
- indx_log = (kdb_ent_header_t *)INDEX(ulog, i);
+ j = (start_sno + i - 1) % ulogentries;
+ indx_log = (kdb_ent_header_t *)INDEX(ulog, j);
if (indx_log->kdb_umagic != KDB_ULOG_MAGIC) {
/*
@@ -646,8 +735,12 @@
return (errno);
}
- if ((caller == FKADMIND) || (caller == FKCOMMAND))
+ if ((caller == FKADMIND) || (caller == FKCOMMAND) || (caller ==
FKPROPD)) {
+ if (ulogentries < 2) /* Min log entries = 2 */
+ return (EINVAL);
+
ulog_filesize += ulogentries * ULOG_BLOCK;
+ }
if (extend_file_to(ulogfd, ulog_filesize) < 0)
return errno;
@@ -714,13 +807,13 @@
return (0);
}
- if ((caller == FKPROPLOG) || (caller == FKPROPD)) {
+ if (caller == FKPROPLOG) {
/* kproplog and kpropd don't need to do anything else. */
ulog_lock(context, KRB5_LOCKMODE_UNLOCK);
return (0);
}
- if (caller == FKADMIND) {
+ if (caller == FKADMIND || caller == FKPROPD) {
switch (ulog->kdb_state) {
case KDB_STABLE:
case KDB_UNSTABLE:
@@ -743,16 +836,19 @@
return (KRB5_LOG_ERROR);
}
}
- assert(caller == FKADMIND || caller == FKCOMMAND);
+ if (ulog->kdb_num == 0 && ulog->kdb_first_sno == 0 &&
ulog->kdb_last_sno) {
+ ulog->kdb_first_sno = ulog->kdb_last_sno;
+ ulog->kdb_first_time = ulog->kdb_last_time;
+ }
+
/*
* Reinit ulog if the log is being truncated or expanded after
* we have circled.
*/
if (ulog->kdb_num != ulogentries) {
- if ((ulog->kdb_num != 0) &&
- ((ulog->kdb_last_sno > ulog->kdb_num) ||
- (ulog->kdb_num > ulogentries))) {
+ if (ulog->kdb_num > ulogentries ||
+ ulog->kdb_first_sno < ulog->kdb_last_sno - ulog->kdb_num) {
ulog_reset(ulog);
ulog_sync_header(ulog);
@@ -762,6 +858,7 @@
* Expand ulog if we have specified a greater size
*/
if (ulog->kdb_num < ulogentries) {
+ ulog_filesize = sizeof (kdb_hlog_t);
ulog_filesize += ulogentries * ulog->kdb_block;
if (extend_file_to(ulogfd, ulog_filesize) < 0) {
@@ -796,7 +893,18 @@
INIT_ULOG(context);
ulogentries = log_ctx->ulogentries;
- retval = ulog_lock(context, KRB5_LOCKMODE_SHARED);
+ retval = ulog_lock(context, KRB5_LOCKMODE_SHARED |
KRB5_LOCKMODE_DONTBLOCK);
+ if (0
+#ifdef EWOULDBLOCK
+ || retval == EWOULDBLOCK
+#endif
+#ifdef EAGAIN
+ || retval == EAGAIN
+#endif
+ ) {
+ ulog_handle->ret = UPDATE_BUSY;
+ return (0);
+ }
if (retval)
return retval;
@@ -810,6 +918,23 @@
}
/*
+ * Defer a full resync when no log history is present to avoid looping.
+ */
+ if (ulog->kdb_num == 0 && ulog->kdb_last_sno == 0) {
+ ulog_handle->ret = UPDATE_BUSY;
+ (void) ulog_lock(context, KRB5_LOCKMODE_UNLOCK);
+ return (0);
+ } else if (ulog->kdb_last_sno <= 0) {
+ /*
+ * This should never happen. last_sno should only be zero if there
+ * are no log entries and should never be negative.
+ */
+ ulog_handle->ret = UPDATE_ERROR;
+ (void) ulog_lock(context, KRB5_LOCKMODE_UNLOCK);
+ return (KRB5_LOG_CORRUPT);
+ }
+
+ /*
* We need to lock out other processes here, such as kadmin.local,
* since we are looking at the last_sno and looking up updates. So
* we can share with other readers.
@@ -828,108 +953,124 @@
(last.last_sno < ulog->kdb_first_sno) ||
(last.last_sno == 0)) {
ulog_handle->lastentry.last_sno = ulog->kdb_last_sno;
+ ulog_handle->lastentry.last_time = ulog->kdb_last_time;
+
(void) ulog_lock(context, KRB5_LOCKMODE_UNLOCK);
(void) krb5_db_unlock(context);
ulog_handle->ret = UPDATE_FULL_RESYNC_NEEDED;
return (0);
- } else if (last.last_sno <= ulog->kdb_last_sno) {
- sno = last.last_sno;
-
- indx = (sno - 1) % ulogentries;
-
- indx_log = (kdb_ent_header_t *)INDEX(ulog, indx);
-
- /*
- * Validate the time stamp just to make sure it was the same sno
- */
- if ((indx_log->kdb_time.seconds == last.last_time.seconds) &&
- (indx_log->kdb_time.useconds == last.last_time.useconds)) {
-
- /*
- * If we have the same sno we return success
- */
- if (last.last_sno == ulog->kdb_last_sno) {
- (void) ulog_lock(context, KRB5_LOCKMODE_UNLOCK);
- (void) krb5_db_unlock(context);
- ulog_handle->ret = UPDATE_NIL;
- return (0);
- }
-
- count = ulog->kdb_last_sno - sno;
+ }
- ulog_handle->updates.kdb_ulog_t_val =
- (kdb_incr_update_t *)malloc(
- sizeof (kdb_incr_update_t) * count);
+ sno = last.last_sno;
- upd = ulog_handle->updates.kdb_ulog_t_val;
+ /*
+ * Validate the client state, including timestamp, against the ulog.
+ * Note: The first entry may not be in the ulog if the slave was
+ * promoted to a master and only has a limited ulog history.
+ */
+ if (sno == ulog->kdb_last_sno) {
+ if (last.last_time.seconds == ulog->kdb_last_time.seconds &&
+ last.last_time.useconds == ulog->kdb_last_time.useconds) {
- if (upd == NULL) {
- (void) ulog_lock(context, KRB5_LOCKMODE_UNLOCK);
- (void) krb5_db_unlock(context);
- ulog_handle->ret = UPDATE_ERROR;
- return (errno);
- }
-
- while (sno < ulog->kdb_last_sno) {
- indx = sno % ulogentries;
-
- indx_log = (kdb_ent_header_t *)
- INDEX(ulog, indx);
-
- (void) memset(upd, 0,
- sizeof (kdb_incr_update_t));
- xdrmem_create(&xdrs,
- (char *)indx_log->entry_data,
- indx_log->kdb_entry_size, XDR_DECODE);
- if (!xdr_kdb_incr_update_t(&xdrs, upd)) {
- (void) ulog_lock(context, KRB5_LOCKMODE_UNLOCK);
- (void) krb5_db_unlock(context);
- ulog_handle->ret = UPDATE_ERROR;
- return (KRB5_LOG_CONV);
- }
- /*
- * Mark commitment since we didn't
- * want to decode and encode the
- * incr update record the first time.
- */
- upd->kdb_commit = indx_log->kdb_commit;
+ (void) ulog_lock(context, KRB5_LOCKMODE_UNLOCK);
+ (void) krb5_db_unlock(context);
+ ulog_handle->ret = UPDATE_NIL;
+ return (0);
+ } else {
- upd++;
- sno++;
- } /* while */
+ ulog_handle->lastentry.last_sno = ulog->kdb_last_sno;
+ ulog_handle->lastentry.last_time = ulog->kdb_last_time;
- ulog_handle->updates.kdb_ulog_t_len = count;
+ (void) ulog_lock(context, KRB5_LOCKMODE_UNLOCK);
+ (void) krb5_db_unlock(context);
+ ulog_handle->ret = UPDATE_FULL_RESYNC_NEEDED;
+ return (0);
+ }
+ }
+ if (sno == ulog->kdb_first_sno) {
+ if (last.last_time.seconds != ulog->kdb_first_time.seconds ||
+ last.last_time.useconds != ulog->kdb_first_time.useconds) {
ulog_handle->lastentry.last_sno = ulog->kdb_last_sno;
- ulog_handle->lastentry.last_time.seconds =
- ulog->kdb_last_time.seconds;
- ulog_handle->lastentry.last_time.useconds =
- ulog->kdb_last_time.useconds;
- ulog_handle->ret = UPDATE_OK;
+ ulog_handle->lastentry.last_time = ulog->kdb_last_time;
(void) ulog_lock(context, KRB5_LOCKMODE_UNLOCK);
(void) krb5_db_unlock(context);
-
+ ulog_handle->ret = UPDATE_FULL_RESYNC_NEEDED;
return (0);
- } else {
- /*
- * We have time stamp mismatch or we no longer have
- * the slave's last sno, so we brute force it
- */
+ }
+ } else {
+ indx = (sno - 1) % ulogentries;
+ indx_log = (kdb_ent_header_t *)INDEX(ulog, indx);
+
+ if (indx_log->kdb_time.seconds != last.last_time.seconds ||
+ indx_log->kdb_time.useconds != last.last_time.useconds) {
+
+ ulog_handle->lastentry.last_sno = ulog->kdb_last_sno;
+ ulog_handle->lastentry.last_time = ulog->kdb_last_time;
+
(void) ulog_lock(context, KRB5_LOCKMODE_UNLOCK);
(void) krb5_db_unlock(context);
ulog_handle->ret = UPDATE_FULL_RESYNC_NEEDED;
-
return (0);
}
}
/*
- * Should never get here, return error
+ * We have now determined the client needs a list of incremental
updates.
*/
+ count = ulog->kdb_last_sno - sno;
+
+ ulog_handle->updates.kdb_ulog_t_val =
+ (kdb_incr_update_t *)malloc(sizeof (kdb_incr_update_t) * count);
+
+ upd = ulog_handle->updates.kdb_ulog_t_val;
+
+ if (upd == NULL) {
+ (void) ulog_lock(context, KRB5_LOCKMODE_UNLOCK);
+ (void) krb5_db_unlock(context);
+ ulog_handle->ret = UPDATE_ERROR;
+ return (errno);
+ }
+
+ while (sno < ulog->kdb_last_sno) {
+ indx = sno % ulogentries;
+ indx_log = (kdb_ent_header_t *)INDEX(ulog, indx);
+
+ (void) memset(upd, 0, sizeof (kdb_incr_update_t));
+ xdrmem_create(&xdrs,
+ (char *)indx_log->entry_data,
+ indx_log->kdb_entry_size, XDR_DECODE);
+ if (!xdr_kdb_incr_update_t(&xdrs, upd)) {
+ (void) ulog_lock(context, KRB5_LOCKMODE_UNLOCK);
+ (void) krb5_db_unlock(context);
+
+ ulog_free_entries(ulog_handle->updates.kdb_ulog_t_val,
+ sno - last.last_sno);
+ ulog_handle->updates.kdb_ulog_t_val = NULL;
+
+ ulog_handle->ret = UPDATE_ERROR;
+ return (KRB5_LOG_CONV);
+ }
+ upd->kdb_commit = TRUE;
+#if 0
+ upd->kdb_commit = indx_log->kdb_commit;
+#endif
+
+ upd++;
+ sno++;
+ } /* while */
+
+ ulog_handle->updates.kdb_ulog_t_len = count;
+
+ ulog_handle->lastentry.last_sno = ulog->kdb_last_sno;
+ ulog_handle->lastentry.last_time = ulog->kdb_last_time;
+ ulog_handle->ret = UPDATE_OK;
+
(void) ulog_lock(context, KRB5_LOCKMODE_UNLOCK);
- ulog_handle->ret = UPDATE_ERROR;
- return (KRB5_LOG_ERROR);
+ (void) krb5_db_unlock(context);
+
+ return (0);
}
krb5_error_code
Only in src.03/lib/kdb: kdb_log.c.~1~
Only in src.03/lib/kdb: kdb_log.c.~2~
More information about the krb5-bugs
mailing list