code: func (s *session) heartbeat(ctx *ServerContext) {
  	// If sessions are being recorded at the proxy, an identical version of this
  	// goroutine is running in the proxy, which means it does not need to run here.
  	if ctx.ClusterConfig.GetSessionRecording() == services.RecordAtProxy &&
  		s.registry.srv.Component() == teleport.ComponentNode {
  		return
  	}
  
  	// If no session server (endpoint interface for active sessions) is passed in
  	// (for example Teleconsole does this) then nothing to sync.
  	sessionServer := s.registry.srv.GetSessionServer()
  	if sessionServer == nil {
  		return
  	}
  
  	s.log.Debugf("Starting poll and sync of terminal size to all parties.")
  	defer s.log.Debugf("Stopping poll and sync of terminal size to all parties.")
  
  	tickerCh := time.NewTicker(defaults.SessionRefreshPeriod)
  	defer tickerCh.Stop()
  
  	// Loop as long as the session is active, updating the session in the backend.
  	for {
  		select {
  		case <-tickerCh.C:
  			partyList := s.exportPartyMembers()
  
  			var active = true
  			err := sessionServer.UpdateSession(rsession.UpdateRequest{
  				Namespace: s.getNamespace(),
  				ID:        s.id,
  				Active:    &active,
  				Parties:   &partyList,
  			})
  			if err != nil {
  				s.log.Warnf("Unable to update session %v as active: %v", s.id, err)
  			}
  		case <-s.closeC:
  			return
  		}
  	}
  }
docs: // heartbeat will loop as long as the session is not closed and mark it as
  // active and update the list of party members. If the session are recorded at
  // the proxy, then this function does nothing as it's counterpart
  // in the proxy will do this work.