2020-05-31 07:32:33 +00:00
|
|
|
package etl
|
|
|
|
|
|
|
|
import (
|
|
|
|
"bufio"
|
|
|
|
"fmt"
|
|
|
|
"io"
|
|
|
|
"io/ioutil"
|
|
|
|
"os"
|
2020-05-31 12:23:34 +00:00
|
|
|
"runtime"
|
2020-05-31 07:32:33 +00:00
|
|
|
|
2021-05-20 18:25:53 +00:00
|
|
|
"github.com/ledgerwatch/erigon/common"
|
|
|
|
"github.com/ledgerwatch/erigon/log"
|
2020-05-31 07:32:33 +00:00
|
|
|
)
|
|
|
|
|
|
|
|
type dataProvider interface {
|
2020-06-10 20:07:14 +00:00
|
|
|
Next(decoder Decoder) ([]byte, []byte, error)
|
2020-08-05 15:33:58 +00:00
|
|
|
Dispose() (uint64, error)
|
2020-05-31 07:32:33 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
type fileDataProvider struct {
|
|
|
|
file *os.File
|
|
|
|
reader io.Reader
|
|
|
|
}
|
|
|
|
|
2020-06-10 20:07:14 +00:00
|
|
|
type Encoder interface {
|
|
|
|
Encode(toWrite interface{}) error
|
|
|
|
Reset(writer io.Writer)
|
|
|
|
}
|
|
|
|
|
2020-10-23 11:18:45 +00:00
|
|
|
func FlushToDisk(encoder Encoder, currentKey []byte, b Buffer, tmpdir string) (dataProvider, error) {
|
2020-06-10 20:07:14 +00:00
|
|
|
if b.Len() == 0 {
|
2020-05-31 07:32:33 +00:00
|
|
|
return nil, nil
|
|
|
|
}
|
2020-08-23 09:53:01 +00:00
|
|
|
// if we are going to create files in the system temp dir, we don't need any
|
|
|
|
// subfolders.
|
2020-10-23 11:18:45 +00:00
|
|
|
if tmpdir != "" {
|
|
|
|
if err := os.MkdirAll(tmpdir, 0755); err != nil {
|
2020-08-23 09:53:01 +00:00
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
}
|
2020-10-23 11:18:45 +00:00
|
|
|
|
|
|
|
bufferFile, err := ioutil.TempFile(tmpdir, "tg-sync-sortable-buf")
|
2020-05-31 07:32:33 +00:00
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
2020-06-25 07:34:40 +00:00
|
|
|
defer bufferFile.Sync() //nolint:errcheck
|
2020-05-31 07:32:33 +00:00
|
|
|
|
2020-06-25 07:34:40 +00:00
|
|
|
w := bufio.NewWriterSize(bufferFile, BufIOSize)
|
2020-05-31 07:32:33 +00:00
|
|
|
defer w.Flush() //nolint:errcheck
|
2020-06-25 07:34:40 +00:00
|
|
|
|
2020-07-14 01:56:47 +00:00
|
|
|
defer func() {
|
|
|
|
b.Reset() // run it after buf.flush and file.sync
|
|
|
|
var m runtime.MemStats
|
|
|
|
runtime.ReadMemStats(&m)
|
|
|
|
log.Info(
|
|
|
|
"Flushed buffer file",
|
|
|
|
"name", bufferFile.Name(),
|
2021-07-03 07:44:23 +00:00
|
|
|
"alloc", common.StorageSize(m.Alloc), "sys", common.StorageSize(m.Sys))
|
2020-07-14 01:56:47 +00:00
|
|
|
}()
|
|
|
|
|
2020-06-10 20:07:14 +00:00
|
|
|
encoder.Reset(w)
|
|
|
|
for _, entry := range b.GetEntries() {
|
|
|
|
err = writeToDisk(encoder, entry.key, entry.value)
|
2020-05-31 07:32:33 +00:00
|
|
|
if err != nil {
|
|
|
|
return nil, fmt.Errorf("error writing entries to disk: %v", err)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return &fileDataProvider{bufferFile, nil}, nil
|
|
|
|
}
|
|
|
|
|
2020-06-10 20:07:14 +00:00
|
|
|
func (p *fileDataProvider) Next(decoder Decoder) ([]byte, []byte, error) {
|
2020-05-31 07:32:33 +00:00
|
|
|
if p.reader == nil {
|
|
|
|
_, err := p.file.Seek(0, 0)
|
|
|
|
if err != nil {
|
|
|
|
return nil, nil, err
|
|
|
|
}
|
2020-06-25 07:34:40 +00:00
|
|
|
p.reader = bufio.NewReaderSize(p.file, BufIOSize)
|
2020-05-31 07:32:33 +00:00
|
|
|
}
|
|
|
|
decoder.Reset(p.reader)
|
|
|
|
return readElementFromDisk(decoder)
|
|
|
|
}
|
|
|
|
|
2020-08-05 15:33:58 +00:00
|
|
|
func (p *fileDataProvider) Dispose() (uint64, error) {
|
|
|
|
info, errStat := os.Stat(p.file.Name())
|
2020-05-31 07:32:33 +00:00
|
|
|
errClose := p.file.Close()
|
|
|
|
errRemove := os.Remove(p.file.Name())
|
|
|
|
if errClose != nil {
|
2020-08-05 15:33:58 +00:00
|
|
|
return 0, errClose
|
2020-05-31 07:32:33 +00:00
|
|
|
}
|
|
|
|
if errRemove != nil {
|
2020-08-05 15:33:58 +00:00
|
|
|
return 0, errRemove
|
2020-05-31 07:32:33 +00:00
|
|
|
}
|
2020-08-05 15:33:58 +00:00
|
|
|
if errStat != nil {
|
|
|
|
return 0, errStat
|
|
|
|
}
|
|
|
|
return uint64(info.Size()), nil
|
2020-05-31 07:32:33 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
func (p *fileDataProvider) String() string {
|
|
|
|
return fmt.Sprintf("%T(file: %s)", p, p.file.Name())
|
|
|
|
}
|
|
|
|
|
2020-06-10 20:07:14 +00:00
|
|
|
func writeToDisk(encoder Encoder, key []byte, value []byte) error {
|
2020-05-31 07:32:33 +00:00
|
|
|
toWrite := [][]byte{key, value}
|
|
|
|
return encoder.Encode(toWrite)
|
|
|
|
}
|
|
|
|
|
|
|
|
func readElementFromDisk(decoder Decoder) ([]byte, []byte, error) {
|
|
|
|
result := make([][]byte, 2)
|
|
|
|
err := decoder.Decode(&result)
|
|
|
|
return result[0], result[1], err
|
|
|
|
}
|
|
|
|
|
|
|
|
type memoryDataProvider struct {
|
2020-06-10 20:07:14 +00:00
|
|
|
buffer Buffer
|
2020-05-31 07:32:33 +00:00
|
|
|
currentIndex int
|
|
|
|
}
|
|
|
|
|
2020-06-10 20:07:14 +00:00
|
|
|
func KeepInRAM(buffer Buffer) dataProvider {
|
2020-05-31 07:32:33 +00:00
|
|
|
return &memoryDataProvider{buffer, 0}
|
|
|
|
}
|
|
|
|
|
2020-06-10 20:07:14 +00:00
|
|
|
func (p *memoryDataProvider) Next(decoder Decoder) ([]byte, []byte, error) {
|
2020-05-31 07:32:33 +00:00
|
|
|
if p.currentIndex >= p.buffer.Len() {
|
|
|
|
return nil, nil, io.EOF
|
|
|
|
}
|
|
|
|
entry := p.buffer.Get(p.currentIndex)
|
|
|
|
p.currentIndex++
|
|
|
|
return entry.key, entry.value, nil
|
|
|
|
}
|
|
|
|
|
2020-08-05 15:33:58 +00:00
|
|
|
func (p *memoryDataProvider) Dispose() (uint64, error) {
|
|
|
|
return 0 /* doesn't take space on disk */, nil
|
2020-05-31 07:32:33 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
func (p *memoryDataProvider) String() string {
|
|
|
|
return fmt.Sprintf("%T(buffer.Len: %d)", p, p.buffer.Len())
|
|
|
|
}
|