code.vegaprotocol.io/vega@v0.79.0/core/broker/file_client.go (about) 1 // Copyright (C) 2023 Gobalsky Labs Limited 2 // 3 // This program is free software: you can redistribute it and/or modify 4 // it under the terms of the GNU Affero General Public License as 5 // published by the Free Software Foundation, either version 3 of the 6 // License, or (at your option) any later version. 7 // 8 // This program is distributed in the hope that it will be useful, 9 // but WITHOUT ANY WARRANTY; without even the implied warranty of 10 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 11 // GNU Affero General Public License for more details. 12 // 13 // You should have received a copy of the GNU Affero General Public License 14 // along with this program. If not, see <http://www.gnu.org/licenses/>. 15 16 package broker 17 18 import ( 19 "encoding/binary" 20 "fmt" 21 "os" 22 "path/filepath" 23 "sync" 24 25 "code.vegaprotocol.io/vega/core/events" 26 "code.vegaprotocol.io/vega/logging" 27 28 "google.golang.org/protobuf/proto" 29 ) 30 31 type FileClient struct { 32 log *logging.Logger 33 34 config *FileConfig 35 36 file *os.File 37 mut sync.RWMutex 38 seqNum uint64 39 } 40 41 const ( 42 NumberOfSeqNumBytes = 8 43 NumberOfSizeBytes = 4 44 45 namedFileClientLogger = "file-client" 46 ) 47 48 func NewFileClient(log *logging.Logger, config *FileConfig) (*FileClient, error) { 49 log = log.Named(namedFileClientLogger) 50 fc := &FileClient{ 51 log: log, 52 config: config, 53 } 54 55 filePath, err := filepath.Abs(config.File) 56 if err != nil { 57 return nil, fmt.Errorf("unable to determine absolute path of file %s: %w", config.File, err) 58 } 59 60 fc.file, err = os.Create(filePath) 61 62 if err != nil { 63 return nil, fmt.Errorf("unable to create file %s: %w", filePath, err) 64 } 65 66 log.Infof("persisting events to: %s\n", filePath) 67 68 return fc, nil 69 } 70 71 func (fc *FileClient) SendBatch(evts []events.Event) error { 72 for _, evt := range evts { 73 if err := fc.Send(evt); err != nil { 74 return err 75 } 76 } 77 return nil 78 } 79 80 func (fc *FileClient) Send(event events.Event) error { 81 fc.mut.RLock() 82 defer fc.mut.RUnlock() 83 84 err := WriteToBufferFile(fc.file, fc.seqNum, event) 85 fc.seqNum++ 86 87 if err != nil { 88 return fmt.Errorf("failed to write event to buffer file: %w", err) 89 } 90 91 return nil 92 } 93 94 func WriteToBufferFile(bufferFile *os.File, bufferSeqNum uint64, event events.Event) error { 95 rawEvent, err := proto.Marshal(event.StreamMessage()) 96 if err != nil { 97 return fmt.Errorf("failed to marshal bus event:%w", err) 98 } 99 return WriteRawToBufferFile(bufferFile, bufferSeqNum, rawEvent) 100 } 101 102 func WriteRawToBufferFile(bufferFile *os.File, bufferSeqNum uint64, rawEvent []byte) error { 103 seqNumBytes := make([]byte, NumberOfSeqNumBytes) 104 sizeBytes := make([]byte, NumberOfSizeBytes) 105 106 size := NumberOfSeqNumBytes + uint32(len(rawEvent)) 107 108 binary.BigEndian.PutUint64(seqNumBytes, bufferSeqNum) 109 binary.BigEndian.PutUint32(sizeBytes, size) 110 allBytes := append([]byte{}, sizeBytes...) 111 allBytes = append(allBytes, seqNumBytes...) 112 allBytes = append(allBytes, rawEvent...) 113 _, err := bufferFile.Write(allBytes) 114 if err != nil { 115 return fmt.Errorf("failed to write to buffer file:%w", err) 116 } 117 118 return nil 119 } 120 121 // Replace - in case of a config change, just replace the file we're writing to with the new one. 122 func (fc *FileClient) Replace(newFC *FileClient) { 123 fc.mut.Lock() 124 defer fc.mut.Unlock() 125 fc.config = newFC.config 126 _ = fc.file.Close() 127 fc.file = newFC.file 128 } 129 130 func (fc *FileClient) Close() error { 131 return fc.file.Close() 132 }