github.com/pingcap/br@v5.3.0-alpha.0.20220125034240-ec59c7b6ce30+incompatible/pkg/storage/writer.go (about) 1 package storage 2 3 import ( 4 "bytes" 5 "compress/gzip" 6 "context" 7 "io" 8 9 "github.com/pingcap/errors" 10 ) 11 12 // CompressType represents the type of compression. 13 type CompressType uint8 14 15 const ( 16 // NoCompression won't compress given bytes. 17 NoCompression CompressType = iota 18 // Gzip will compress given bytes in gzip format. 19 Gzip 20 ) 21 22 type flusher interface { 23 Flush() error 24 } 25 26 type emptyFlusher struct{} 27 28 func (e *emptyFlusher) Flush() error { 29 return nil 30 } 31 32 type interceptBuffer interface { 33 io.WriteCloser 34 flusher 35 Len() int 36 Cap() int 37 Bytes() []byte 38 Reset() 39 } 40 41 func newInterceptBuffer(chunkSize int, compressType CompressType) interceptBuffer { 42 if compressType == NoCompression { 43 return newNoCompressionBuffer(chunkSize) 44 } 45 return newSimpleCompressBuffer(chunkSize, compressType) 46 } 47 48 func newCompressWriter(compressType CompressType, w io.Writer) simpleCompressWriter { 49 switch compressType { 50 case Gzip: 51 return gzip.NewWriter(w) 52 default: 53 return nil 54 } 55 } 56 57 func newCompressReader(compressType CompressType, r io.Reader) (io.ReadCloser, error) { 58 switch compressType { 59 case Gzip: 60 return gzip.NewReader(r) 61 default: 62 return nil, nil 63 } 64 } 65 66 type noCompressionBuffer struct { 67 *bytes.Buffer 68 } 69 70 func (b *noCompressionBuffer) Flush() error { 71 return nil 72 } 73 74 func (b *noCompressionBuffer) Close() error { 75 return nil 76 } 77 78 func newNoCompressionBuffer(chunkSize int) *noCompressionBuffer { 79 return &noCompressionBuffer{bytes.NewBuffer(make([]byte, 0, chunkSize))} 80 } 81 82 type simpleCompressWriter interface { 83 io.WriteCloser 84 flusher 85 } 86 87 type simpleCompressBuffer struct { 88 *bytes.Buffer 89 compressWriter simpleCompressWriter 90 len int 91 cap int 92 } 93 94 func (b *simpleCompressBuffer) Write(p []byte) (int, error) { 95 written, err := b.compressWriter.Write(p) 96 b.len += written 97 return written, errors.Trace(err) 98 } 99 100 func (b *simpleCompressBuffer) Len() int { 101 return b.len 102 } 103 104 func (b *simpleCompressBuffer) Cap() int { 105 return b.cap 106 } 107 108 func (b *simpleCompressBuffer) Reset() { 109 b.len = 0 110 b.Buffer.Reset() 111 } 112 113 func (b *simpleCompressBuffer) Flush() error { 114 return b.compressWriter.Flush() 115 } 116 117 func (b *simpleCompressBuffer) Close() error { 118 return b.compressWriter.Close() 119 } 120 121 func newSimpleCompressBuffer(chunkSize int, compressType CompressType) *simpleCompressBuffer { 122 bf := bytes.NewBuffer(make([]byte, 0, chunkSize)) 123 return &simpleCompressBuffer{ 124 Buffer: bf, 125 len: 0, 126 cap: chunkSize, 127 compressWriter: newCompressWriter(compressType, bf), 128 } 129 } 130 131 type bufferedWriter struct { 132 buf interceptBuffer 133 writer ExternalFileWriter 134 } 135 136 func (u *bufferedWriter) Write(ctx context.Context, p []byte) (int, error) { 137 bytesWritten := 0 138 for u.buf.Len()+len(p) > u.buf.Cap() { 139 // We won't fit p in this chunk 140 141 // Is this chunk full? 142 chunkToFill := u.buf.Cap() - u.buf.Len() 143 if chunkToFill > 0 { 144 // It's not full so we write enough of p to fill it 145 prewrite := p[0:chunkToFill] 146 w, err := u.buf.Write(prewrite) 147 bytesWritten += w 148 if err != nil { 149 return bytesWritten, errors.Trace(err) 150 } 151 p = p[w:] 152 } 153 u.buf.Flush() 154 err := u.uploadChunk(ctx) 155 if err != nil { 156 return 0, errors.Trace(err) 157 } 158 } 159 w, err := u.buf.Write(p) 160 bytesWritten += w 161 return bytesWritten, errors.Trace(err) 162 } 163 164 func (u *bufferedWriter) uploadChunk(ctx context.Context) error { 165 if u.buf.Len() == 0 { 166 return nil 167 } 168 b := u.buf.Bytes() 169 u.buf.Reset() 170 _, err := u.writer.Write(ctx, b) 171 return errors.Trace(err) 172 } 173 174 func (u *bufferedWriter) Close(ctx context.Context) error { 175 u.buf.Close() 176 err := u.uploadChunk(ctx) 177 if err != nil { 178 return errors.Trace(err) 179 } 180 return u.writer.Close(ctx) 181 } 182 183 // NewUploaderWriter wraps the Writer interface over an uploader. 184 func NewUploaderWriter(writer ExternalFileWriter, chunkSize int, compressType CompressType) ExternalFileWriter { 185 return newBufferedWriter(writer, chunkSize, compressType) 186 } 187 188 // newBufferedWriter is used to build a buffered writer. 189 func newBufferedWriter(writer ExternalFileWriter, chunkSize int, compressType CompressType) *bufferedWriter { 190 return &bufferedWriter{ 191 writer: writer, 192 buf: newInterceptBuffer(chunkSize, compressType), 193 } 194 } 195 196 // BytesWriter is a Writer implementation on top of bytes.Buffer that is useful for testing. 197 type BytesWriter struct { 198 buf *bytes.Buffer 199 } 200 201 // Write delegates to bytes.Buffer. 202 func (u *BytesWriter) Write(ctx context.Context, p []byte) (int, error) { 203 return u.buf.Write(p) 204 } 205 206 // Close delegates to bytes.Buffer. 207 func (u *BytesWriter) Close(ctx context.Context) error { 208 // noop 209 return nil 210 } 211 212 // Bytes delegates to bytes.Buffer. 213 func (u *BytesWriter) Bytes() []byte { 214 return u.buf.Bytes() 215 } 216 217 // String delegates to bytes.Buffer. 218 func (u *BytesWriter) String() string { 219 return u.buf.String() 220 } 221 222 // Reset delegates to bytes.Buffer. 223 func (u *BytesWriter) Reset() { 224 u.buf.Reset() 225 } 226 227 // NewBufferWriter creates a Writer that simply writes to a buffer (useful for testing). 228 func NewBufferWriter() *BytesWriter { 229 return &BytesWriter{buf: &bytes.Buffer{}} 230 }