Files

134 lines
3.5 KiB
Go

package filestream
import (
"io"
"sync"
"github.com/VictoriaMetrics/VictoriaMetrics/lib/fs/fsutil"
"github.com/VictoriaMetrics/VictoriaMetrics/lib/logger"
)
// ParallelFileCreator is used for parallel creating of files for the given dstPath.
//
// ParallelFileCreator is needed for speeding up creating many files on high-latency
// storage systems such as NFS or Ceph.
type ParallelFileCreator struct {
tasks []parallelFileCreatorTask
}
type parallelFileCreatorTask struct {
dstPath string
wc *WriteCloser
nocache bool
}
// Add registers a task for creating the file at dstPath and assigning it to *wc.
//
// Tasks are executed in parallel on Run() call.
func (pfc *ParallelFileCreator) Add(dstPath string, wc *WriteCloser, nocache bool) {
pfc.tasks = append(pfc.tasks, parallelFileCreatorTask{
dstPath: dstPath,
wc: wc,
nocache: nocache,
})
}
// Run runs all the registered tasks for creating files in parallel.
func (pfc *ParallelFileCreator) Run() {
var wg sync.WaitGroup
concurrencyCh := fsutil.GetConcurrencyCh()
for _, task := range pfc.tasks {
concurrencyCh <- struct{}{}
wg.Go(func() {
*task.wc = MustCreate(task.dstPath, task.nocache)
<-concurrencyCh
})
}
wg.Wait()
}
// ParallelFileOpener is used for parallel opening of files at the given dstPath.
//
// ParallelFileOpener is needed for speeding up opening many files on high-latency
// storage systems such as NFS or Ceph.
type ParallelFileOpener struct {
tasks []parallelFileOpenerTask
}
type parallelFileOpenerTask struct {
path string
rc *ReadCloser
nocache bool
}
// Add registers a task for opening the file ath the given path and assigning it to *rc.
//
// Tasks are executed in parallel on Run() call.
func (pfo *ParallelFileOpener) Add(path string, rc *ReadCloser, nocache bool) {
pfo.tasks = append(pfo.tasks, parallelFileOpenerTask{
path: path,
rc: rc,
nocache: nocache,
})
}
// Run runs all the registered tasks for opening files in parallel.
func (pfo *ParallelFileOpener) Run() {
var wg sync.WaitGroup
concurrencyCh := fsutil.GetConcurrencyCh()
for _, task := range pfo.tasks {
concurrencyCh <- struct{}{}
wg.Go(func() {
*task.rc = MustOpen(task.path, task.nocache)
<-concurrencyCh
})
}
wg.Wait()
}
// ParallelStreamWriter is used for parallel writing of data from io.WriterTo to the given dstPath files.
//
// ParallelStreamWriter is needed for speeding up writing data to many files on high-latency
// storage systems such as NFS or Ceph.
type ParallelStreamWriter struct {
tasks []parallelStreamWriterTask
}
type parallelStreamWriterTask struct {
dstPath string
src io.WriterTo
}
// Add adds a task to execute in parallel - to write the data from src to the dstPath.
//
// Tasks are executed in parallel on Run() call.
func (psw *ParallelStreamWriter) Add(dstPath string, src io.WriterTo) {
psw.tasks = append(psw.tasks, parallelStreamWriterTask{
dstPath: dstPath,
src: src,
})
}
// Run executes all the tasks added via Add() call in parallel.
func (psw *ParallelStreamWriter) Run() {
var wg sync.WaitGroup
concurrencyCh := fsutil.GetConcurrencyCh()
for _, task := range psw.tasks {
concurrencyCh <- struct{}{}
wg.Go(func() {
f := MustCreate(task.dstPath, false)
if _, err := task.src.WriteTo(f); err != nil {
f.MustClose()
// Do not call MustRemovePath(path), so the user could inspect
// the file contents during investigation of the issue.
logger.Panicf("FATAL: cannot write data to %q: %s", task.dstPath, err)
}
f.MustClose()
<-concurrencyCh
})
}
wg.Wait()
}