diff --git a/knot-resolver.files b/knot-resolver.files
index d7b8b74a17b60c52b3e17a70080c5b20ffcc6bc1..d2d7480c47d5396db665afc8cf9fec33c620edaa 100644
--- a/knot-resolver.files
+++ b/knot-resolver.files
@@ -21,3 +21,4 @@ lib/rplan.h
 lib/rplan.c
 lib/layer/stats.h
 lib/layer/stats.c
+lib/defines.h
diff --git a/lib/context.c b/lib/context.c
index 7e371cddde2344847a29a9587c857d3cf1efb5ba..b07849e71c5f8534c9c827bef3315ca16776f5f5 100644
--- a/lib/context.c
+++ b/lib/context.c
@@ -54,7 +54,6 @@ int kr_result_init(struct kr_context *ctx, struct kr_result *result)
 	knot_wire_set_qr(ans->wire);
 
 	result->ans = ans;
-	gettimeofday(&result->t_start, NULL);
 
 	return 0;
 }
diff --git a/lib/context.h b/lib/context.h
index c07104814c2b012b9015615b7c164de1aa416b92..ee1c8ce82ab2f798bcea5fd17c6a2d3167447181 100644
--- a/lib/context.h
+++ b/lib/context.h
@@ -27,6 +27,7 @@ struct kr_result {
 	knot_pkt_t *ans;
 	unsigned flags;
 	struct timeval t_start, t_end;
+	unsigned total_rtt;
 	unsigned nr_queries;
 };
 
diff --git a/lib/defines.h b/lib/defines.h
new file mode 100644
index 0000000000000000000000000000000000000000..e109834d1deab30665385bcd1e66927b27b3a3fd
--- /dev/null
+++ b/lib/defines.h
@@ -0,0 +1,21 @@
+/* Copyright 2014 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz>
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+    http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+#pragma once
+
+/*
+ * Connection limits.
+ */
+#define KR_CONN_RTT_MAX 10000
diff --git a/lib/delegpt.c b/lib/delegpt.c
index 58165e9895bf2d409ea50d2bc5de83cf18fd4168..54d3ec6a54a1cff29eb26af6de5f03c98f9213cb 100644
--- a/lib/delegpt.c
+++ b/lib/delegpt.c
@@ -1,4 +1,5 @@
 #include "lib/delegpt.h"
+#include "lib/defines.h"
 #include <common/mempool.h>
 
 static void delegpt_free(struct kr_ns *dp, mm_ctx_t *mm)
@@ -104,6 +105,22 @@ struct kr_ns *kr_ns_find(list_t *list, const knot_dname_t *name)
 	return NULL;
 }
 
