github.com/cloud-foundations/dominator@v0.0.0-20221004181915-6e4fee580046/objectserver/rpcd/lib/addObjectsWithMaster.go (about) 1 package lib 2 3 import ( 4 "container/list" 5 "encoding/gob" 6 "fmt" 7 "io" 8 9 "github.com/Cloud-Foundations/Dominator/lib/errors" 10 "github.com/Cloud-Foundations/Dominator/lib/hash" 11 "github.com/Cloud-Foundations/Dominator/lib/log" 12 "github.com/Cloud-Foundations/Dominator/lib/objectserver" 13 oclient "github.com/Cloud-Foundations/Dominator/lib/objectserver/client" 14 "github.com/Cloud-Foundations/Dominator/lib/srpc" 15 proto "github.com/Cloud-Foundations/Dominator/proto/objectserver" 16 ) 17 18 func addObjectsWithMaster(conn *srpc.Conn, decoder srpc.Decoder, 19 encoder srpc.Encoder, objSrv objectserver.StashingObjectServer, 20 masterAddress string, logger log.DebugLogger) error { 21 logger.Printf("AddObjectsWithMaster(%s) starting\n", conn.RemoteAddr()) 22 defer conn.Flush() 23 numAdded := 0 24 numObj := 0 25 outgoingQueueSendChan, outgoingQueueReceiveChan := newOutgoingQueue() 26 errorChan := make(chan error, 1) 27 defer close(outgoingQueueSendChan) 28 go processOutgoingQueue(encoder, outgoingQueueReceiveChan, errorChan, 29 logger) 30 masterQueueSendChan, masterQueueReceiveChan := newMasterQueue() 31 defer close(masterQueueSendChan) 32 masterDrain := make(chan error, 1) 33 masterClient, err := srpc.DialHTTP("tcp", masterAddress, 0) 34 if err != nil { 35 sendError(outgoingQueueSendChan, err) 36 return nil 37 } 38 defer masterClient.Close() 39 var masterConn *srpc.Conn 40 var masterEncoder *gob.Encoder 41 locallyKnownObjects := make([]hash.Hash, 0) 42 // First process the stream of incoming objects, verify or stash+send. 43 for ; ; numObj++ { 44 if len(errorChan) > 0 { 45 break 46 } 47 var request proto.AddObjectRequest 48 var response proto.AddObjectResponse 49 err := decoder.Decode(&request) 50 if err != nil { 51 if err == io.EOF || err == io.ErrUnexpectedEOF { 52 break 53 } 54 return errors.New("error decoding: " + err.Error()) 55 } 56 if request.Length < 1 { 57 break 58 } 59 var data []byte 60 response.Hash, data, err = objSrv.StashOrVerifyObject(conn, 61 request.Length, request.ExpectedHash) 62 if err != nil { 63 sendError(outgoingQueueSendChan, err) 64 break 65 } 66 entry := make(chan proto.AddObjectResponse, 1) 67 if data == nil { // Object already exists 68 entry <- response 69 outgoingQueueSendChan <- entry 70 locallyKnownObjects = append(locallyKnownObjects, response.Hash) 71 continue 72 } 73 if masterEncoder == nil { 74 masterConn, err = masterClient.Call("ObjectServer.AddObjects") 75 if err != nil { 76 sendError(outgoingQueueSendChan, 77 errors.New("error calling master: "+masterAddress+": "+ 78 err.Error())) 79 break 80 } 81 defer func() { 82 if masterConn != nil { 83 masterConn.Close() 84 } 85 }() 86 go processResponses(gob.NewDecoder(masterConn), 87 masterQueueReceiveChan, masterDrain, objSrv) 88 masterEncoder = gob.NewEncoder(masterConn) 89 logger.Debugln(0, 90 "AddObjectsWithMaster(): called ObjectServer.AddObjects for master") 91 } 92 err = sendRequest(masterConn, masterEncoder, data, response.Hash) 93 if err != nil { 94 sendError(outgoingQueueSendChan, err) 95 break 96 } 97 masterQueueSendChan <- entry 98 outgoingQueueSendChan <- entry 99 numAdded++ 100 } 101 logger.Debugln(0, "AddObjectsWithMaster(): Read all objects from client") 102 if masterEncoder != nil { 103 err := sendRequest(masterConn, masterEncoder, nil, hash.Hash{}) 104 if err != nil { 105 sendError(outgoingQueueSendChan, err) 106 logger.Printf( 107 "AddObjectsWithMaster(): failed, %d of %d so far are new objects: %s", 108 numAdded, numObj, err) 109 return nil 110 } 111 masterQueueSendChan <- nil 112 err = <-masterDrain 113 masterConn.Close() 114 masterConn = nil 115 if err != nil { 116 logger.Printf( 117 "AddObjectsWithMaster(): failed, %d of %d so far are new objects: %s", 118 numAdded, numObj, err) 119 return nil 120 } 121 } 122 // Check for objects missing on the master. 123 err = sendMissingObjects(objSrv, masterClient, locallyKnownObjects, logger) 124 if err != nil { 125 sendError(outgoingQueueSendChan, err) 126 err = <-errorChan 127 logger.Printf( 128 "AddObjectsWithMaster(): failed, %d of %d so far are new objects: %s", 129 numAdded, numObj, err) 130 return nil 131 } 132 logger.Debugln(0, "Sending flush request for outgoing queue") 133 outgoingQueueSendChan <- nil 134 logger.Debugln(0, "Sent flush request for outgoing queue") 135 if err := <-errorChan; err != nil { 136 logger.Printf( 137 "AddObjectsWithMaster(): failed, %d of %d so far are new objects: %s", 138 numAdded, numObj, err) 139 } else { 140 logger.Printf("AddObjectsWithMaster(): %d of %d are new objects", 141 numAdded, numObj) 142 } 143 return nil 144 } 145 146 func processOutgoingQueue(encoder srpc.Encoder, 147 queue <-chan <-chan proto.AddObjectResponse, errorChan chan<- error, 148 logger log.DebugLogger) { 149 // Hold back one response until flush entry. This ensures the client will 150 // read an error message before seeing last "OK". 151 var previousResponse *proto.AddObjectResponse 152 for entry := range queue { 153 var response proto.AddObjectResponse 154 if entry != nil { 155 response = <-entry 156 if response.ErrorString != "" { // Preempt previous response. 157 previousResponse = &response 158 } 159 } 160 if previousResponse != nil { 161 if err := encoder.Encode(*previousResponse); err != nil { 162 err = errors.New("error encoding: " + err.Error()) 163 errorChan <- err 164 break 165 } 166 if err := errors.New(previousResponse.ErrorString); err != nil { 167 errorChan <- err 168 break 169 } 170 } 171 if entry == nil { 172 errorChan <- nil 173 break 174 } 175 previousResponse = &response 176 } 177 logger.Debugln(0, "Draining outgoing queue") 178 for range queue { // Drain the queue. 179 } 180 } 181 182 func processResponses(decoder *gob.Decoder, 183 queue <-chan chan<- proto.AddObjectResponse, completed chan<- error, 184 objSrv objectserver.StashingObjectServer) { 185 var err error 186 for entry := range queue { 187 if entry == nil { 188 break 189 } 190 var reply proto.AddObjectResponse 191 if err = decoder.Decode(&reply); err != nil { 192 reply.ErrorString = "error decoding: " + err.Error() 193 entry <- reply 194 break 195 } 196 if reply.ErrorString == "" { 197 if err = objSrv.CommitObject(reply.Hash); err != nil { 198 reply.ErrorString = "commit error: " + err.Error() 199 entry <- reply 200 break 201 } 202 } 203 entry <- reply 204 } 205 completed <- err 206 for range queue { // Drain the queue. 207 } 208 } 209 210 func sendRequest(conn *srpc.Conn, encoder *gob.Encoder, data []byte, 211 hashVal hash.Hash) error { 212 request := proto.AddObjectRequest{ 213 Length: uint64(len(data)), 214 ExpectedHash: &hashVal, 215 } 216 if err := encoder.Encode(request); err != nil { 217 return err 218 } 219 if len(data) > 0 { 220 _, err := conn.Write(data) 221 return err 222 } 223 return conn.Flush() 224 } 225 226 func sendError(queue chan<- <-chan proto.AddObjectResponse, err error) { 227 entry := make(chan proto.AddObjectResponse, 1) 228 entry <- proto.AddObjectResponse{ErrorString: err.Error()} 229 queue <- entry 230 } 231 232 func sendMissingObjects(objSrv objectserver.StashingObjectServer, 233 masterClient *srpc.Client, locallyKnownObjects []hash.Hash, 234 logger log.DebugLogger) error { 235 if len(locallyKnownObjects) < 1 { 236 return nil 237 } 238 logger.Debugf(0, 239 "Checking %d locally known objects for presence on master\n", 240 len(locallyKnownObjects)) 241 objClient := oclient.AttachObjectClient(masterClient) 242 lengths, err := objClient.CheckObjects(locallyKnownObjects) 243 objClient.Close() 244 if err != nil { 245 return fmt.Errorf("error checking master for known objects: %s", err) 246 } 247 allObjectsKnownOnMaster := true 248 for _, lengthOnMaster := range lengths { 249 if lengthOnMaster < 1 { 250 allObjectsKnownOnMaster = false 251 break 252 } 253 } 254 if allObjectsKnownOnMaster { 255 return nil 256 } 257 adderQueue, err := oclient.NewObjectAdderQueue(masterClient) 258 if err != nil { 259 return err 260 } 261 defer func() { 262 if adderQueue != nil { 263 adderQueue.Close() 264 } 265 }() 266 numSent := 0 267 for index, lengthOnMaster := range lengths { 268 if lengthOnMaster > 0 { 269 continue 270 } 271 hashVal := locallyKnownObjects[index] 272 length, reader, err := objectserver.GetObject(objSrv, hashVal) 273 if err != nil { 274 return err 275 } 276 data := make([]byte, length) 277 nRead, err := io.ReadFull(reader, data) 278 reader.Close() 279 if err != nil { 280 return err 281 } 282 if uint64(nRead) != length { 283 return fmt.Errorf( 284 "failed to read file data, wanted: %d, got: %d bytes", 285 length, nRead) 286 } 287 if err := adderQueue.AddData(data, hashVal); err != nil { 288 return err 289 } 290 numSent++ 291 } 292 err = adderQueue.Close() 293 adderQueue = nil 294 logger.Printf("AddObjectsWithMaster() Sent: %d of %d locally known objects\n", 295 numSent, len(locallyKnownObjects)) 296 return err 297 } 298 299 func newOutgoingQueue() (chan<- <-chan proto.AddObjectResponse, 300 <-chan <-chan proto.AddObjectResponse) { 301 send := make(chan (<-chan proto.AddObjectResponse), 1) 302 receive := make(chan (<-chan proto.AddObjectResponse), 1) 303 go manageOutgoingQueue(send, receive) 304 return send, receive 305 } 306 307 func manageOutgoingQueue(send <-chan <-chan proto.AddObjectResponse, 308 receive chan<- <-chan proto.AddObjectResponse) { 309 queue := list.New() 310 for { 311 if front := queue.Front(); front == nil { 312 if send == nil { 313 close(receive) 314 return 315 } 316 response, ok := <-send 317 if !ok { 318 close(receive) 319 return 320 } 321 queue.PushBack(response) 322 } else { 323 select { 324 case receive <- front.Value.(<-chan proto.AddObjectResponse): 325 queue.Remove(front) 326 case response, ok := <-send: 327 if ok { 328 queue.PushBack(response) 329 } else { 330 send = nil 331 } 332 } 333 } 334 } 335 } 336 337 func newMasterQueue() (chan<- chan<- proto.AddObjectResponse, 338 <-chan chan<- proto.AddObjectResponse) { 339 send := make(chan chan<- proto.AddObjectResponse, 1) 340 receive := make(chan chan<- proto.AddObjectResponse, 1) 341 go manageMasterQueue(send, receive) 342 return send, receive 343 } 344 345 func manageMasterQueue(send <-chan chan<- proto.AddObjectResponse, 346 receive chan<- chan<- proto.AddObjectResponse) { 347 queue := list.New() 348 for { 349 if front := queue.Front(); front == nil { 350 if send == nil { 351 close(receive) 352 return 353 } 354 response, ok := <-send 355 if !ok { 356 close(receive) 357 return 358 } 359 queue.PushBack(response) 360 } else { 361 select { 362 case receive <- front.Value.(chan<- proto.AddObjectResponse): 363 queue.Remove(front) 364 case response, ok := <-send: 365 if ok { 366 queue.PushBack(response) 367 } else { 368 send = nil 369 } 370 } 371 } 372 } 373 }