in quorum/quorum.go [84:169]
func BecomeMaster(uri string, db string) error {
retry := 30
for retry != 0 {
if conn, err := makeSession(uri); err == nil {
masterCollection := conn.Client.Database(db).Collection(QUORUM_COLLECTION)
status := STATUS_LOOKASIDE
entry := new(ElectionEntry)
// keep quorum
Keep:
for {
if status == STATUS_LOOKASIDE || status == STATUS_MASTER {
wait(HeartBeatPeriod)
}
switch status {
case STATUS_LOOKASIDE:
// take from database firstly
err = masterCollection.FindOne(context.Background(),
bson.M{"_id": electionObjectId}).Decode(entry)
switch err {
case nil:
if entry.Host == getNetAddr() && entry.PID == os.Getpid() {
// master is me. just update heartbeat
status = STATUS_MASTER
} else {
status = STATUS_FOLLOW
}
case mongo.ErrNoDocuments:
LOG.Debug("No master node found. we elect myself")
status = STATUS_COMPETE_MASTER
default:
LOG.Warn("Fetch master election info %s failed. %v", electionObjectId, err)
status = STATUS_SESSION_CLOSE
}
case STATUS_MASTER:
if _, err := masterCollection.UpdateOne(context.Background(),
bson.D{{"_id", electionObjectId}, {"pid", os.Getpid()}},
bson.M{"$set": promotion()}); err == nil {
masterChanged(PromoteMaster)
} else {
LOG.Warn("Update master election info failed. %v", err)
status = STATUS_LOOKASIDE
}
case STATUS_COMPETE_MASTER:
// there is no one master
competeMaster(masterCollection)
status = STATUS_LOOKASIDE
case STATUS_FOLLOW:
if master {
masterChanged(DescendMaster)
}
// there has been already another master. check its heartbeat
heartbeat := entry.Heartbeat
if time.Now().Unix()-heartbeat >= int64(HeartBeatTimeoutInSeconds) {
// I wanna be the master. DON'T care about the success of update
masterCollection.UpdateOne(context.Background(),
bson.D{{"_id", electionObjectId}},bson.M{"$set": promotion()})
LOG.Info("Expired master found. compete to become master")
// wait random time. just disrupt others compete
wait(time.Millisecond * time.Duration(rand.Uint32()%2500+1))
} else {
// follow current master
LOG.Info("Follow current master %v", entry)
}
status = STATUS_LOOKASIDE
case STATUS_SESSION_CLOSE:
conn.Close()
break Keep
}
}
}
retry--
}
return fmt.Errorf("unreachable master election mongo %s", uri)
}