github.com/cloud-foundations/dominator@v0.0.0-20221004181915-6e4fee580046/lib/filegen/client/manager.go (about) 1 package client 2 3 import ( 4 "bytes" 5 "io" 6 "time" 7 8 "github.com/Cloud-Foundations/Dominator/lib/hash" 9 "github.com/Cloud-Foundations/Dominator/lib/log" 10 "github.com/Cloud-Foundations/Dominator/lib/objectserver" 11 "github.com/Cloud-Foundations/Dominator/lib/srpc" 12 proto "github.com/Cloud-Foundations/Dominator/proto/filegenerator" 13 "github.com/Cloud-Foundations/tricorder/go/tricorder" 14 "github.com/Cloud-Foundations/tricorder/go/tricorder/units" 15 ) 16 17 func newManager(objSrv objectserver.ObjectServer, logger log.Logger) *Manager { 18 sourceReconnectChannel := make(chan string) 19 m := &Manager{ 20 sourceMap: make(map[string]*sourceType), 21 objectServer: objSrv, 22 machineMap: make(map[string]*machineType), 23 addMachineChannel: make(chan *machineType), 24 removeMachineChannel: make(chan string), 25 updateMachineChannel: make(chan *machineType), 26 serverMessageChannel: make(chan *serverMessageType), 27 sourceReconnectChannel: sourceReconnectChannel, 28 objectWaiters: make(map[hash.Hash][]chan<- hash.Hash), 29 logger: logger} 30 tricorder.RegisterMetric("filegen/client/num-object-waiters", 31 &m.numObjectWaiters.value, units.None, 32 "number of goroutines waiting for objects") 33 go m.manage(sourceReconnectChannel) 34 return m 35 } 36 37 func (m *Manager) manage(sourceConnectChannel <-chan string) { 38 for { 39 select { 40 case machine := <-m.addMachineChannel: 41 m.addMachine(machine) 42 case hostname := <-m.removeMachineChannel: 43 m.removeMachine(hostname) 44 case machine := <-m.updateMachineChannel: 45 m.updateMachine(machine) 46 case serverMessage := <-m.serverMessageChannel: 47 m.processMessage(serverMessage) 48 case sourceName := <-sourceConnectChannel: 49 m.processSourceConnect(sourceName) 50 } 51 } 52 } 53 54 func (m *Manager) processMessage(serverMessage *serverMessageType) { 55 if msg := serverMessage.serverMessage.GetObjectResponse; msg != nil { 56 if _, _, err := m.objectServer.AddObject( 57 bytes.NewReader(msg.Data), 0, &msg.Hash); err != nil { 58 m.logger.Println(err) 59 } else { 60 if waiters, ok := m.objectWaiters[msg.Hash]; ok { 61 for _, channel := range waiters { 62 channel <- msg.Hash 63 } 64 delete(m.objectWaiters, msg.Hash) 65 } 66 } 67 } 68 if msg := serverMessage.serverMessage.YieldResponse; msg != nil { 69 if machine, ok := m.machineMap[msg.Hostname]; ok { 70 m.handleYieldResponse(machine, msg.Files) 71 } // else machine no longer known. Drop the message. 72 } 73 } 74 75 func (m *Manager) processSourceConnect(sourceName string) { 76 source := m.sourceMap[sourceName] 77 for _, machine := range m.machineMap { 78 if pathnames, ok := machine.sourceToPaths[sourceName]; ok { 79 request := &proto.ClientRequest{ 80 YieldRequest: &proto.YieldRequest{machine.machine, pathnames}} 81 source.sendChannel <- request 82 } 83 } 84 } 85 86 func (m *Manager) getSource(sourceName string) *sourceType { 87 source, ok := m.sourceMap[sourceName] 88 if ok { 89 return source 90 } 91 source = new(sourceType) 92 sendChannel := make(chan *proto.ClientRequest, 4096) 93 source.sendChannel = sendChannel 94 m.sourceMap[sourceName] = source 95 go manageSource(sourceName, m.sourceReconnectChannel, sendChannel, 96 m.serverMessageChannel, m.logger) 97 return source 98 } 99 100 func manageSource(sourceName string, sourceReconnectChannel chan<- string, 101 clientRequestChannel <-chan *proto.ClientRequest, 102 serverMessageChannel chan<- *serverMessageType, logger log.Logger) { 103 closeNotifyChannel := make(chan struct{}) 104 initialRetryTimeout := time.Millisecond * 100 105 retryTimeout := initialRetryTimeout 106 reconnect := false 107 for ; ; time.Sleep(retryTimeout) { 108 if retryTimeout < time.Minute { 109 retryTimeout *= 2 110 } 111 client, err := srpc.DialHTTP("tcp", sourceName, time.Second*15) 112 if err != nil { 113 logger.Printf("error connecting to: %s: %s\n", sourceName, err) 114 continue 115 } 116 conn, err := client.Call("FileGenerator.Connect") 117 if err != nil { 118 client.Close() 119 logger.Println(err) 120 continue 121 } 122 retryTimeout = initialRetryTimeout 123 go handleServerMessages(sourceName, conn, serverMessageChannel, 124 closeNotifyChannel, logger) 125 if reconnect { 126 sourceReconnectChannel <- sourceName 127 } else { 128 reconnect = true 129 } 130 sendClientRequests(conn, clientRequestChannel, closeNotifyChannel, 131 logger) 132 conn.Close() 133 client.Close() 134 } 135 } 136 137 func sendClientRequests(conn *srpc.Conn, 138 clientRequestChannel <-chan *proto.ClientRequest, 139 closeNotifyChannel <-chan struct{}, logger log.Logger) { 140 for { 141 select { 142 case clientRequest := <-clientRequestChannel: 143 if err := conn.Encode(clientRequest); err != nil { 144 logger.Printf("error encoding client request: %s\n", err) 145 return 146 } 147 if len(clientRequestChannel) < 1 { 148 if err := conn.Flush(); err != nil { 149 logger.Printf("error flushing: %s\n", err) 150 return 151 } 152 } 153 case <-closeNotifyChannel: 154 return 155 } 156 } 157 } 158 159 func handleServerMessages(sourceName string, decoder srpc.Decoder, 160 serverMessageChannel chan<- *serverMessageType, 161 closeNotifyChannel chan<- struct{}, logger log.Logger) { 162 for { 163 var message proto.ServerMessage 164 if err := decoder.Decode(&message); err != nil { 165 if err == io.EOF { 166 logger.Printf("connection to source: %s closed\n", sourceName) 167 } else { 168 logger.Println(err) 169 } 170 closeNotifyChannel <- struct{}{} 171 return 172 } 173 serverMessageChannel <- &serverMessageType{sourceName, message} 174 } 175 }