コード例 #1
0
ファイル: producer.go プロジェクト: jimjh/octopi
// Send sends the message to the broker, and blocks until an acknowledgement is
// received. If the max number of retries is exceeded, returns the last error.
func (p *Producer) Send(topic string, payload []byte) error {

	seqnum := atomic.AddInt64(&p.seqnum, 1)
	message := protocol.Message{seqnum, payload, crc32.ChecksumIEEE(payload)}
	request := &protocol.ProduceRequest{p.id, topic, message}

	log.Debug("Sending %v", request)

	p.lock.Lock()
	defer p.lock.Unlock()

	for {

		if _, err := p.socket.Send(request, MAX_RETRIES, origin()); nil != err {
			p.socket.Reset(p.register)
			continue
		}

		log.Debug("Acknowledgement received.")
		return nil

	}

	return nil

}
コード例 #2
0
ファイル: sync.go プロジェクト: jimjh/octopi
// SyncFollower streams updates to a follower through the given connection.
// Once the follower has fully caught up, add it to the follower set.
func (b *Broker) SyncFollower(conn *websocket.Conn, tails Offsets, hostport protocol.HostPort) error {

	follower := &Follower{
		conn:     conn,
		tails:    tails,
		hostport: hostport,
		quit:     make(chan interface{}, 1),
	}

	if err := b.ackFollower(follower); nil != err {
		return err
	}

	log.Debug("Begin synchronizing follower %v.", follower.hostport)
	for !follower.caughtUp(b) {
		if err := follower.catchUp(b); nil != err {
			return err
		}
	}

	log.Info("Follower %v has fully caught up.", follower.hostport)

	<-follower.quit
	return nil

}
コード例 #3
0
ファイル: sync.go プロジェクト: jimjh/octopi
// catchUpLog synchronizes a single log with the follower. Returns an error if
// it votes to abort the synchronization.
func (f *Follower) catchUpLog(broker *Broker, topic string) error {

	file, err := OpenLog(broker.config, topic, f.tails[topic])
	if nil != err {
		log.Warn("Could not open log file for topic: %s.", topic)
		return err
	}

	defer file.Close()
	var total uint32 = uint32(f.tails[topic])
	log.Debug("Started with %d.", total)

	for {

		// read next entry
		entry, err := file.ReadNext()
		if nil != err {
			return nil // EOF
		}

		// send to follower
		sync := &protocol.Sync{topic, entry.Message, entry.RequestId}
		if err = websocket.JSON.Send(f.conn, sync); nil != err {
			return err
		}

		total += entry.length() + 4
		log.Debug("Send %v.", entry.RequestId)
		log.Debug("Sent %d to %s.", total, f.conn.RemoteAddr())

		// wait for ack
		var ack protocol.SyncACK
		if err = websocket.JSON.Receive(f.conn, &ack); nil != err {
			return err
		}

		f.tails[topic] = ack.Offset
		log.Debug("%s is at %d.", f.conn.RemoteAddr(), ack.Offset)

	}

	return nil

}
コード例 #4
0
ファイル: sync.go プロジェクト: jimjh/octopi
// catchUp tries to bring _this_ broker up to date with its leader.
func (b *Broker) catchUp() error {

	write := func(request *protocol.Sync) (int64, error) {

		b.lock.Lock()
		defer b.lock.Unlock()

		file, err := b.getOrOpenLog(request.Topic)
		if nil != err {
			return 0, err
		}

		entry := &LogEntry{request.Message, request.RequestId}
		if err = file.WriteNext(entry); nil != err {
			return 0, err
		}

		stat, err := file.Stat()
		log.Debug("File size on %s is %d.", b.Origin(), stat.Size())
		if nil != err {
			return 0, err
		}

		return stat.Size(), nil

	}

	for {

		var request protocol.Sync
		if err := b.leader.Receive(&request); nil != err {
			log.Warn("Unable to receive from leader.")
			return err
		}

		offset, err := write(&request)
		if nil != err {
			log.Warn("Unable to open log file for %s.", request.Topic)
			continue
		}

		ack := &protocol.SyncACK{request.Topic, offset}
		if err := b.leader.Acknowledge(ack); nil != err {
			log.Warn("Unable to ack leader: %s", err.Error())
		}

		b.cond.Broadcast()

	}

	return nil

}
コード例 #5
0
ファイル: subscription.go プロジェクト: jimjh/octopi
// next reads the next message from the associated log file. When it reaches
// the end of the file, it waits (using a conditional variable) for more
// messages from the broker. As a consequence of this design, a waiting
// subscription cannot be closed until a new message is published, waking it
// up. Returns nil or associated error.
func (s *Subscription) next() error {

	entry, err := s.log.ReadNext()

	switch err {
	case nil:
	case io.EOF: // wait for more
		log.Debug("Reached end of log.")
		s.broker.wait(s)
		return nil
	default: // abort
		return err
	}

	return websocket.JSON.Send(s.conn, &entry.Message)

}
コード例 #6
0
ファイル: subscription.go プロジェクト: jimjh/octopi
// Serve blocks until either the websocket connection is closed, or until a
// message is received on the `quit` channel. This method may be invoked at
// most once; after it returns, the subscription is closed.
func (s *Subscription) Serve() error {

	defer s.log.Close()
	defer log.Debug("Stopped serving subscription %p.", s)

	for {
		select {
		case <-s.quit:
			return nil
		default:
			if err := s.next(); nil != err {
				log.Error("Unable to serve subscription: %s", err.Error())
				return err
			}
		}
	}

	return nil

}
コード例 #7
0
ファイル: sync.go プロジェクト: jimjh/octopi
// caughtUp checks if the follower has really caught up, and adds it to the
// broker's follower set.
func (f *Follower) caughtUp(broker *Broker) bool {

	broker.lock.Lock()
	defer broker.lock.Unlock()

	//log.Info("Obtained lock for %v", f.hostport)

	expected := broker.tails()
	for topic, offset := range expected {
		if offset != f.tails[topic] {
			log.Debug("Not fully caught up yet for %s. %d -> %d", topic, f.tails[topic], offset)
			return false
		}
	}

	// check if follower already in set. if so, delete prev entry.
	for follower, _ := range broker.followers {
		if f.hostport == follower.hostport {
			delete(broker.followers, follower)
		}
	}

	// add to set of followers
	broker.followers[f] = true

	// create struct to communicate with register
	var addFollow protocol.InsyncChange
	addFollow.Type = protocol.ADD
	addFollow.HostPort = f.hostport

	// add in-sync follower
	// check if disconnect from register. if so, exit.
	if err := websocket.JSON.Send(broker.regConn, addFollow); nil != err {
		log.Warn("Unable to update register: %s.", err.Error())
	}

	return true

}
コード例 #8
0
ファイル: brokerimpl.go プロジェクト: jimjh/octopi
// BecomeLeader returns only after successfully declaring leadership with the
// register. It locks down the broker, declares leadership with the register,
// and checkpoints the tails of all open logs.
func (b *Broker) BecomeLeader() error {

	endpoint := "ws://" + b.config.Register() + "/" + protocol.LEADER
	origin := b.Origin()

	log.Debug("Resetting...")
	b.leader.Reset(origin)

	b.lock.Lock()
	defer b.lock.Unlock()

	for {

		var err error
		b.regConn, err = websocket.Dial(endpoint, "", origin)

		if nil != err {
			log.Warn("Error dialing %s: %s", endpoint, err.Error())
			backoff()
			continue
		}

		err = websocket.JSON.Send(b.regConn, origin)
		if nil != err {
			backoff()
			continue
		}

		break

	}

	b.checkpoints = b.tails()
	b.role = LEADER
	return nil

}
コード例 #9
0
ファイル: brokerimpl.go プロジェクト: jimjh/octopi
func backoff() {
	duration := time.Duration(rand.Intn(protocol.MAX_RETRY_INTERVAL))
	log.Debug("Backing off %d milliseconds.", duration)
	time.Sleep(duration * time.Millisecond)
}