Index: /trunk/downstream.go
===================================================================
--- /trunk/downstream.go	(revision 408)
+++ /trunk/downstream.go	(revision 409)
@@ -851,4 +851,18 @@
 			delete(net.offlineClients, dc.clientName)
 		}
+
+		// Fast-forward history to last message
+		for target, history := range net.history {
+			if ch, ok := net.channels[target]; ok && ch.Detached {
+				continue
+			}
+
+			lastID, err := lastMsgID(net, target, time.Now())
+			if err != nil {
+				dc.logger.Printf("failed to get last message ID: %v", err)
+				continue
+			}
+			history.clients[dc.clientName] = lastID
+		}
 	})
 
@@ -857,5 +871,5 @@
 
 func (dc *downstreamConn) sendNetworkHistory(net *network) {
-	if dc.caps["draft/chathistory"] {
+	if dc.caps["draft/chathistory"] || dc.srv.LogPath == "" {
 		return
 	}
@@ -865,11 +879,15 @@
 		}
 
-		seq, ok := history.clients[dc.clientName]
+		lastDelivered, ok := history.clients[dc.clientName]
 		if !ok {
 			continue
 		}
-		history.clients[dc.clientName] = history.ring.Cur()
-
-		consumer := history.ring.NewConsumer(seq)
+
+		limit := 4000
+		history, err := loadHistoryLatestID(dc.network, target, lastDelivered, limit)
+		if err != nil {
+			dc.logger.Printf("failed to send implicit history for %q: %v", target, err)
+			continue
+		}
 
 		batchRef := "history"
@@ -882,10 +900,5 @@
 		}
 
-		for {
-			msg := consumer.Consume()
-			if msg == nil {
-				break
-			}
-
+		for _, msg := range history {
 			// Don't replay all messages, because that would mess up client
 			// state. For instance we just sent the list of users, sending
@@ -901,8 +914,6 @@
 
 			if dc.caps["batch"] {
-				msg = msg.Copy()
 				msg.Tags["batch"] = irc.TagValue(batchRef)
 			}
-
 			dc.SendMessage(dc.marshalMessage(msg, net))
 		}
Index: unk/ring.go
===================================================================
--- /trunk/ring.go	(revision 408)
+++ 	(revision )
@@ -1,86 +1,0 @@
-package soju
-
-import (
-	"fmt"
-
-	"gopkg.in/irc.v3"
-)
-
-// Ring implements a single producer, multiple consumer ring buffer. The ring
-// buffer size is fixed. The ring buffer is stored in memory.
-type Ring struct {
-	buffer []*irc.Message
-	cap    uint64
-	cur    uint64
-}
-
-// NewRing creates a new ring buffer.
-func NewRing(capacity int) *Ring {
-	return &Ring{
-		buffer: make([]*irc.Message, capacity),
-		cap:    uint64(capacity),
-	}
-}
-
-// Produce appends a new message to the ring buffer.
-func (r *Ring) Produce(msg *irc.Message) {
-	i := int(r.cur % r.cap)
-	r.buffer[i] = msg
-	r.cur++
-}
-
-// Cur returns the current history sequence number.
-func (r *Ring) Cur() uint64 {
-	return r.cur
-}
-
-// NewConsumer creates a new ring buffer consumer.
-//
-// The consumer will get messages starting from the specified history sequence
-// number (see Ring.Cur).
-func (r *Ring) NewConsumer(seq uint64) *RingConsumer {
-	return &RingConsumer{ring: r, cur: seq}
-}
-
-// RingConsumer is a ring buffer consumer.
-type RingConsumer struct {
-	ring *Ring
-	cur  uint64
-}
-
-// diff returns the number of pending messages. It assumes the Ring is locked.
-func (rc *RingConsumer) diff() uint64 {
-	if rc.cur > rc.ring.cur {
-		panic(fmt.Sprintf("soju: consumer cursor (%v) greater than producer cursor (%v)", rc.cur, rc.ring.cur))
-	}
-	return rc.ring.cur - rc.cur
-}
-
-// Peek returns the next pending message if any without consuming it. A nil
-// message is returned if no message is available.
-func (rc *RingConsumer) Peek() *irc.Message {
-	diff := rc.diff()
-	if diff == 0 {
-		return nil
-	}
-	if diff > rc.ring.cap {
-		// Consumer drops diff - cap entries
-		rc.cur = rc.ring.cur - rc.ring.cap
-	}
-	i := int(rc.cur % rc.ring.cap)
-	msg := rc.ring.buffer[i]
-	if msg == nil {
-		panic(fmt.Sprintf("soju: unexpected nil ring buffer entry at index %v", i))
-	}
-	return msg
-}
-
-// Consume consumes and returns the next pending message. A nil message is
-// returned if no message is available.
-func (rc *RingConsumer) Consume() *irc.Message {
-	msg := rc.Peek()
-	if msg != nil {
-		rc.cur++
-	}
-	return msg
-}
Index: /trunk/server.go
===================================================================
--- /trunk/server.go	(revision 408)
+++ /trunk/server.go	(revision 409)
@@ -48,5 +48,4 @@
 	Hostname       string
 	Logger         Logger
-	RingCap        int
 	HistoryLimit   int
 	LogPath        string
@@ -65,5 +64,4 @@
 	return &Server{
 		Logger:       log.New(log.Writer(), "", log.LstdFlags),
-		RingCap:      4096,
 		HistoryLimit: 1000,
 		users:        make(map[string]*user),
Index: /trunk/upstream.go
===================================================================
--- /trunk/upstream.go	(revision 408)
+++ /trunk/upstream.go	(revision 409)
@@ -709,5 +709,4 @@
 				ch.Members[newNick] = memberships
 				uc.appendLog(ch.Name, msg)
-				uc.appendHistory(ch.Name, msg)
 			}
 		}
