github.com/cloud-foundations/dominator@v0.0.0-20221004181915-6e4fee580046/lib/objectserver/client/queue.go (about) 1 package client 2 3 import ( 4 "crypto/sha512" 5 "fmt" 6 "io" 7 8 "github.com/Cloud-Foundations/Dominator/lib/errors" 9 "github.com/Cloud-Foundations/Dominator/lib/hash" 10 "github.com/Cloud-Foundations/Dominator/lib/queue" 11 "github.com/Cloud-Foundations/Dominator/lib/srpc" 12 "github.com/Cloud-Foundations/Dominator/proto/objectserver" 13 ) 14 15 func newObjectAdderQueue(client *srpc.Client) (*ObjectAdderQueue, error) { 16 var objQ ObjectAdderQueue 17 var err error 18 objQ.conn, err = client.Call("ObjectServer.AddObjects") 19 if err != nil { 20 return nil, err 21 } 22 getResponseSendChan, getResponseReceiveChan := queue.NewEventQueue() 23 errorChan := make(chan error, 1024) 24 objQ.getResponseChan = getResponseSendChan 25 objQ.errorChan = errorChan 26 objQ.sendSemaphore = make(chan struct{}, 1) 27 go readResponses(objQ.conn, getResponseReceiveChan, errorChan) 28 return &objQ, nil 29 } 30 31 func (objQ *ObjectAdderQueue) add(reader io.Reader, length uint64) ( 32 hash.Hash, error) { 33 var hashVal hash.Hash 34 data := make([]byte, length) 35 nRead, err := io.ReadFull(reader, data) 36 if err != nil { 37 return hashVal, err 38 } 39 if uint64(nRead) != length { 40 return hashVal, errors.New(fmt.Sprintf( 41 "failed to read file data, wanted: %d, got: %d bytes", 42 length, nRead)) 43 } 44 hasher := sha512.New() 45 if _, err := hasher.Write(data); err != nil { 46 return hashVal, err 47 } 48 copy(hashVal[:], hasher.Sum(nil)) 49 err = objQ.addData(data, hashVal) 50 return hashVal, err 51 } 52 53 func (objQ *ObjectAdderQueue) addData(data []byte, hashVal hash.Hash) error { 54 if err := objQ.consumeErrors(false); err != nil { 55 return err 56 } 57 // Send in a goroutine to increase concurrency. A small win. 58 objQ.sendSemaphore <- struct{}{} 59 go func() { 60 defer func() { 61 <-objQ.sendSemaphore 62 }() 63 var request objectserver.AddObjectRequest 64 request.Length = uint64(len(data)) 65 request.ExpectedHash = &hashVal 66 objQ.conn.Encode(request) 67 objQ.conn.Write(data) 68 objQ.getResponseChan <- struct{}{} 69 }() 70 return nil 71 } 72 73 func (objQ *ObjectAdderQueue) close() error { 74 // Wait for any sends in progress to complete. 75 objQ.sendSemaphore <- struct{}{} 76 var request objectserver.AddObjectRequest 77 err := objQ.conn.Encode(request) 78 err = updateError(err, objQ.conn.Flush()) 79 close(objQ.getResponseChan) 80 err = updateError(err, objQ.consumeErrors(true)) 81 return updateError(err, objQ.conn.Close()) 82 } 83 84 func updateError(oldError, newError error) error { 85 if oldError == nil { 86 return newError 87 } 88 return oldError 89 } 90 91 func (objQ *ObjectAdderQueue) consumeErrors(untilClose bool) error { 92 if untilClose { 93 for err := range objQ.errorChan { 94 if err != nil { 95 return err 96 } 97 } 98 } else { 99 for len(objQ.errorChan) > 0 { 100 err := <-objQ.errorChan 101 if err != nil { 102 return err 103 } 104 } 105 } 106 return nil 107 } 108 109 func readResponses(conn *srpc.Conn, getResponseChan <-chan struct{}, 110 errorChan chan<- error) { 111 defer close(errorChan) 112 for range getResponseChan { 113 var reply objectserver.AddObjectResponse 114 err := conn.Decode(&reply) 115 if err == nil { 116 err = errors.New(reply.ErrorString) 117 } 118 errorChan <- err 119 if err != nil { 120 break 121 } 122 } 123 }