-
Notifications
You must be signed in to change notification settings - Fork 1.7k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
[feat] - additional buffer pool #2829
Changes from all commits
dea33d2
464bf31
37c5fc2
77addfb
0d0a63c
ac1b90d
967a002
03c6682
c33ba5d
7efeefb
7dbcb54
f6683ff
93ac4a8
3106235
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -15,12 +15,6 @@ import ( | |
"github.com/trufflesecurity/trufflehog/v3/pkg/cleantemp" | ||
) | ||
|
||
// sharedBufferPool is the shared buffer pool used by all BufferedFileWriters. | ||
// This allows for efficient reuse of buffers across multiple writers. | ||
var sharedBufferPool *pool.Pool | ||
|
||
func init() { sharedBufferPool = pool.NewBufferPool() } | ||
|
||
type bufferedFileWriterMetrics struct{} | ||
|
||
func (bufferedFileWriterMetrics) recordDataProcessed(size uint64, dur time.Duration) { | ||
|
@@ -33,6 +27,30 @@ func (bufferedFileWriterMetrics) recordDiskWrite(size int64) { | |
fileSizeHistogram.Observe(float64(size)) | ||
} | ||
|
||
type PoolSize int | ||
|
||
const ( | ||
Default PoolSize = iota | ||
Large | ||
) | ||
|
||
const ( | ||
defaultBufferSize = 1 << 12 // 4KB | ||
largeBufferSize = 1 << 16 // 64KB | ||
) | ||
|
||
func init() { | ||
defaultBufferPool = pool.NewBufferPool(defaultBufferSize) | ||
largeBufferPool = pool.NewBufferPool(largeBufferSize) | ||
} | ||
|
||
// Different buffer pools for different buffer sizes. | ||
// This allows for more efficient memory management based on the size of the data being written. | ||
var ( | ||
defaultBufferPool *pool.Pool | ||
largeBufferPool *pool.Pool | ||
) | ||
|
||
// state represents the current mode of BufferedFileWriter. | ||
type state uint8 | ||
|
||
|
@@ -67,28 +85,51 @@ func WithThreshold(threshold uint64) Option { | |
return func(w *BufferedFileWriter) { w.threshold = threshold } | ||
} | ||
|
||
// WithBufferSize sets the buffer size for the BufferedFileWriter. | ||
func WithBufferSize(size PoolSize) Option { | ||
return func(w *BufferedFileWriter) { | ||
switch size { | ||
case Default: | ||
w.bufPool = defaultBufferPool | ||
case Large: | ||
w.bufPool = largeBufferPool | ||
default: | ||
w.bufPool = defaultBufferPool | ||
} | ||
} | ||
} | ||
|
||
const defaultThreshold = 10 * 1024 * 1024 // 10MB | ||
// New creates a new BufferedFileWriter with the given options. | ||
func New(opts ...Option) *BufferedFileWriter { | ||
w := &BufferedFileWriter{ | ||
threshold: defaultThreshold, | ||
state: writeOnly, | ||
bufPool: sharedBufferPool, | ||
} | ||
|
||
for _, opt := range opts { | ||
opt(w) | ||
} | ||
|
||
if w.bufPool == nil { | ||
w.bufPool = defaultBufferPool | ||
} | ||
|
||
return w | ||
} | ||
|
||
// NewFromReader creates a new instance of BufferedFileWriter and writes the content from the provided reader to the writer. | ||
func NewFromReader(r io.Reader, opts ...Option) (*BufferedFileWriter, error) { | ||
opts = append(opts, WithBufferSize(Large)) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. If I'm reading this right, you only ever use the explicit option to use the large buffer pool, and get the default one via default method calls. I think this is a little awkward in a way that could be remediated by making the option a boolean called something like There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Yes, I did consider that approach. I thought making it configurable might be appropriate, anticipating additional sizes for future use cases. However, I may have jumped the gun and overcomplicated the solution. |
||
writer := New(opts...) | ||
if _, err := io.Copy(writer, r); err != nil && !errors.Is(err, io.EOF) { | ||
return nil, fmt.Errorf("error writing to buffered file writer: %w", err) | ||
} | ||
|
||
if writer.buf == nil { | ||
return nil, fmt.Errorf("buffer is empty, no reader created") | ||
} | ||
|
||
return writer, nil | ||
} | ||
|
||
|
@@ -163,9 +204,12 @@ func (w *BufferedFileWriter) Write(data []byte) (int, error) { | |
// This ensures all the data is in one place - either entirely in the buffer or the file. | ||
if bufferLength > 0 { | ||
if _, err := w.buf.WriteTo(w.file); err != nil { | ||
if err := os.RemoveAll(w.filename); err != nil { | ||
return 0, fmt.Errorf("failed to remove file: %w", err) | ||
} | ||
return 0, err | ||
} | ||
w.bufPool.Put(w.buf) | ||
w.buf.Reset() | ||
} | ||
} | ||
|
||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why did you change this?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The main reason is convenience. Using an
int
instead of auint32
eliminates the need to cast in all the places where it's used.