@@ -819,5 +818,4 @@
 
 				uc.appendLog(ch.Name, msg)
-				uc.appendHistory(ch.Name, msg)
 			}
 		}
@@ -1612,5 +1610,4 @@
 }
 
-// TODO: handle moving logs when a network name changes, when support for this is added
 func (uc *upstreamConn) appendLog(entity string, msg *irc.Message) {
 	if uc.srv.LogPath == "" {
@@ -1624,11 +1621,4 @@
 	}
 
-	if _, err := ml.Append(msg); err != nil {
-		uc.logger.Printf("failed to log message: %v", err)
-	}
-}
-
-// appendHistory appends a message to the history. entity can be empty.
-func (uc *upstreamConn) appendHistory(entity string, msg *irc.Message) {
 	detached := false
 	if ch, ok := uc.network.channels[entity]; ok {
@@ -1638,12 +1628,17 @@
 	history, ok := uc.network.history[entity]
 	if !ok {
+		lastID, err := lastMsgID(uc.network, entity, time.Now())
+		if err != nil {
+			uc.logger.Printf("failed to log message: failed to get last message ID: %v", err)
+			return
+		}
+
 		history = &networkHistory{
-			clients: make(map[string]uint64),
-			ring:    NewRing(uc.srv.RingCap),
+			clients: make(map[string]string),
 		}
 		uc.network.history[entity] = history
 
 		for clientName, _ := range uc.network.offlineClients {
-			history.clients[clientName] = 0
+			history.clients[clientName] = lastID
 		}
 
@@ -1652,20 +1647,24 @@
 			// clients too
 			uc.forEachDownstream(func(dc *downstreamConn) {
-				history.clients[dc.clientName] = 0
-			})
-		}
-	}
-
-	history.ring.Produce(msg)
+				history.clients[dc.clientName] = lastID
+			})
+		}
+	}
+
+	msgID, err := ml.Append(msg)
+	if err != nil {
+		uc.logger.Printf("failed to log message: %v", err)
+		return
+	}
 
 	if !detached {
 		uc.forEachDownstream(func(dc *downstreamConn) {
-			history.clients[dc.clientName] = history.ring.Cur()
-		})
-	}
-}
-
-// produce appends a message to the logs, adds it to the history and forwards
-// it to connected downstream connections.
+			history.clients[dc.clientName] = msgID
+		})
+	}
+}
+
+// produce appends a message to the logs and forwards it to connected downstream
+// connections.
 //
 // If origin is not nil and origin doesn't support echo-message, the message is
@@ -1675,6 +1674,4 @@
 		uc.appendLog(target, msg)
 	}
-
-	uc.appendHistory(target, msg)
 
 	// Don't forward messages if it's a detached channel
Index: /trunk/user.go
===================================================================
--- /trunk/user.go	(revision 408)
+++ /trunk/user.go	(revision 409)
@@ -52,6 +52,5 @@
 
 type networkHistory struct {
-	clients map[string]uint64 // indexed by client name
-	ring    *Ring             // can be nil if there are no offline clients
+	clients map[string]string // indexed by client name
 }
 
