From aa779124dc8969762b9f9fcc922bf39a92eccc9c Mon Sep 17 00:00:00 2001
From: Marek Vavrusa <marek.vavrusa@nic.cz>
Date: Wed, 19 Jun 2013 16:28:57 +0200
Subject: [PATCH] Fixed a problem with too many differences (>1k) in one IXFR.

---
 src/knot/server/journal.c            |  2 +-
 src/knot/server/xfr-handler.c        | 66 ++++++++++++++++--------
 src/knot/server/zones.c              | 75 +++++++---------------------
 src/libknot/nameserver/name-server.c |  1 -
 src/libknot/updates/xfr-in.c         | 11 +++-
 5 files changed, 71 insertions(+), 84 deletions(-)

diff --git a/src/knot/server/journal.c b/src/knot/server/journal.c
index 2fe9f317ec..b725083a20 100644
--- a/src/knot/server/journal.c
+++ b/src/knot/server/journal.c
@@ -189,7 +189,7 @@ int journal_write_in(journal_t *j, journal_node_t **rn, uint64_t id, size_t len)
 
 		/* Check if it has been synced to disk. */
 		if (head->flags & JOURNAL_DIRTY) {
-			return KNOT_EAGAIN;
+			return KNOT_EBUSY;
 		}
 
 		/* Write back evicted node. */
diff --git a/src/knot/server/xfr-handler.c b/src/knot/server/xfr-handler.c
index 114fc714ec..0402fe482b 100644
--- a/src/knot/server/xfr-handler.c
+++ b/src/knot/server/xfr-handler.c
@@ -296,6 +296,7 @@ static void xfr_task_cleanup(knot_ns_xfr_t *rq)
 	/* Cleanup other data - so that the structure may be reused. */
 	rq->packet_nr = 0;
 	rq->tsig_data_size = 0;
+	hattrie_clear(rq->lookup_tree);
 }
 
 /*! \brief Close and free task. */
@@ -642,6 +643,28 @@ static int xfr_task_resp(xfrworker_t *w, knot_ns_xfr_t *rq)
 
 	return ret;
 }
