func (fsm *FSM) Apply(l *raft.Log) interface{} { // Skip all messages that are raft-related. if l.Type != raft.LogCommand { return nil } if err := fsm.ircstore.StoreLog(l); err != nil { log.Panicf("Could not persist message in irclogs/: %v", err) } msg := types.NewRobustMessageFromBytes(l.Data) log.Printf("Apply(msg.Type=%s)\n", msg.Type) defer func() { if msg.Type == types.RobustMessageOfDeath { return } if r := recover(); r != nil { // Panics in ircserver.ProcessMessage() are a problem, since // they will bring down the entire raft cluster and you cannot // bring up any raft node anymore without deleting the entire // log. // // Therefore, when we panic, we invalidate the log entry in // question before crashing. This doesn’t fix the underlying // bug, i.e. an IRC message will then go unhandled, but it // prevents RobustIRC from dying horribly in such a situation. msg.Type = types.RobustMessageOfDeath data, err := json.Marshal(msg) if err != nil { glog.Fatalf("Could not marshal message: %v", err) } l.Data = data if err := fsm.store.StoreLog(l); err != nil { glog.Fatalf("Could not store log while marking message as message of death: %v", err) } log.Printf("Marked %+v as message of death\n", l) glog.Fatalf("%v", r) } }() switch msg.Type { case types.RobustMessageOfDeath: // To prevent the message from being accepted again. ircServer.UpdateLastClientMessageID(&msg) log.Printf("Skipped message of death.\n") case types.RobustCreateSession: ircServer.CreateSession(msg.Id, msg.Data) case types.RobustDeleteSession: if _, err := ircServer.GetSession(msg.Session); err == nil { // TODO(secure): overwrite QUIT messages for services with an faq entry explaining that they are not robust yet. reply := ircServer.ProcessMessage(msg.Id, msg.Session, irc.ParseMessage("QUIT :"+string(msg.Data))) ircServer.SendMessages(reply, msg.Session, msg.Id.Id) } case types.RobustIRCFromClient: // Need to do this first, because ircserver.ProcessMessage could delete // the session, e.g. by using KILL or QUIT. if err := ircServer.UpdateLastClientMessageID(&msg); err != nil { log.Printf("Error updating the last message for session: %v\n", err) } else { reply := ircServer.ProcessMessage(msg.Id, msg.Session, irc.ParseMessage(string(msg.Data))) ircServer.SendMessages(reply, msg.Session, msg.Id.Id) } case types.RobustConfig: newCfg, err := config.FromString(string(msg.Data)) if err != nil { log.Printf("Skipping unexpectedly invalid configuration (%v)\n", err) } else { netConfig = newCfg ircServer.Config = netConfig.IRC } } appliedMessages.WithLabelValues(msg.Type.String()).Inc() return nil }