diff --git a/fe/lbv2/lbv2.go b/fe/lbv2/lbv2.go
index 9991e24d2c8ec13f77f2b0a786b5a416679d7106..dac92092d3d7097f624fc64cdf8464ecc1c2bc62 100644
--- a/fe/lbv2/lbv2.go
+++ b/fe/lbv2/lbv2.go
@@ -6,12 +6,11 @@ import (
 	"math/rand"
 	"net"
 	"sync"
+	"time"
 
 	"github.com/jmcvetta/randutil"
 )
 
-const baseWeight = 1000000
-
 // Node utilization along a specific dimension. Utilization is treated
 // as a vector, the LoadBalancer is opaque to the actual meaning of
 // the dimensions used (though it makes sense for Requests to be the
@@ -47,7 +46,7 @@ func (c NodeScore) SetScore(w float64) NodeScore {
 }
 
 // Damping factor for the utilization query cost model.
-const costAlpha = 0.9
+const costAlpha = 0.8
 
 type costEstimate float64
 
@@ -341,6 +340,8 @@ func NewActiveNodesFilter() NodeFilter {
 	return &activeNodesFilter{}
 }
 
+const baseWeight = 1000000
+
 // Return a random item based on a weighted distribution.
 func weightedPolicyFunc(wnodes []NodeScore) Node {
 	// Need to convert this anyway.
@@ -389,3 +390,9 @@ func highestScorePolicyFunc(wnodes []NodeScore) Node {
 }
 
 var HighestScorePolicy = PolicyFunc(highestScorePolicyFunc)
+
+func init() {
+	// Seed the math/rand PRNG. The current time works just fine,
+	// it doesn't need to be cryptographically robust.
+	rand.Seed(time.Now().Unix())
+}
diff --git a/fe/loadbalancing_test.go b/fe/loadbalancing_test.go
index e2d8d89e0b28ef63f35dea3ff11491285a6d0a84..4ca4ec0923e42319057a615088c9010efa6a84aa 100644
--- a/fe/loadbalancing_test.go
+++ b/fe/loadbalancing_test.go
@@ -9,6 +9,7 @@ import (
 	"git.autistici.org/ale/autoradio/fe/lbv2"
 )
 
+// Not a very good way to compare probability distributions.
 func compareDistribution(a, b map[string]int, n int) bool {
 	for k, aval := range a {
 		bval := b[k]
@@ -26,6 +27,7 @@ func runLBTest(t *testing.T, nodes []*autoradio.NodeStatus, ctx lbv2.RequestCont
 		t.Fatalf("Error parsing spec \"%s\": %v", lbspec, err)
 	}
 
+	// We're not testing the time component, disable predictors.
 	lb.DisableUtilizationPredictors()
 
 	// Run lb.Choose() n times and accumulate the results.
@@ -131,3 +133,70 @@ func TestLoadBalancer_PoliciesIgnoreDisabledNodes(t *testing.T) {
 		runLBTest(t, nodes, nil, spec, 1000, map[string]int{"node1": 1000})
 	}
 }
+
+func TestLoadBalancer_UtilizationPredictor(t *testing.T) {
+	cap := 100
+	node := &autoradio.NodeStatus{
+		Name:         "node1",
+		IcecastUp:    true,
+		MaxListeners: cap,
+		Mounts: []autoradio.IcecastMountStatus{
+			{Listeners: 0},
+		},
+	}
+	nodes := []*autoradio.NodeStatus{node}
+
+	lb, err := parseLoadBalancerSpec("listeners_available,listeners_score,best")
+	if err != nil {
+		t.Fatal(err)
+	}
+	lb.Update(nodes)
+
+	// See how many clients can connect successfully until the
+	// predicted utilization goes above 1.
+	okRequests := func() int {
+		for i := 0; i < 110; i++ {
+			result := lb.Choose(nil)
+			if result == nil {
+				return i
+			}
+		}
+		return 111
+	}
+
+	// At first, the query cost function is 1 - a new query will
+	// cause utilization to stay >1 until Update() is called.
+	nr := okRequests()
+	if nr > 1 {
+		t.Fatalf("initial run: ok_requests = %d, want 1", nr)
+	}
+
+	reachedCapacityAt := -1
+	for i := 0; i < 1000; i++ {
+		// Update the LoadBalancer with the current connection count.
+		node.Mounts[0].Listeners = nr
+		lb.Update(nodes)
+
+		newNr := okRequests()
+		if newNr == 0 && nr < cap {
+			t.Errorf("iteration %d: no successful requests but utilization is below 100% (avail=%d, ok_requests=%d)", i, cap-nr, newNr)
+		}
+		if newNr > (cap - nr) {
+			t.Fatalf("iteration %d: over capacity (avail=%d, ok_requests=%d)", i, cap-nr, newNr)
+		}
+		nr += newNr
+		if nr >= cap && reachedCapacityAt < 0 {
+			reachedCapacityAt = i
+		}
+	}
+	// Verify how quickly the query cost converged to the real
+	// value. This depends on the value of the costAlpha constant,
+	// and it will vary from 1 (if the damping factor is zero the
+	// real value immediately kicks in) to 100 (if the query cost
+	// stays at 1, as it is initially).
+	// The default value for costAlpha of 0.8 should cause
+	// convergence at i==20.
+	if reachedCapacityAt > 20 {
+		t.Fatalf("reached capacity at iteration %d, the query cost function did not converge properly", reachedCapacityAt)
+	}
+}