+static int xfr_fallback_axfr(knot_ns_xfr_t *rq)
+{
+	log_server_notice("%s Retrying with AXFR.\n", rq->msg);
+	rq->wire_size = rq->wire_maxlen; /* Reset maximum bufsize */
+	int ret = xfrin_create_axfr_query(rq->zone->name, rq, &rq->wire_size, 1);
+	/* Send AXFR/IN query. */
+	if (ret == KNOT_EOK) {
+		ret = rq->send(rq->session, &rq->addr,
+		               rq->wire, rq->wire_size);
+		/* Switch to AXFR and return. */
+		if (ret == rq->wire_size) {
+			xfr_task_cleanup(rq);
+			rq->type = XFR_TYPE_AIN;
+			rq->msg[XFR_MSG_DLTTR] = 'A';
+			ret = KNOT_EOK;
+		} else {
+			ret = KNOT_ERROR;
+		}
+	}
+
+	return ret;
+}
 
 static int xfr_task_xfer(xfrworker_t *w, knot_ns_xfr_t *rq)
 {
@@ -675,25 +698,16 @@ static int xfr_task_xfer(xfrworker_t *w, knot_ns_xfr_t *rq)
 	dbg_xfr_verb("xfr: processed XFR pkt (%s)\n", knot_strerror(ret));
 
 	/* IXFR refused, try again with AXFR. */
-	if (rq->type == XFR_TYPE_IIN && ret == KNOT_EXFRREFUSED) {
-		log_server_notice("%s Transfer failed, fallback to AXFR.\n", rq->msg);
-		rq->wire_size = rq->wire_maxlen; /* Reset maximum bufsize */
-		ret = xfrin_create_axfr_query(rq->zone->name, rq, &rq->wire_size, 1);
-		/* Send AXFR/IN query. */
-		if (ret == KNOT_EOK) {
-			ret = rq->send(rq->session, &rq->addr,
-			               rq->wire, rq->wire_size);
-			/* Switch to AXFR and return. */
-			if (ret == rq->wire_size) {
-				xfr_task_cleanup(rq);
-				rq->type = XFR_TYPE_AIN;
-				rq->msg[XFR_MSG_DLTTR] = 'A';
-				return KNOT_EOK;
-			} else {
-				ret = KNOT_ERROR;
-			}
+	if (rq->type == XFR_TYPE_IIN) {
+		switch(ret) {
+		case KNOT_ESPACE: /* Fallthrough */
+			log_server_notice("%s Exceeded journal size limit.\n",
+			                  rq->msg);
+		case KNOT_EXFRREFUSED:
+			return xfr_fallback_axfr(rq);
+		default:
+			break;
 		}
-		return ret; /* Something failed in fallback. */
 	}
 
 	/* Handle errors. */
@@ -706,19 +720,26 @@ static int xfr_task_xfer(xfrworker_t *w, knot_ns_xfr_t *rq)
 	/* Only for successful xfers. */
 	if (ret > 0) {
 		ret = xfr_task_finalize(w, rq);
-
-		/* AXFR bootstrap timeout. */
 		if (ret != KNOT_EOK && !knot_zone_contents(rq->zone)) {
+
+			/* AXFR bootstrap timeout. */
 			zonedata_t *zd = (zonedata_t *)knot_zone_data(rq->zone);
 			int tmr_s = AXFR_BOOTSTRAP_RETRY * tls_rand();
 			zd->xfr_in.bootstrap_retry = tmr_s;
 			log_zone_info("%s Next attempt to bootstrap "
 			              "in %d seconds.\n",
 			              rq->msg, tmr_s / 1000);
+		} else if (ret == KNOT_EBUSY && rq->type == XFR_TYPE_IIN) {
+
+			/* Attempt to retry with AXFR. */
+			ret = xfr_fallback_axfr(rq);
+			if (ret == KNOT_EOK)
+				return ret;
 		} else {
-			zones_schedule_notify(rq->zone); /* NOTIFY */
-		}
 
+			/* Passed, schedule NOTIFYs. */
+			zones_schedule_notify(rq->zone);
+		}
 
 		/* Update REFRESH/RETRY */
 		zones_schedule_refresh(rq->zone);
@@ -1264,6 +1285,7 @@ int xfr_task_free(knot_ns_xfr_t *rq)
 
 	/* Free DNAME trie. */
 	hattrie_free(rq->lookup_tree);
+	rq->lookup_tree = NULL;
 
 	/* Free TSIG buffers. */
 	free(rq->digest);
diff --git a/src/knot/server/zones.c b/src/knot/server/zones.c
index e5ecf5455c..46ba65ea38 100644
--- a/src/knot/server/zones.c
+++ b/src/knot/server/zones.c
@@ -2836,48 +2836,6 @@ static int zones_store_changeset(const knot_changeset_t *chs, journal_t *j,
 	/* Reserve space for the journal entry. */
 	char *journal_entry = NULL;
 	ret = journal_map(j, k, &journal_entry, entry_size);
-
-	/* Sync to zonefile may be needed. */
-	while (ret == KNOT_EAGAIN) {
-		/* Cancel sync timer. */
-		event_t *tmr = zd->ixfr_dbsync;
-		if (tmr) {
-			dbg_xfr_verb("xfr: cancelling zonefile "
-			             "SYNC timer of '%s'\n",
-			             zd->conf->name);
-			evsched_cancel(tmr->parent, tmr);
-		}
-
-		/* Synchronize. */
-		dbg_xfr_verb("xfr: forcing zonefile SYNC "
-		             "of '%s'\n",
-		             zd->conf->name);
-		ret = zones_zonefile_sync(zone, j);
-		if (ret != KNOT_EOK && ret != KNOT_ERANGE) {
-			continue;
-		}
-
-		/* Reschedule sync timer. */
-		if (tmr) {
-			/* Fetch sync timeout. */
-			rcu_read_lock();
-			int timeout = zd->conf->dbsync_timeout;
-			timeout *= 1000; /* Convert to ms. */
-			rcu_read_unlock();
-
-			/* Reschedule. */
-			dbg_xfr_verb("xfr: resuming SYNC "
-			             "of '%s'\n",
-			             zd->conf->name);
-			evsched_schedule(tmr->parent, tmr,
-			                 timeout);
-
-		}
-
-		/* Attempt to map again. */
-		ret = journal_map(j, k, &journal_entry, entry_size);
-	}
-
 	if (ret != KNOT_EOK) {
 		dbg_xfr("Failed to map space for journal entry: %s.\n",
 		        knot_strerror(ret));
@@ -2960,8 +2918,7 @@ int zones_store_changesets(knot_zone_t *zone, knot_changesets_t *src)
 		return KNOT_EINVAL;
 	}
 
-//	knot_zone_t *zone = xfr->zone;
-//	knot_changesets_t *src = (knot_changesets_t *)xfr->data;
+	int ret = KNOT_EOK;
 
 	/* Fetch zone-specific data. */
 	zonedata_t *zd = (zonedata_t *)zone->data;
@@ -2974,7 +2931,6 @@ int zones_store_changesets(knot_zone_t *zone, knot_changesets_t *src)
 	if (j == NULL) {
 		return KNOT_EBUSY;
 	}
-	int ret = 0;
 
 	/* Begin writing to journal. */
 	for (unsigned i = 0; i < src->count; ++i) {
@@ -2982,17 +2938,24 @@ int zones_store_changesets(knot_zone_t *zone, knot_changesets_t *src)
 		knot_changeset_t* chs = src->sets + i;
 
 		ret = zones_store_changeset(chs, j, zone, zd);
-		if (ret != KNOT_EOK) {
-			journal_release(j);
-			return ret;
-		}
+		if (ret != KNOT_EOK)
+			break;
 	}
 
 	/* Release journal. */
 	journal_release(j);
 
+	/* Flush if the journal is full. */
+	event_t *tmr = zd->ixfr_dbsync;
+	if (ret == KNOT_EBUSY && tmr) {
+		log_server_notice("Journal for '%s' is full, flushing.\n",
+		                  zd->conf->name);
+		evsched_cancel(tmr->parent, tmr);
+		evsched_schedule(tmr->parent, tmr, 0);
+	}
+
 	/* Written changesets to journal. */
-	return KNOT_EOK;
+	return ret;
 }
 
 /*----------------------------------------------------------------------------*/
@@ -3125,8 +3088,7 @@ int zones_store_and_apply_chgsets(knot_changesets_t *chs,
 	}
 	if (ret != KNOT_EOK) {
 		log_zone_error("%s Failed to serialize and store "
-		               "changesets - %s\n", msgpref,
-		               knot_strerror(ret));
+		               "changesets.\n", msgpref);
 		/* Free changesets, but not the data. */
 		zones_store_changesets_rollback(transaction);
 		knot_free_changesets(&chs);
@@ -3137,8 +3099,7 @@ int zones_store_and_apply_chgsets(knot_changesets_t *chs,
 	apply_ret = xfrin_apply_changesets(zone, chs, new_contents);
 
 	if (apply_ret != KNOT_EOK) {
-		log_zone_error("%s Failed to apply changesets - %s\n",
-		               msgpref, knot_strerror(apply_ret));
+		log_zone_error("%s Failed to apply changesets.\n", msgpref);
 
 		/* Free changesets, but not the data. */
 		zones_store_changesets_rollback(transaction);
@@ -3150,8 +3111,7 @@ int zones_store_and_apply_chgsets(knot_changesets_t *chs,
 	ret = zones_store_changesets_commit(transaction);
 	if (ret != KNOT_EOK) {
 		/*! \todo THIS WILL LEAK!! xfrin_rollback_update() needed. */
-		log_zone_error("%s Failed to commit stored changesets "
-		               "- %s\n", msgpref, knot_strerror(apply_ret));
+		log_zone_error("%s Failed to commit stored changesets.\n", msgpref);
 		knot_free_changesets(&chs);
 		return ret;
 	}
@@ -3160,8 +3120,7 @@ int zones_store_and_apply_chgsets(knot_changesets_t *chs,
 	switch_ret = xfrin_switch_zone(zone, *new_contents, type);
 
 	if (switch_ret != KNOT_EOK) {
-		log_zone_error("%s Failed to replace current zone - %s\n",
-		               msgpref, knot_strerror(switch_ret));
+		log_zone_error("%s Failed to replace current zone.\n", msgpref);
 		// Cleanup old and new contents
 		xfrin_rollback_update(zone->contents, new_contents,
 		                      &chs->changes);
diff --git a/src/libknot/nameserver/name-server.c b/src/libknot/nameserver/name-server.c
index 00ec5c966b..4a95240181 100644
--- a/src/libknot/nameserver/name-server.c
+++ b/src/libknot/nameserver/name-server.c
@@ -4176,7 +4176,6 @@ int knot_ns_process_ixfrin(knot_nameserver_t *nameserver,
 
 	if (ret < 0) {
 		knot_packet_free(&xfr->query);
-		hattrie_clear(xfr->lookup_tree);
 		return ret;
 	} else if (ret > 0) {
 		dbg_ns("ns_process_ixfrin: IXFR finished\n");
diff --git a/src/libknot/updates/xfr-in.c b/src/libknot/updates/xfr-in.c
index 04d75eaed0..7aa95dbfa5 100644
--- a/src/libknot/updates/xfr-in.c
+++ b/src/libknot/updates/xfr-in.c
@@ -18,6 +18,8 @@
 #include <assert.h>
 #include <urcu.h>
 
+#include "knot/server/journal.h"
+
 #include "updates/xfr-in.h"
 
 #include "nameserver/name-server.h"
@@ -1159,8 +1161,13 @@ dbg_xfrin_exec_verb(
 			} else {
 				// normal SOA, start new changeset
 				(*chs)->count++;
-				if ((ret = knot_changesets_check_size(*chs))
-				     != KNOT_EOK) {
+				ret = knot_changesets_check_size(*chs);
+
+				/* Check changesets for maximum count (so they fit into journal). */
+				if ((*chs)->count > JOURNAL_NCOUNT)
+					ret = KNOT_ESPACE;
+
+				if (ret != KNOT_EOK) {
 					(*chs)->count--;
 					knot_rrset_deep_free(&rr, 1, 1);
 					goto cleanup;
-- 
GitLab