Store copy actions and errors in DB.
This commit is contained in:
parent
950474fa7f
commit
d062632ec8
@ -41,9 +41,9 @@ expanded it by adding four more mirrors. ZFS doesn't automatically rebalance
|
|||||||
existing data, but does skew writes of new data so that more go to the newer
|
existing data, but does skew writes of new data so that more go to the newer
|
||||||
mirrors.
|
mirrors.
|
||||||
|
|
||||||
To rebalance the data manually, the algorithm is straightforward:
|
To rebalance the data, the algorithm is straightforward:
|
||||||
|
|
||||||
for file in dataset,
|
* for file in dataset,
|
||||||
* copy the file to a temporary directory in another dataset
|
* copy the file to a temporary directory in another dataset
|
||||||
* delete the original file
|
* delete the original file
|
||||||
* copy from the temporary directory to recreate the original file
|
* copy from the temporary directory to recreate the original file
|
||||||
|
60
datashake.go
60
datashake.go
@ -49,7 +49,7 @@ func main() {
|
|||||||
running.Store(true)
|
running.Store(true)
|
||||||
go func() {
|
go func() {
|
||||||
if err := filepath.WalkDir(*sourceDir, process); err != nil {
|
if err := filepath.WalkDir(*sourceDir, process); err != nil {
|
||||||
errors <- err
|
fmt.Println("error:", err)
|
||||||
}
|
}
|
||||||
pending.Wait()
|
pending.Wait()
|
||||||
close(errors)
|
close(errors)
|
||||||
@ -60,7 +60,7 @@ Loop:
|
|||||||
select {
|
select {
|
||||||
case err, ok := <-errors:
|
case err, ok := <-errors:
|
||||||
if ok {
|
if ok {
|
||||||
fmt.Println("error:", err)
|
db.Alert(err)
|
||||||
} else {
|
} else {
|
||||||
break Loop
|
break Loop
|
||||||
}
|
}
|
||||||
@ -80,7 +80,7 @@ var (
|
|||||||
|
|
||||||
tasks *pool.Pool
|
tasks *pool.Pool
|
||||||
pending sync.WaitGroup
|
pending sync.WaitGroup
|
||||||
errors = make(chan error)
|
errors = make(chan Error)
|
||||||
|
|
||||||
db = DB{
|
db = DB{
|
||||||
Processed: make(map[string]struct{}),
|
Processed: make(map[string]struct{}),
|
||||||
@ -113,14 +113,26 @@ func process(path string, d fs.DirEntry, err error) (typicallyNil error) {
|
|||||||
// work rebalances a single file.
|
// work rebalances a single file.
|
||||||
func work(path string, d fs.DirEntry) {
|
func work(path string, d fs.DirEntry) {
|
||||||
var err error
|
var err error
|
||||||
|
var srcFilePath = path
|
||||||
|
var tempFilePath string
|
||||||
|
|
||||||
|
reportErr := func(reported error) {
|
||||||
|
e := Error{
|
||||||
|
Message: reported.Error(),
|
||||||
|
FilePath: srcFilePath,
|
||||||
|
TempPath: tempFilePath,
|
||||||
|
}
|
||||||
|
errors <- e
|
||||||
|
}
|
||||||
|
|
||||||
defer func() {
|
defer func() {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
errors <- err
|
reportErr(err)
|
||||||
}
|
}
|
||||||
}()
|
}()
|
||||||
|
|
||||||
srcFileName := d.Name()
|
srcFileName := d.Name()
|
||||||
srcFilePath, err := filepath.Abs(path)
|
srcFilePath, err = filepath.Abs(path)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@ -139,7 +151,7 @@ func work(path string, d fs.DirEntry) {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
tempFilePath := filepath.Join(tempDirPath, srcFileName)
|
tempFilePath = filepath.Join(tempDirPath, srcFileName)
|
||||||
safeToRemoveTemp := true
|
safeToRemoveTemp := true
|
||||||
defer func() {
|
defer func() {
|
||||||
if !safeToRemoveTemp {
|
if !safeToRemoveTemp {
|
||||||
@ -147,11 +159,11 @@ func work(path string, d fs.DirEntry) {
|
|||||||
"%s may be lost in %s",
|
"%s may be lost in %s",
|
||||||
srcFilePath, tempDirPath,
|
srcFilePath, tempDirPath,
|
||||||
)
|
)
|
||||||
errors <- err
|
reportErr(err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
if err := os.RemoveAll(tempDirPath); err != nil {
|
if err := os.RemoveAll(tempDirPath); err != nil {
|
||||||
errors <- err
|
reportErr(err)
|
||||||
}
|
}
|
||||||
}()
|
}()
|
||||||
|
|
||||||
@ -213,7 +225,14 @@ func copy(srcPath, dstPath string) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
_, err = io.Copy(dstFile, srcFile)
|
_, err = io.Copy(dstFile, srcFile)
|
||||||
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
|
}
|
||||||
|
db.Record(Action{
|
||||||
|
Source: srcPath,
|
||||||
|
Destination: dstPath,
|
||||||
|
})
|
||||||
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// DB holds a set of files which have been rebalanced.
|
// DB holds a set of files which have been rebalanced.
|
||||||
@ -224,6 +243,19 @@ func copy(srcPath, dstPath string) error {
|
|||||||
// back to that JSON file as the program finishes.
|
// back to that JSON file as the program finishes.
|
||||||
type DB struct {
|
type DB struct {
|
||||||
Processed map[string]struct{}
|
Processed map[string]struct{}
|
||||||
|
Log []Action
|
||||||
|
Errors []Error
|
||||||
|
}
|
||||||
|
|
||||||
|
type Action struct {
|
||||||
|
Source string
|
||||||
|
Destination string
|
||||||
|
}
|
||||||
|
|
||||||
|
type Error struct {
|
||||||
|
Message string
|
||||||
|
FilePath string
|
||||||
|
TempPath string
|
||||||
}
|
}
|
||||||
|
|
||||||
func (db *DB) Contains(path string) bool {
|
func (db *DB) Contains(path string) bool {
|
||||||
@ -239,6 +271,18 @@ func (db *DB) Remember(path string) {
|
|||||||
db.Processed[path] = struct{}{}
|
db.Processed[path] = struct{}{}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (db *DB) Record(a Action) {
|
||||||
|
dbLock.Lock()
|
||||||
|
defer dbLock.Unlock()
|
||||||
|
db.Log = append(db.Log, a)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (db *DB) Alert(e Error) {
|
||||||
|
dbLock.Lock()
|
||||||
|
defer dbLock.Unlock()
|
||||||
|
db.Errors = append(db.Errors, e)
|
||||||
|
}
|
||||||
|
|
||||||
func loadDb() error {
|
func loadDb() error {
|
||||||
if *dbPath == "" {
|
if *dbPath == "" {
|
||||||
return nil
|
return nil
|
||||||
|
Loading…
Reference in New Issue
Block a user