github.com/cloud-foundations/dominator@v0.0.0-20221004181915-6e4fee580046/hypervisor/rpcd/getUpdates.go (about)

     1  package rpcd
     2  
     3  import (
     4  	"errors"
     5  	"io"
     6  	"time"
     7  
     8  	"github.com/Cloud-Foundations/Dominator/lib/srpc"
     9  	proto "github.com/Cloud-Foundations/Dominator/proto/hypervisor"
    10  )
    11  
    12  const (
    13  	flushDelay     = time.Millisecond * 10
    14  	heartbeatDelay = time.Minute * 15
    15  )
    16  
    17  func (t *srpcType) GetUpdates(conn *srpc.Conn) error {
    18  	heartbeatTimer := time.NewTimer(heartbeatDelay)
    19  	closeChannel, responseChannel := t.getUpdatesReader(conn, heartbeatTimer)
    20  	updateChannel := t.manager.MakeUpdateChannel()
    21  	defer t.manager.CloseUpdateChannel(updateChannel)
    22  	flushTimer := time.NewTimer(flushDelay)
    23  	var numToFlush uint
    24  	defer t.unregisterManagedExternalLeases()
    25  	for {
    26  		select {
    27  		case update, ok := <-updateChannel:
    28  			if !ok {
    29  				err := errors.New("receiver not keeping up with updates")
    30  				t.logger.Printf("error sending update: %s\n", err)
    31  				return err
    32  			}
    33  			if err := conn.Encode(update); err != nil {
    34  				t.logger.Printf("error sending update: %s\n", err)
    35  				return err
    36  			}
    37  			numToFlush++
    38  			flushTimer.Reset(flushDelay)
    39  		case update, ok := <-responseChannel:
    40  			if !ok {
    41  				err := errors.New("receiver not keeping up with reponses")
    42  				t.logger.Printf("error sending response: %s\n", err)
    43  				return err
    44  			}
    45  			if err := conn.Encode(update); err != nil {
    46  				t.logger.Printf("error sending response: %s\n", err)
    47  				return err
    48  			}
    49  			numToFlush++
    50  			flushTimer.Reset(flushDelay)
    51  		case <-flushTimer.C:
    52  			if numToFlush > 1 {
    53  				t.logger.Debugf(0, "flushing %d events\n", numToFlush)
    54  			}
    55  			numToFlush = 0
    56  			if err := conn.Flush(); err != nil {
    57  				t.logger.Printf("error flushing update(s): %s\n", err)
    58  				return err
    59  			}
    60  			heartbeatTimer.Reset(heartbeatDelay)
    61  		case <-heartbeatTimer.C:
    62  			err := conn.Encode(proto.Update{
    63  				HealthStatus: t.manager.GetHealthStatus()})
    64  			if err != nil {
    65  				t.logger.Printf("error writing heartbeat: %s\n", err)
    66  				return err
    67  			}
    68  			numToFlush = 0
    69  			if err := conn.Flush(); err != nil {
    70  				t.logger.Printf("error flushing heartbeat: %s\n", err)
    71  				return err
    72  			}
    73  			heartbeatTimer.Reset(heartbeatDelay)
    74  		case err := <-closeChannel:
    75  			if err == nil {
    76  				t.logger.Debugf(0, "update client disconnected: %s\n",
    77  					conn.RemoteAddr())
    78  				return nil
    79  			}
    80  			t.logger.Println(err)
    81  			return err
    82  		}
    83  	}
    84  }
    85  
    86  func (t *srpcType) getUpdatesReader(decoder srpc.Decoder,
    87  	heartbeatTimer *time.Timer) (
    88  	<-chan error, <-chan proto.Update) {
    89  	closeChannel := make(chan error)
    90  	responseChannel := make(chan proto.Update, 16)
    91  	go func() {
    92  		for {
    93  			var request proto.GetUpdatesRequest
    94  			if err := decoder.Decode(&request); err != nil {
    95  				if err == io.EOF {
    96  					err = nil
    97  				}
    98  				closeChannel <- err
    99  				return
   100  			}
   101  			heartbeatTimer.Reset(heartbeatDelay)
   102  			if req := request.RegisterExternalLeasesRequest; req != nil {
   103  				go t.registerManagedExternalLeases(*req)
   104  			}
   105  			update := proto.Update{HealthStatus: t.manager.GetHealthStatus()}
   106  			select {
   107  			case responseChannel <- update:
   108  			default:
   109  				close(responseChannel)
   110  				return
   111  			}
   112  		}
   113  	}()
   114  	return closeChannel, responseChannel
   115  }