Ejemplo n.º 1
0
// SynchronizedWithNetwork returns an error if the time of any of |peers| is
// too far off the local time.
func SynchronizedWithNetwork(peerAddr string, peers []string, networkPassword string) error {
	var collectPeers []string
	for _, peer := range peers {
		if peer != peerAddr {
			collectPeers = append(collectPeers, peer)
		}
	}
	log.Printf("Collecting time of remaining nodes %v\n", collectPeers)
	results, err := collectTime(collectPeers, networkPassword)
	if err != nil {
		glog.Warningf("Could not collect time from all of %v: %v\n", collectPeers, err)
	}
	return synchronizedWithNetwork(results)
}
Ejemplo n.º 2
0
// SynchronizedWithMasterAndNetwork returns an error if the time of either
// |join| or any other node in the network is too far off the local time.
func SynchronizedWithMasterAndNetwork(peerAddr, join, networkPassword string) error {
	result, status, err := getServerTime(join, networkPassword)
	if err != nil {
		log.Fatalf("Could not join %q: %v\n", join, err)
	}
	var peers []string
	for _, peer := range status.Peers {
		if peer != peerAddr && peer != join {
			peers = append(peers, peer)
		}
	}
	log.Printf("Collecting time of remaining nodes %v\n", peers)
	results, err := collectTime(peers, networkPassword)
	if err != nil {
		glog.Warningf("Could not collect time from all of %v: %v\n", peers, err)
	}
	results = append(results, result)
	return synchronizedWithNetwork(results)
}
Ejemplo n.º 3
0
func handleGetMessages(w http.ResponseWriter, r *http.Request, ps httprouter.Params) {
	// Avoid sessionOrProxy() because GetMessages can be answered on any raft
	// node, it’s a read-only request.
	session, err := session(r, ps)
	if err != nil {
		if err == ircserver.ErrSessionNotYetSeen {
			http.Error(w, err.Error(), http.StatusInternalServerError)
		} else {
			http.Error(w, err.Error(), http.StatusNotFound)
		}
		return
	}

	remoteAddr := r.RemoteAddr
	getMessagesRequestsMu.Lock()
	GetMessageRequests[remoteAddr] = GetMessageStats{
		Session:   session,
		Nick:      ircServer.GetNick(session),
		Started:   time.Now(),
		UserAgent: r.Header.Get("User-Agent"),
	}
	getMessagesRequestsMu.Unlock()
	defer func() {
		getMessagesRequestsMu.Lock()
		delete(GetMessageRequests, remoteAddr)
		getMessagesRequestsMu.Unlock()
	}()

	lastSeen := ircServer.GetStartId(session)
	lastSeenStr := r.FormValue("lastseen")
	if lastSeenStr != "0.0" && lastSeenStr != "" {
		parts := strings.Split(lastSeenStr, ".")
		if len(parts) != 2 {
			log.Printf("cannot parse %q\n", lastSeenStr)
			http.Error(w, fmt.Sprintf("Malformed lastseen value (%q)", lastSeenStr),
				http.StatusInternalServerError)
			return
		}
		first, err := strconv.ParseInt(parts[0], 0, 64)
		if err != nil {
			log.Printf("cannot parse %q\n", lastSeenStr)
			http.Error(w, fmt.Sprintf("Malformed lastseen value (%q)", lastSeenStr),
				http.StatusInternalServerError)
			return
		}
		last, err := strconv.ParseInt(parts[1], 0, 64)
		if err != nil {
			log.Printf("cannot parse %q\n", lastSeenStr)
			http.Error(w, fmt.Sprintf("Malformed lastseen value (%q)", lastSeenStr),
				http.StatusInternalServerError)
			return
		}
		lastSeen = types.RobustId{
			Id:    first,
			Reply: last,
		}
		log.Printf("Trying to resume at %v\n", lastSeen)
	}

	enc := json.NewEncoder(w)
	flushTimer := time.NewTimer(1 * time.Second)
	flushTimer.Stop()
	var lastFlush time.Time
	willFlush := false
	msgschan := make(chan []*types.RobustMessage)
	ctx, cancel := context.WithCancel(context.Background())
	go func() {
		var msgs []*types.RobustMessage
		pingDone := make(chan bool)
		go func() {
			pingTicker := time.NewTicker(pingInterval)
			for {
				select {
				case <-pingTicker.C:
					msgschan <- []*types.RobustMessage{pingMessage()}
				case <-pingDone:
					pingTicker.Stop()
					return
				}
			}
		}()

		// With the following output messages stored for a session:
		// Id                  Reply
		// 1431542836610113945.1
		// 1431542836610113945.2     |lastSeen|
		// 1431542836610113945.3
		// 1431542836691955391.1
		// …when resuming, GetNext(1431542836610113945.2) will return
		// 1431542836691955391.*, skipping the remaining messages with
		// Id=1431542836610113945.
		// Hence, we need to Get(1431542836610113945.2) to send
		// 1431542836610113945.3 and following to the client.
		if msgs, ok := ircServer.Get(lastSeen); ok && int(lastSeen.Reply) < len(msgs) {
			msgschan <- msgs[lastSeen.Reply:]
		}

		for {
			if ctx.Err() != nil {
				pingDone <- true
				close(msgschan)
				return
			}
			msgs = ircServer.GetNext(ctx, lastSeen)
			if len(msgs) == 0 {
				continue
			}
			// This check prevents replaying old messages in the scenario where
			// the client has seen newer messages than the server, for example
			// because the server is currently recovering from a snapshot after
			// being restarted, or because the server’s network connection to
			// the rest of the network is currently slow.
			if msgs[0].Id.Id < lastSeen.Id {
				glog.Warningf("lastSeen (%d) more recent than GetNext() result %d\n", lastSeen.Id, msgs[0].Id.Id)
				glog.Warningf("This should only happen while the server is recovering from a snapshot\n")
				glog.Warningf("The message in question is %v\n", msgs[0])
				// Prevent busylooping while new messages are applied.
				time.Sleep(250 * time.Millisecond)
				continue
			}

			lastSeen = msgs[0].Id
			msgschan <- msgs
		}
	}()
	defer func() {
		cancel()
		ircServer.InterruptGetNext()
		for _ = range msgschan {
		}
	}()
	for {
		select {
		case msgs := <-msgschan:
			for _, msg := range msgs {
				if msg.Type != types.RobustPing && !msg.InterestingFor[session.Id] {
					continue
				}

				if err := enc.Encode(msg); err != nil {
					log.Printf("Error encoding JSON: %v\n", err)
					return
				}
			}

			if _, err := ircServer.GetSession(session); err != nil {
				// Session was deleted in the meanwhile, abort this request.
				return
			}

			updateLastContact()

			// The 10 seconds threshold is arbitrary. The only criterion is
			// that it must be higher than raft’s HeartbeatTimeout of 2s. The
			// higher it is chosen, the longer users have to wait until they
			// can connect to a different node. Note that in the worst case,
			// |pingInterval| = 20s needs to pass before this threshold is
			// evaluated.
			if node.State() != raft.Leader && time.Since(lastContact) > 10*time.Second {
				// This node is neither the leader nor was it recently in
				// contact with the master, indicating that it is partitioned
				// from the rest of the network. We abort this GetMessages
				// request so that clients can connect to a different server
				// and receive new messages.
				log.Printf("Aborting GetMessages request due to LastContact (%v) too long ago\n", node.LastContact())
				return
			}

			if time.Since(lastFlush) > 100*time.Millisecond {
				if f, ok := w.(http.Flusher); ok {
					f.Flush()
				}
				lastFlush = time.Now()
			} else if !willFlush {
				// Delay flushing by 10ms to avoid flushing too often, which
				// results in lots of write() syscall.
				flushTimer.Reset(10 * time.Millisecond)
				willFlush = true
			}

		case <-flushTimer.C:
			if f, ok := w.(http.Flusher); ok {
				f.Flush()
			}
			willFlush = false
			lastFlush = time.Now()
		}
	}
}