diff --git a/src/libknot/nameserver/name-server.c b/src/libknot/nameserver/name-server.c
index 66377cff445c7131c21d39d373416605fdb28fdf..3ca6f2850f7866f133e852e152c437103f9ec340 100644
--- a/src/libknot/nameserver/name-server.c
+++ b/src/libknot/nameserver/name-server.c
@@ -4374,7 +4374,8 @@ int knot_ns_process_update2(const knot_packet_t *query,
 		dbg_ns("Failed to apply UPDATE to the zone copy or no update"
 		       " made: %s\n", (ret < 0) ? knot_strerror(ret)
 		                                : "No change made.");
-		xfrin_rollback_update(old_contents, &contents_copy, &changes);
+		xfrin_rollback_update(old_contents, &contents_copy, changes);
+		knot_changes_free(&changes);
 		return ret;
 	}
 
@@ -4383,7 +4384,8 @@ int knot_ns_process_update2(const knot_packet_t *query,
 	if (ret != KNOT_EOK) {
 		dbg_ns("Failed to finalize updated zone: %s\n",
 		       knot_strerror(ret));
-		xfrin_rollback_update(old_contents, &contents_copy, &changes);
+		xfrin_rollback_update(old_contents, &contents_copy, changes);
+		knot_changes_free(&changes);
 		*rcode = (ret == KNOT_EMALF) ? KNOT_RCODE_FORMERR
 		                             : KNOT_RCODE_SERVFAIL;
 		return ret;
diff --git a/src/libknot/updates/changesets.h b/src/libknot/updates/changesets.h
index 144ae55a3cc88eb6886f2b9ea9d1d94665b233f9..da4f87cd2dde18b0ff9b913e0c0ebdbd92b69e0b 100644
--- a/src/libknot/updates/changesets.h
+++ b/src/libknot/updates/changesets.h
@@ -112,6 +112,11 @@ typedef enum {
 	KNOT_CHANGESET_REMOVE
 } knot_changeset_part_t;
 
+typedef enum {
+	KNOT_CHANGES_OLD,
+	KNOT_CHANGES_NEW
+} knot_changes_part_t;
+
 /*----------------------------------------------------------------------------*/
 
 int knot_changesets_init(knot_changesets_t **changesets,
@@ -145,20 +150,11 @@ int knot_changeset_is_empty(const knot_changeset_t *changeset);
 
 void knot_free_changesets(knot_changesets_t **changesets);
 
-int knot_changes_rrsets_reserve(knot_rrset_t ***rrsets,
-                                int *count, int *allocated, int to_add);
-
-int knot_changes_nodes_reserve(knot_node_t ***nodes,
-                               int *count, int *allocated);
-
-int knot_changes_rdata_reserve(knot_rrset_t ***rdatas,
-                               int count, int *allocated, int to_add);
-
-void knot_changes_add_rdata(knot_rrset_t **rdatas, int *count,
-                            knot_rrset_t *rrset);
+int knot_changes_add_rr(knot_changes_t *ch, knot_rrset_t *rrset,
+                        knot_changes_part_t part);
 
-int knot_changes_add_rrsets(const knot_rrset_t **from, size_t count,
-                            knot_rrset_t **to, int proc_sigs);
+int knot_changes_add_rrset(knot_changes_t *ch, knot_rrset_t *rrset,
+                           knot_changes_part_t part);
 
 #endif /* _KNOT_CHANGESETS_H_ */
 
diff --git a/src/libknot/updates/xfr-in.c b/src/libknot/updates/xfr-in.c
index 86dc9d521d1ec85bf038bc0109e804ed9fcab972..1d58bf34fba98dde3aafc43bd07a813bd3fc22cb 100644
--- a/src/libknot/updates/xfr-in.c
+++ b/src/libknot/updates/xfr-in.c
@@ -1274,45 +1274,30 @@ int xfrin_copy_old_rrset(knot_rrset_t *old, knot_rrset_t **copy,
 		return KNOT_ENOMEM;
 	}
 
-	// add the RRSet to the list of new RRSets
-	// create place also for RRSIGs
+	// add the RRSet to the list of new RRSets, RRSIG as well
 	if (save_new) {
-		changes->new_rrsets[changes->new_rrsets_count++] = *copy;
-
-		dbg_xfrin_detail("Adding RDATA from the RRSet copy to new RDATA list."
-				 "\n");
-		knot_changes_add_rdata(changes->new_rdata,
-					&changes->new_rdata_count,
-					*copy);
-
+		knot_changes_add_rrset(changes, *copy, KNOT_CHANGES_NEW);
 		if ((*copy)->rrsigs != NULL) {
 			assert(old->rrsigs != NULL);
-			changes->new_rrsets[changes->new_rrsets_count++] =
-					(*copy)->rrsigs;
-
-			dbg_xfrin_detail("Adding RDATA from RRSIG of the RRSet copy to "
-					 "new RDATA list.\n");
-			knot_changes_add_rdata(changes->new_rdata,
-						&changes->new_rdata_count,
-						(*copy)->rrsigs);
+			ret = knot_changes_add_rrset(changes, (*copy)->rrsigs,
+			      KNOT_CHANGES_NEW);
+			if (ret != KNOT_EOK) {
+				return ret;
+			}
 		}
 	}
 
-	changes->old_rrsets[changes->old_rrsets_count++] = old;
-
-	dbg_xfrin_detail("Adding RDATA from old RRSet to old RDATA list.\n");
-	knot_changes_add_rdata(changes->old_rdata, &changes->old_rdata_count,
-			       old);
-
-	if ((*copy)->rrsigs != NULL) {
-		assert(old->rrsigs != NULL);
-		changes->old_rrsets[changes->old_rrsets_count++] = old->rrsigs;
+	ret = knot_changes_add_rrset(changes, old, KNOT_CHANGES_OLD);
+	if (ret != KNOT_EOK) {
+		return ret;
+	}
 
-		dbg_xfrin_detail("Adding RDATA from RRSIG of the old RRSet to "
-				 "old RDATA list.\n");
-		knot_changes_add_rdata(changes->old_rdata,
-					&changes->old_rdata_count,
-					old->rrsigs);
+	if (old->rrsigs != NULL) {
+		ret = knot_changes_add_rrset(changes, old->rrsigs,
+		                             KNOT_CHANGES_OLD);
+		if (ret != KNOT_EOK) {
+			return ret;
+		}
 	}
 
 	return KNOT_EOK;
@@ -1456,8 +1441,10 @@ static int xfrin_apply_remove_rrsigs(knot_changes_t *changes,
 	assert(rr_removed);
 
 	// connect the RDATA to the list of old RDATA
-	knot_changes_add_rdata(changes->old_rdata, &changes->old_rdata_count,
-			       rr_removed);
+	ret = knot_changes_add_rrset(changes, rr_removed, KNOT_CHANGES_OLD);
+	if (ret != KNOT_EOK) {
+		return ret;
+	}
 
 	// if the RRSet is empty, remove from node and add to old RRSets
 	// check if there is no RRSIGs; if there are, leave the RRSet
@@ -1467,21 +1454,23 @@ static int xfrin_apply_remove_rrsigs(knot_changes_t *changes,
 		knot_rrset_set_rrsigs(*rrset, NULL);
 
 		// add RRSet to the list of old RRSets
-		changes->old_rrsets[changes->old_rrsets_count++] = rrsigs;
-
-		// saving old RDATA is not necessary as there is none
+		ret = knot_changes_add_rrset(changes, rrsigs, KNOT_CHANGES_OLD);
+		if (ret != KNOT_EOK) {
+			return ret;
+		}
 
 		// now check if the RRSet is not totally empty
 		if (knot_rrset_rdata_rr_count(*rrset) == 0) {
 			assert(knot_rrset_rrsigs(*rrset) == NULL);
-
 			// remove the whole RRSet from the node
 			knot_rrset_t *tmp = knot_node_remove_rrset(node,
 						     knot_rrset_type(*rrset));
 			assert(tmp == *rrset);
-
-			changes->old_rrsets[changes->old_rrsets_count++] =
-				*rrset;
+			int ret = knot_changes_add_rrset(changes, *rrset,
+			                                 KNOT_CHANGES_OLD);
+			if (ret != KNOT_EOK) {
+				return ret;
+			}
 		}
 	}
 
@@ -1576,8 +1565,10 @@ dbg_xfrin_exec_detail(
 	}
 
 	if (rr_remove->rdata_count != 0) {
-		knot_changes_add_rdata(changes->old_rdata,
-					&changes->old_rdata_count, rr_remove);
+		ret = knot_changes_add_rrset(changes, rr_remove, KNOT_CHANGES_OLD);
+		if (ret != KNOT_EOK) {
+			return ret;
+		}
 	} else {
 		/* Discard empty RRSet. */
 		knot_rrset_free(&rr_remove);
@@ -1600,7 +1591,10 @@ dbg_xfrin_exec_detail(
 		// add the removed RRSet to list of old RRSets
 
 		assert(tmp == *rrset);
-		changes->old_rrsets[changes->old_rrsets_count++] = *rrset;
+		ret = knot_changes_add_rrset(changes, *rrset, KNOT_CHANGES_OLD);
+		if (ret != KNOT_EOK) {
+			return ret;
+		}
 	}
 
 	return KNOT_EOK;
@@ -1612,7 +1606,6 @@ static int xfrin_apply_remove_all_rrsets(knot_changes_t *changes,
                                          knot_node_t *node, uint16_t type,
                                          uint32_t chflags)
 {
-	int ret = KNOT_EOK;
 	knot_rrset_t **rrsets = NULL;
 	unsigned rrsets_count = 0;
 	int is_apex = knot_node_rrset(node, KNOT_RRTYPE_SOA) != NULL;
@@ -1694,12 +1687,11 @@ dbg_xfrin_exec_verb(
 			continue;
 		}
 
-		changes->old_rrsets[changes->old_rrsets_count++] = rrsets[i];
-
-		/* Remove old RDATA. */
-		knot_changes_add_rdata(changes->old_rdata,
-					&changes->old_rdata_count,
-					rrsets[i]);
+		int ret = knot_changes_add_rrset(changes, rrsets[i],
+		                                 KNOT_CHANGES_OLD);
+		if (ret != KNOT_EOK) {
+			return ret;
+		}
 	}
 
 	free(rrsets);
@@ -1760,13 +1752,10 @@ int xfrin_replace_rrset_in_node(knot_node_t *node,
 	// save also the RDATA, because RDATA are not deleted with the RRSet
 	// save the new RRSet to the new RRSet, so that it is deleted if the
 	// apply fails
-	changes->old_rrsets[changes->old_rrsets_count++] = rrset_old;
-
-	dbg_xfrin_verb("Adding RDATA from old RRSet to the list of old RDATA."
-		       "\n");
-	knot_changes_add_rdata(changes->old_rdata, &changes->old_rdata_count,
-			       rrset_old);
-
+	int ret = knot_changes_add_rrset(changes, rrset_old, KNOT_CHANGES_OLD);
+	if (ret != KNOT_EOK) {
+		return ret;
+	}
 	// store RRSIGs from the old RRSet to the new
 	knot_rrset_set_rrsigs(rrset_new, knot_rrset_get_rrsigs(rrset_old));
 
@@ -1781,12 +1770,10 @@ int xfrin_replace_rrset_in_node(knot_node_t *node,
 	}
 	assert(ret == 0);
 
-	changes->new_rrsets[changes->new_rrsets_count++] = rrset_new;
-
-	dbg_xfrin_verb("Adding RDATA from new RRSet to the list of new RDATA."
-		       "\n");
-	knot_changes_add_rdata(changes->new_rdata, &changes->new_rdata_count,
-			       rrset_new);
+	ret = knot_changes_add_rrset(changes, rrset_new, KNOT_CHANGES_NEW);
+	if (ret != KNOT_EOK) {
+		return ret;
+	}
 
 	return KNOT_EOK;
 }
@@ -2096,7 +2083,10 @@ dbg_xfrin_exec_verb(
 			return KNOT_ERROR;
 		}
 
-		changes->new_rrsets[changes->new_rrsets_count++] = *rrset;
+		ret = knot_changes_add_rrset(changes, *rrset, KNOT_CHANGES_NEW);
+		if (ret != KNOT_EOK) {
+			return ret;
+		}
 	}
 
 dbg_xfrin_exec_detail(
@@ -2321,37 +2311,17 @@ static void xfrin_cleanup_failed_update(knot_zone_contents_t *old_contents,
 
 void xfrin_rollback_update(knot_zone_contents_t *old_contents,
                            knot_zone_contents_t **new_contents,
-                           knot_changes_t **changes)
+                           knot_changes_t *changes)
 {
-	assert(changes != NULL);
+	if (changes == NULL) {
+		return;
+	}
 
 	dbg_xfrin("Rolling back changeset application.\n");
-
-	if (*changes != NULL) {
-		// discard new RRSets
-		for (int i = 0; i < (*changes)->new_rrsets_count; ++i) {
-			//knot_rrset_deep_free(&changes->new_rrsets[i], 0, 1, 1);
-			if ((*changes)->new_rrsets[i]->rdata_count == 0) {
-				knot_rrset_free(&(*changes)->new_rrsets[i]);
-			}
-		}
-
-		for (int i = 0; i < (*changes)->new_rdata_count; ++i) {
-			dbg_xfrin_detail("Freeing %d. RDATA: %p\n", i,
-					 (*changes)->new_rdata[i]);
-			knot_rrset_deep_free_no_sig(&(*changes)->new_rdata[i], 1, 1);
-		}
-
-		// free allocated arrays of nodes and rrsets
-		free((*changes)->new_rrsets);
-		free((*changes)->new_rdata);
-		free((*changes)->old_nodes);
-		free((*changes)->old_nsec3);
-		free((*changes)->old_rrsets);
-		free((*changes)->old_rdata);
-
-		free(*changes);
-		*changes = NULL;
+	knot_rr_node_t *rr_node = NULL;
+	WALK_LIST(rr_node, changes->new_rrsets) {
+		knot_rrset_t *rrset = rr_node->rr;
+		knot_rrset_deep_free_no_sig(&rrset, 1, 1);
 	}
 
 	xfrin_cleanup_failed_update(old_contents, new_contents);
@@ -2562,13 +2532,6 @@ dbg_xfrin_exec_detail(
 				int count = 1;//knot_rrset_rdata_rr_count(chset->add[i]);
 
 				// connect the RDATA to the list of new RDATA
-				int res = knot_changes_rdata_reserve(
-					&changes->new_rdata,
-					changes->new_rdata_count,
-					&changes->new_rdata_allocated, count);
-				if (res != KNOT_EOK) {
-					return res;
-				}
 
 				knot_changes_add_rdata(changes->new_rdata,
 					    &changes->new_rdata_count,
diff --git a/src/libknot/updates/xfr-in.h b/src/libknot/updates/xfr-in.h
index 54a22c554d6b20d2bbb473354739e68eb06c999c..7763a2d72177cc504c8f80acc4f2d7f791d06a6d 100644
--- a/src/libknot/updates/xfr-in.h
+++ b/src/libknot/updates/xfr-in.h
@@ -200,7 +200,7 @@ void xfrin_cleanup_successful_update(knot_changes_t **changes);
 
 void xfrin_rollback_update(knot_zone_contents_t *old_contents,
                            knot_zone_contents_t **new_contents,
-                           knot_changes_t **changes);
+                           knot_changes_t *changes);
 
 int xfrin_copy_rrset(knot_node_t *node, uint16_t type,
                      knot_rrset_t **rrset, knot_changes_t *changes,