func TestLogs(t *testing.T) { dir, l := LeveldbTestStore(t) defer os.RemoveAll(dir) // Should be no first index idx, err := l.FirstIndex() if err != nil { t.Fatalf("err: %v ", err) } if idx != 0 { t.Fatalf("bad idx: %d", idx) } // Should be no last index idx, err = l.LastIndex() if err != nil { t.Fatalf("err: %v ", err) } if idx != 0 { t.Fatalf("bad idx: %d", idx) } // Try a filed fetch var out raft.Log if err := l.GetLog(10, &out); err.Error() != "log not found" { t.Fatalf("err: %v ", err) } // Write out a log log := raft.Log{ Index: 1, Term: 1, Type: raft.LogCommand, Data: []byte("first"), } for i := 1; i <= 10; i++ { log.Index = uint64(i) log.Term = uint64(i) if err := l.StoreLog(&log); err != nil { t.Fatalf("err: %v", err) } } // Attempt to write multiple logs var logs []*raft.Log for i := 11; i <= 20; i++ { nl := &raft.Log{ Index: uint64(i), Term: uint64(i), Type: raft.LogCommand, Data: []byte("first"), } logs = append(logs, nl) } if err := l.StoreLogs(logs); err != nil { t.Fatalf("err: %v", err) } // Try to fetch if err := l.GetLog(10, &out); err != nil { t.Fatalf("err: %v ", err) } // Try to fetch if err := l.GetLog(20, &out); err != nil { t.Fatalf("err: %v ", err) } // Check the lowest index idx, err = l.FirstIndex() if err != nil { t.Fatalf("err: %v ", err) } if idx != 1 { t.Fatalf("bad idx: %d", idx) } // Check the highest index idx, err = l.LastIndex() if err != nil { t.Fatalf("err: %v ", err) } if idx != 20 { t.Fatalf("bad idx: %d", idx) } // Delete a suffix if err := l.DeleteRange(5, 20); err != nil { t.Fatalf("err: %v ", err) } // Verify they are all deleted for i := 5; i <= 20; i++ { if err := l.GetLog(uint64(i), &out); err != raft.ErrLogNotFound { t.Fatalf("err: %v ", err) } } // Index should be one idx, err = l.FirstIndex() if err != nil { t.Fatalf("err: %v ", err) } if idx != 1 { t.Fatalf("bad idx: %d", idx) } idx, err = l.LastIndex() if err != nil { t.Fatalf("err: %v ", err) } if idx != 4 { t.Fatalf("bad idx: %d", idx) } // Should not be able to fetch if err := l.GetLog(5, &out); err.Error() != "log not found" { t.Fatalf("err: %v ", err) } }
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 }