Repository: hashicorp/nomad
File: client/heartbeatstop.go (Go)

Code before:
// past that it should be prevented from restarting
func (h *heartbeatStop) shouldStop(alloc *structs.Allocation) bool {
	if timeout, ok := getDisconnectStopTimeout(alloc); ok {
		return h.shouldStopAfter(time.Now(), timeout)
	}
	return false
}

func (h *heartbeatStop) shouldStopAfter(now time.Time, interval time.Duration) bool {
	lastOk := h.getLastOk()
	if lastOk.IsZero() {
		return now.After(h.startupGrace)
	}
	return now.After(lastOk.Add(interval))
}

// watch is a loop that checks for allocations that should be stopped. It also manages the
// registration of allocs to be stopped in a single thread.
func (h *heartbeatStop) watch() {
	// If we never manage to successfully contact the server, we want to stop our allocs
	// after duration + start time
	h.setLastOk(time.Now())
	allocIntervals := map[string]time.Duration{}
	var interval time.Duration

	maxInterval := time.Hour
	timer, stopTimer := helper.NewStoppedTimer()
	defer stopTimer()

	for {
		// we want to fire the ticker only once the shortest
		// stop_on_client_after interval has expired. we'll reset the ticker on
		// every heartbeat and every time a new alloc appears
		interval = maxInterval
		for _, t := range allocIntervals {
			if t < interval {
				interval = t
			}
		}
		timer.Reset(interval)

		select {
		case alloc := <-h.allocHookCh:
			// receiving a new alloc implies we're still connected, so we'll go
			// back to the top to reset the interval
			if timeout, ok := getDisconnectStopTimeout(alloc); ok {
				allocIntervals[alloc.ID] = timeout
			}

		case <-h.heartbeatCh:
			continue

Review comment (performance):
is this hour arbitrary?  I don't see it in docs.

if I'm reading all this right, it represents the amount of time between loop iterations, if the `select` doesn't get poked by anything else, right?

so it isn't a documented value that users care about (or might be surprised by), it just keeps the loop looping (very slowly) in lieu of any other input?

Code after:
// past that it should be prevented from restarting
func (h *heartbeatStop) shouldStop(alloc *structs.Allocation) bool {
	if timeout, ok := getDisconnectStopTimeout(alloc); ok {
		return h.shouldStopAfter(time.Now(), timeout)
	}
	return false
}

func (h *heartbeatStop) shouldStopAfter(now time.Time, interval time.Duration) bool {
	lastOk := h.getLastOk()
	if lastOk.IsZero() {
		return now.After(h.startupGrace)
	}
	return now.After(lastOk.Add(interval))
}

// watch is a loop that checks for allocations that should be stopped. It also manages the
// registration of allocs to be stopped in a single thread.
func (h *heartbeatStop) watch() {
	// If we never manage to successfully contact the server, we want to stop our allocs
	// after duration + start time
	h.setLastOk(time.Now())
	allocIntervals := map[string]time.Duration{}

	timer, stopTimer := helper.NewStoppedTimer()
	defer stopTimer()

	for {
		// we want to fire the ticker only once the shortest
		// stop_on_client_after interval has expired. we'll reset the ticker on
		// every heartbeat and every time a new alloc appears
		var interval time.Duration
		for _, t := range allocIntervals {
			if t < interval || interval == 0 {
				interval = t
			}
		}
		if interval != 0 {
			timer.Reset(interval)
		} else {
			timer.Stop()
		}

		select {
		case <-h.heartbeatCh:
			continue

		case <-h.shutdownCh:
			return

		case alloc := <-h.allocHookCh: