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 }