diff --git a/src/knot/updates/ddns.c b/src/knot/updates/ddns.c
index 785a5bfedb6ff1c316acb6592a861e8efc13c949..abc05a62ce23ff3dbbbe17e070d063a9e82f068f 100644
--- a/src/knot/updates/ddns.c
+++ b/src/knot/updates/ddns.c
@@ -295,61 +295,70 @@ static int knot_ddns_check_prereq(const knot_rrset_t *rrset,
 
 /* ----------------------- changeset lists helpers -------------------------- */
 
+#warning TODO: Store changesets as a lookup structure
+
+/*!< \brief Returns true if \a cmp code returns true for one RR in list. */
+#define LIST_MATCH(l, cmp) \
+	knot_rr_ln_t *_n; \
+	WALK_LIST(_n, l) { \
+		const knot_rrset_t *list_rr = _n->rr; \
+		if (cmp) { \
+			return true; \
+		} \
+	}; \
+	return false;
+
+/*!< \brief Deletes RR from list if \a cmp code returns true for it. */
+#define LIST_DEL_MATCH(l, cmp) \
+	knot_rr_ln_t *_n; \
+	node_t *_nxt; \
+	WALK_LIST_DELSAFE(_n, _nxt, l) { \
+		knot_rrset_t *list_rr = _n->rr; \
+		if (cmp) { \
+			knot_rrset_free(&list_rr, NULL); \
+			rem_node((node_t *)_n); \
+		} \
+	};
+
 /*!< \brief Checks whether RR was already removed. */
 static bool removed_rr(const knot_changeset_t *changeset, const knot_rrset_t *rr)
 {
-	knot_rr_ln_t *n;
-	WALK_LIST(n, changeset->remove) {
-		const knot_rrset_t *list_rr = n->rr;
-		if (knot_rrset_equal(rr, list_rr, KNOT_RRSET_COMPARE_WHOLE)) {
-			return true;
-		}
-	};
+	LIST_MATCH(changeset->remove,
+	           knot_rrset_equal(rr, list_rr, KNOT_RRSET_COMPARE_WHOLE));
+}
 
-	return false;
+/*!< \brief Checks whether any CNAME RR under dname was added. */
+static bool cname_added(const knot_changeset_t *changeset, const knot_dname_t *d)
+{
+	knot_rrset_t mock_cname;
+	knot_rrset_init(&mock_cname, (knot_dname_t *)d,
+	                KNOT_RRTYPE_CNAME, KNOT_CLASS_IN);
+	LIST_MATCH(changeset->add,
+	           knot_rrset_equal(&mock_cname, list_rr, KNOT_RRSET_COMPARE_HEADER));
+}
+
+/*!< \brief Checks whether any RR under given name was added. */
+static bool name_added(const knot_changeset_t *changeset, const knot_dname_t *d)
+{
+	LIST_MATCH(changeset->add, knot_dname_is_equal(d, list_rr->owner));
 }
 
 /*!< \brief Removes RR from list, full equality check. */
 static void remove_rr_from_list(list_t *l, const knot_rrset_t *rr)
 {
-	knot_rr_ln_t *rr_node = NULL;
-	node_t *nxt = NULL;
-	WALK_LIST_DELSAFE(rr_node, nxt, *l) {
-		knot_rrset_t *rrset = rr_node->rr;
-		if (knot_rrset_equal(rrset, rr, KNOT_RRSET_COMPARE_WHOLE)) {
-			knot_rrset_free(&rrset, NULL);
-			rem_node((node_t *)rr_node);
-			return;
-		}
-	}
+	LIST_DEL_MATCH(*l, knot_rrset_equal(list_rr, rr, KNOT_RRSET_COMPARE_WHOLE));
 }
 
 /*!< \brief Removes RR from list, owner and type check. */
 static void remove_header_from_list(list_t *l, const knot_rrset_t *rr)
 {
-	knot_rr_ln_t *rr_node = NULL;
-	node_t *nxt = NULL;
-	WALK_LIST_DELSAFE(rr_node, nxt, *l) {
-		knot_rrset_t *rrset = rr_node->rr;
-		if (knot_rrset_equal(rrset, rr, KNOT_RRSET_COMPARE_HEADER)) {
-			knot_rrset_free(&rrset, NULL);
-			rem_node((node_t *)rr_node);
-		}
-	}
+	LIST_DEL_MATCH(*l, knot_rrset_equal(list_rr, rr, KNOT_RRSET_COMPARE_HEADER));
 }
 
 /*!< \brief Removes RR from list, owner check. */
 static void remove_owner_from_list(list_t *l, const knot_dname_t *owner)
 {
-	knot_rr_ln_t *rr_node = NULL;
-	node_t *nxt = NULL;
-	WALK_LIST_DELSAFE(rr_node, nxt, *l) {
-		knot_rrset_t *rrset = rr_node->rr;
-		if (knot_dname_is_equal(rrset->owner, owner)) {
-			knot_rrset_free(&rrset, NULL);
-			rem_node((node_t *)rr_node);
-		}
-	}
+	LIST_DEL_MATCH(*l, knot_dname_is_equal(list_rr->owner, owner));
 }
 
 /* --------------------- true/false helper functions ------------------------ */
@@ -393,9 +402,16 @@ static bool should_replace(const knot_rrset_t *chg_rrset,
 }
 
 /*!< \brief Returns true if node will be empty after update application. */
-static bool node_empty(const knot_node_t *node, const knot_changeset_t *changeset)
+static bool node_empty(const knot_node_t *node, knot_dname_t *owner,
+                       const knot_changeset_t *changeset)
 {
+	if (node == NULL && name_added(changeset, owner)) {
+		// Node created in update.
+		return false;
+	}
+
 	if (node == NULL) {
+		// Node empty.
 		return true;
 	}
 
@@ -406,6 +422,7 @@ static bool node_empty(const knot_node_t *node, const knot_changeset_t *changese
 		for (uint16_t j = 0; j < node_rrset.rrs.rr_count; ++j) {
 			knot_rrset_add_rr_from_rrset(&node_rr, &node_rrset, j, NULL);
 			if (!removed_rr(changeset, &node_rr)) {
+				// One of the RRs from node was not removed.
 				knot_rrs_clear(&node_rr.rrs, NULL);
 				return false;
 			}
@@ -437,9 +454,15 @@ static bool node_contains_rr(const knot_node_t *node,
 }
 
 /*!< \brief Returns true if CNAME is in this node. */
-static bool adding_to_cname(const knot_node_t *node,
+static bool adding_to_cname(const knot_dname_t *owner,
+                            const knot_node_t *node,
                             knot_changeset_t *changeset)
 {
+	if (cname_added(changeset, owner)) {
+		// Added a CNAME in this update.
+		return true;
+	}
+
 	if (node == NULL) {
 		// Node did not exist before update.
 		return false;
@@ -451,8 +474,13 @@ static bool adding_to_cname(const knot_node_t *node,
 		return false;
 	}
 
-	// Return true if we have not removed CNAME in this update.
-	return !removed_rr(changeset, &cname);
+	if (removed_rr(changeset, &cname)) {
+		// Node did contain CNAME, but it was removed in this update.
+		return false;
+	}
+
+	// CNAME present
+	return true;
 }
 
 /*!< \brief Used to ignore SOA deletions and SOAs with lower serial than zone. */
@@ -481,7 +509,7 @@ static bool skip_record_addition(knot_changeset_t *changeset,
 		if (should_replace(rr, rrset)) {
 			// Replacing singleton RR.
 			knot_rrset_free(&rrset, NULL);
-			rrset = rr;
+			rr_node->rr = rr;
 			return true;
 		} else if (knot_rrset_equal(rr, rrset, KNOT_RRSET_COMPARE_WHOLE)) {
 			// Freeing duplication.
@@ -599,7 +627,7 @@ static int process_add_cname(const knot_node_t *node,
 		}
 
 		return add_rr_to_chgset(rr, changeset, NULL);
-	} else if (!node_empty(node, changeset)) {
+	} else if (!node_empty(node, rr->owner, changeset)) {
 		// Other occupied node => ignore.
 		return KNOT_EOK;
 	} else {
@@ -665,7 +693,7 @@ static int process_add_normal(const knot_node_t *node,
                               knot_changeset_t *changeset,
                               int *apex_ns_rem)
 {
-	if (adding_to_cname(node, changeset)) {
+	if (adding_to_cname(rr->owner, node, changeset)) {
 		// Adding RR to CNAME node, ignore.
 		return KNOT_EOK;
 	}
@@ -708,16 +736,8 @@ static int process_rem_rr(const knot_rrset_t *rr,
                           knot_changeset_t *changeset,
                           int *apex_ns_rem)
 {
-	// Remove possible previously added RR
-	remove_rr_from_list(&changeset->add, rr);
-	if (node == NULL) {
-		// Removing from node that did not exists before update
-		return KNOT_EOK;
-	}
-
-	uint16_t type = rr->type;
 	const bool apex_ns = knot_node_rrtype_exists(node, KNOT_RRTYPE_SOA) &&
-	                     type == KNOT_RRTYPE_NS;
+	                     rr->type == KNOT_RRTYPE_NS;
 	if (apex_ns) {
 		const knot_rrs_t *ns_rrs = knot_node_rrs(node, KNOT_RRTYPE_NS);
 		if (*apex_ns_rem == ns_rrs->rr_count - 1) {
@@ -726,6 +746,13 @@ static int process_rem_rr(const knot_rrset_t *rr,
 		}
 	}
 
+	// Remove possible previously added RR
+	remove_rr_from_list(&changeset->add, rr);
+	if (node == NULL) {
+		// Removing from node that did not exists before update
+		return KNOT_EOK;
+	}
+
 	knot_rrset_t to_modify = knot_node_rrset(node, rr->type);
 	if (knot_rrset_empty(&to_modify)) {
 		// No such RRSet
@@ -754,12 +781,6 @@ static int process_rem_rrset(const knot_rrset_t *rrset,
                              const knot_node_t *node,
                              knot_changeset_t *changeset)
 {
-	// Removing all previously added RRs with this owner and type from changeset
-	remove_header_from_list(&changeset->add, rrset);
-	if (node == NULL) {
-		return KNOT_EOK;
-	}
-
 	if (rrset->type == KNOT_RRTYPE_SOA ||
 	    knot_rrtype_is_ddns_forbidden(rrset->type)) {
 		// Ignore SOA and DNSSEC removals.
@@ -772,6 +793,12 @@ static int process_rem_rrset(const knot_rrset_t *rrset,
 		return KNOT_EOK;
 	}
 
+	// Remove all previously added RRs with this owner and type from changeset
+	remove_header_from_list(&changeset->add, rrset);
+	if (node == NULL) {
+		return KNOT_EOK;
+	}
+
 	if (!knot_node_rrtype_exists(node, rrset->type)) {
 		// no such RR, ignore
 		return KNOT_EOK;
diff --git a/src/knot/zone/node.h b/src/knot/zone/node.h
index f491d8140fd17525bb8d0df0b91e245e99ba79af..b374bdd5e182192862d36274c0906bcb9c00e80d 100644
--- a/src/knot/zone/node.h
+++ b/src/knot/zone/node.h
@@ -405,6 +405,7 @@ static void inline knot_node_fill_rrset(const knot_node_t *node, uint16_t type,
                                         knot_rrset_t *rrset)
 {
 	if (node == NULL || rrset == NULL) {
+		knot_rrset_init_empty(rrset);
 		return;
 	}
 	for (uint i = 0; i < node->rrset_count; ++i) {