예제 #1
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

}
예제 #2
0
파일: sync.go 프로젝트: jimjh/octopi
// ackFollower sends an acknowledgement to the follower.
func (b *Broker) ackFollower(f *Follower) error {

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

	ack := new(protocol.Ack)
	if b.role != LEADER || f.conn.RemoteAddr().String() == b.Origin() {
		log.Warn("Denying follow requests from %s.", f.conn.RemoteAddr())
		ack.Status = protocol.StatusFailure
	} else {

		inner := new(protocol.FollowACK)
		inner.Truncate = make(Offsets)
		for topic, checkpoint := range b.checkpoints {
			log.Info("Checkpoint is %v", checkpoint)
			if f.tails[topic] > checkpoint {
				inner.Truncate[topic] = checkpoint
				f.tails[topic] = checkpoint
			}
		}

		ack.Status = protocol.StatusSuccess
		ack.Payload, _ = json.Marshal(inner)

	}

	return websocket.JSON.Send(f.conn, ack)

}
예제 #3
0
파일: publish.go 프로젝트: jimjh/octopi
// producer handles incoming produce requests. Producers may send multiple
// produce requests on the same persistent connection. The function exits when
// an `io.EOF` is received on the connection.
func producer(conn *websocket.Conn) {

	defer conn.Close()

	for {

		var request protocol.ProduceRequest

		err := websocket.JSON.Receive(conn, &request)
		if err == io.EOF { // graceful shutdown
			break
		}

		if nil != err {
			log.Warn("Ignoring invalid message from %v.", conn.RemoteAddr())
			continue
		}

		ack := new(protocol.Ack)
		if err := broker.Publish(request.Topic, request.ID, &request.Message); nil != err {
			log.Error(err.Error())
			ack.Status = protocol.StatusFailure
		} else {
			ack.Status = protocol.StatusSuccess
		}
		// TODO: should redirect if this node is not the leader

		websocket.JSON.Send(conn, &ack)

	}

	log.Info("Closed producer connection from %v.", conn.RemoteAddr())

}
예제 #4
0
파일: regimpl.go 프로젝트: jimjh/octopi
// LeaderDisconnect empties out the leader and notifies followers
// of a change in leader
func (r *Register) LeaderDisconnect() {
	r.lock.Lock()

	// create a copy to release lock earlier
	tmpSet := make(map[string]bool)

	for hp, _ := range r.insync {
		tmpSet[hp] = true
	}

	r.lock.Unlock()

	if len(tmpSet) == 0 {
		r.lock.Lock()
		log.Warn("Set of followers is 0! Notify all seen brokers!!")
		for hp, _ := range r.seenBrokers {
			tmpSet[hp] = true
		}
		r.lock.Unlock()
	}

	// notify all followers with the same set for consistency
	// and only remove from original set if fail to contact
	for hp, _ := range tmpSet {
		go r.notifyFollower(hp, tmpSet)
	}
}
예제 #5
0
파일: twitprod.go 프로젝트: jimjh/octopi
func main() {

	var broker = flag.String("register", "localhost:12345", "host and port number of broker")
	var user = flag.String("user", "octopx", "username")
	flag.Parse()

	tp, err := twitproducer.NewTwitProducer(*user, "octopioctopus", *broker, nil)

	if nil != err {
		log.Warn("Did not receive a correct twitproducer")
	}

	err = tp.RelayMessages()

	if nil != err {
		log.Warn("%v", err)
	}

}
예제 #6
0
파일: brokerimpl.go 프로젝트: jimjh/octopi
// tails returns the sizes of all log files, organized by their topics.
func (b *Broker) tails() Offsets {

	tails := make(Offsets, len(b.logs))

	for topic, file := range b.logs {
		stat, err := file.Stat()
		if nil != err {
			log.Warn("Unable to get stats from log file: %s.", file.Name())
			continue
		}
		tails[topic] = stat.Size()
	}

	return tails

}
예제 #7
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

}
예제 #8
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

}
예제 #9
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

}