+void kr_ns_invalidate(struct kr_ns *ns)
+{
+	/* Slow start. */
+	ns->flags = DP_LAME;
+	ns->stat.M = KR_CONN_RTT_MAX;
+	ns->stat.S = 0;
+	ns->stat.n = 1;
+
+	/* Move to the end of the preference list. */
+	node_t *next = ns->node.next;
+	if (next->next) {
+		rem_node(&ns->node);
+		insert_node(&ns->node, next);
+	}
+}
+
 void kr_ns_remove(struct kr_ns *ns, mm_ctx_t *mm)
 {
 	rem_node((node_t *)ns);
diff --git a/lib/delegpt.h b/lib/delegpt.h
index 6a5b6ad237aea284042b4321c13400d5fd6ce5f1..a2841515f7acbc74ff534e368dcc0ee2a68abd77 100644
--- a/lib/delegpt.h
+++ b/lib/delegpt.h
@@ -35,7 +35,10 @@ struct kr_ns {
 	knot_dname_t *name;
 	struct sockaddr_storage addr;
 	unsigned valid_until;
-	unsigned mean_rtt;
+	struct {
+		double M, S; /* Mean, Variance S/n */
+		unsigned n;
+	} stat;
 	unsigned flags;
 };
 
@@ -56,4 +59,6 @@ list_t *kr_delegmap_find(struct kr_delegmap *map, const knot_dname_t *name);
 
 struct kr_ns *kr_ns_get(list_t *list, const knot_dname_t *name, mm_ctx_t *mm);
 struct kr_ns *kr_ns_find(list_t *list, const knot_dname_t *name);
+void kr_ns_invalidate(struct kr_ns *ns);
 void kr_ns_remove(struct kr_ns *ns, mm_ctx_t *mm);
+
diff --git a/lib/layer/stats.c b/lib/layer/stats.c
index f0f2fcd682a09675e4a76ceed2795182ec0fd2e0..4b66c0f21a66c964cecb4018b677933bfc72a93b 100644
--- a/lib/layer/stats.c
+++ b/lib/layer/stats.c
@@ -20,6 +20,44 @@ limitations under the License.
 
 #define DEBUG_MSG(fmt, ...) fprintf(stderr, "[stats] " fmt, ## __VA_ARGS__)
 
+static void update_ns_preference(struct kr_ns *ns, struct kr_ns *next)
+{
+	assert(ns);
+	assert(next);
+
+	/* Push down if next has better score. */
+	if (next->stat.M < ns->stat.M) {
+		rem_node(&ns->node);
+		insert_node(&ns->node, &next->node);
+	}
+}
+
+static void update_ns_preference_list(struct kr_ns *cur)
+{
+	assert(cur);
+	struct kr_ns *next = (struct kr_ns *)cur->node.next;
+
+	/* O(n), walk the list (shouldn't be too large). */
+	/* TODO: cut on first swap? random swaps? */
+	while (next->node.next != NULL) {
+		update_ns_preference(cur, next);
+		cur  = next;
+		next = (struct kr_ns *)cur->node.next;
+	}
+}
+
+static void update_stats(struct kr_ns *ns, double rtt)
+{
+	/* Knuth, TAOCP, p.232 (Welford running variance/mean). */
+	double d_mean = (rtt - ns->stat.M);
+	ns->stat.n += 1;
+	ns->stat.M += d_mean / ns->stat.n;
+	ns->stat.S += d_mean * (rtt - ns->stat.M);
+
+	/* Update NS position in preference list. */
+	update_ns_preference_list(ns);
+}
+
 static int begin(knot_layer_t *ctx, void *param)
 {
 	ctx->data = param;
@@ -38,7 +76,7 @@ static int finish(knot_layer_t *ctx)
 
 	DEBUG_MSG("rcode: %d (%u RRs)\n", knot_wire_get_rcode(result->ans->wire), result->ans->rrset_count);
 	DEBUG_MSG("queries: %u\n", result->nr_queries);
-	DEBUG_MSG("total time: %.02f msecs\n", time_diff(&result->t_start, &result->t_end));
+	DEBUG_MSG("total time: %u msecs\n", result->total_rtt);
 #endif
 
 	return ctx->state;
@@ -51,6 +89,9 @@ static int query(knot_layer_t *ctx, knot_pkt_t *pkt)
 
 	result->nr_queries += 1;
 
+	/* Store stats. */
+	gettimeofday(&result->t_start, NULL);
+
 	return ctx->state;
 }
 
@@ -60,19 +101,27 @@ static int answer(knot_layer_t *ctx, knot_pkt_t *pkt)
 	struct kr_layer_param *param = ctx->data;
 	struct kr_context* resolve = param->ctx;
 	struct kr_result *result = param->result;
+	struct kr_ns *ns = resolve->current_ns;
 
 	/* Store stats. */
 	gettimeofday(&result->t_end, NULL);
 
+	/* Update NS statistics. */
+	double rtt = time_diff(&result->t_start, &result->t_end);
+	if (rtt > 0.0) {
+		update_stats(ns, rtt);
+		result->total_rtt += rtt;
+	}
+
 #ifndef NDEBUG
-	char *ns_name = knot_dname_to_str(resolve->current_ns->name);
+	char *ns_name = knot_dname_to_str(ns->name);
 	char pad[16];
 	memset(pad, '-', sizeof(pad));
 	pad[MIN(sizeof(pad) - 1, list_size(&resolve->rplan.q) * 2)] = '\0';
-	DEBUG_MSG("#%s %s ... RC=%d, AA=%d, cumulative time: %.02f msecs\n",
+	DEBUG_MSG("#%s %s ... RC=%d, AA=%d, RTT: %.02f msecs\n",
 	          pad, ns_name, knot_wire_get_rcode(pkt->wire),
 	          knot_wire_get_aa(pkt->wire) != 0,
-	          time_diff(&result->t_start, &result->t_end));
+	          rtt);
 	free(ns_name);
 #endif
 
diff --git a/lib/resolve.c b/lib/resolve.c
index 409b16837f16a8baddf477c6e65becab4b73e44e..9fc4362e58cd415cef9551f84fea91dfd8bfa346 100644
--- a/lib/resolve.c
+++ b/lib/resolve.c
@@ -4,6 +4,7 @@
 #include <libknot/processing/requestor.h>
 #include <libknot/descriptor.h>
 #include "lib/resolve.h"
+#include "lib/defines.h"
 #include "lib/layer/iterate.h"
 #include "lib/layer/static.h"
 #include "lib/layer/stats.h"
@@ -29,7 +30,7 @@ static int resolve_ns(struct kr_context *resolve, struct kr_ns *ns)
 
 static void iterate(struct knot_requestor *requestor, struct kr_context* ctx)
 {
-	struct timeval timeout = { 5, 0 };
+	struct timeval timeout = { KR_CONN_RTT_MAX / 1000, 0 };
 	const struct kr_query *next = kr_rplan_next(&ctx->rplan);
 	assert(next);
 
@@ -66,7 +67,8 @@ static void iterate(struct knot_requestor *requestor, struct kr_context* ctx)
 	knot_requestor_enqueue(requestor, tx);
 	int ret = knot_requestor_exec(requestor, &timeout);
 	if (ret != 0) {
-		kr_ns_remove(ns, ctx->dp_map.pool);
+		/* Resolution failed, invalidate current resolver. */
+		kr_ns_invalidate(ns);
 	}
 
 	/* Pop resolved query. */