mirror of
https://github.com/gilbertchen/duplicacy
synced 2025-12-17 16:53:19 +00:00
Improvements for the copy command
* Added a `-download-threads` option for specifying the number of downloading threads * Show progress log messages during copy
This commit is contained in:
@@ -1171,9 +1171,14 @@ func copySnapshots(context *cli.Context) {
|
|||||||
os.Exit(ArgumentExitCode)
|
os.Exit(ArgumentExitCode)
|
||||||
}
|
}
|
||||||
|
|
||||||
threads := context.Int("threads")
|
uploadingThreads := context.Int("threads")
|
||||||
if threads < 1 {
|
if uploadingThreads < 1 {
|
||||||
threads = 1
|
uploadingThreads = 1
|
||||||
|
}
|
||||||
|
|
||||||
|
downloadingThreads := context.Int("download-threads")
|
||||||
|
if downloadingThreads < 1 {
|
||||||
|
downloadingThreads = 1
|
||||||
}
|
}
|
||||||
|
|
||||||
repository, source := getRepositoryPreference(context, context.String("from"))
|
repository, source := getRepositoryPreference(context, context.String("from"))
|
||||||
@@ -1181,7 +1186,7 @@ func copySnapshots(context *cli.Context) {
|
|||||||
runScript(context, source.Name, "pre")
|
runScript(context, source.Name, "pre")
|
||||||
|
|
||||||
duplicacy.LOG_INFO("STORAGE_SET", "Source storage set to %s", source.StorageURL)
|
duplicacy.LOG_INFO("STORAGE_SET", "Source storage set to %s", source.StorageURL)
|
||||||
sourceStorage := duplicacy.CreateStorage(*source, false, threads)
|
sourceStorage := duplicacy.CreateStorage(*source, false, downloadingThreads)
|
||||||
if sourceStorage == nil {
|
if sourceStorage == nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@@ -1211,7 +1216,7 @@ func copySnapshots(context *cli.Context) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
duplicacy.LOG_INFO("STORAGE_SET", "Destination storage set to %s", destination.StorageURL)
|
duplicacy.LOG_INFO("STORAGE_SET", "Destination storage set to %s", destination.StorageURL)
|
||||||
destinationStorage := duplicacy.CreateStorage(*destination, false, threads)
|
destinationStorage := duplicacy.CreateStorage(*destination, false, uploadingThreads)
|
||||||
if destinationStorage == nil {
|
if destinationStorage == nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@@ -1236,7 +1241,7 @@ func copySnapshots(context *cli.Context) {
|
|||||||
snapshotID = context.String("id")
|
snapshotID = context.String("id")
|
||||||
}
|
}
|
||||||
|
|
||||||
sourceManager.CopySnapshots(destinationManager, snapshotID, revisions, threads)
|
sourceManager.CopySnapshots(destinationManager, snapshotID, revisions, uploadingThreads, downloadingThreads)
|
||||||
runScript(context, source.Name, "post")
|
runScript(context, source.Name, "post")
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1999,6 +2004,12 @@ func main() {
|
|||||||
Usage: "number of uploading threads",
|
Usage: "number of uploading threads",
|
||||||
Argument: "<n>",
|
Argument: "<n>",
|
||||||
},
|
},
|
||||||
|
cli.IntFlag{
|
||||||
|
Name: "download-threads",
|
||||||
|
Value: 1,
|
||||||
|
Usage: "number of downloading threads",
|
||||||
|
Argument: "<n>",
|
||||||
|
},
|
||||||
cli.StringFlag{
|
cli.StringFlag{
|
||||||
Name: "key",
|
Name: "key",
|
||||||
Usage: "the RSA private key to decrypt file chunks from the source storage",
|
Usage: "the RSA private key to decrypt file chunks from the source storage",
|
||||||
|
|||||||
@@ -1600,7 +1600,7 @@ func (manager *BackupManager) RestoreFile(chunkDownloader *ChunkDownloader, chun
|
|||||||
|
|
||||||
// CopySnapshots copies the specified snapshots from one storage to the other.
|
// CopySnapshots copies the specified snapshots from one storage to the other.
|
||||||
func (manager *BackupManager) CopySnapshots(otherManager *BackupManager, snapshotID string,
|
func (manager *BackupManager) CopySnapshots(otherManager *BackupManager, snapshotID string,
|
||||||
revisionsToBeCopied []int, threads int) bool {
|
revisionsToBeCopied []int, uploadingThreads int, downloadingThreads int) bool {
|
||||||
|
|
||||||
if !manager.config.IsCompatiableWith(otherManager.config) {
|
if !manager.config.IsCompatiableWith(otherManager.config) {
|
||||||
LOG_ERROR("CONFIG_INCOMPATIBLE", "Two storages are not compatible for the copy operation")
|
LOG_ERROR("CONFIG_INCOMPATIBLE", "Two storages are not compatible for the copy operation")
|
||||||
@@ -1745,63 +1745,64 @@ func (manager *BackupManager) CopySnapshots(otherManager *BackupManager, snapsho
|
|||||||
|
|
||||||
LOG_DEBUG("SNAPSHOT_COPY", "Found %d chunks on destination storage", len(otherChunks))
|
LOG_DEBUG("SNAPSHOT_COPY", "Found %d chunks on destination storage", len(otherChunks))
|
||||||
|
|
||||||
chunksToCopy := 0
|
var chunksToCopy []string
|
||||||
chunksToSkip := 0
|
|
||||||
|
|
||||||
for chunkHash := range chunks {
|
for chunkHash := range chunks {
|
||||||
otherChunkID := otherManager.config.GetChunkIDFromHash(chunkHash)
|
otherChunkID := otherManager.config.GetChunkIDFromHash(chunkHash)
|
||||||
if _, found := otherChunks[otherChunkID]; found {
|
if _, found := otherChunks[otherChunkID]; !found {
|
||||||
chunksToSkip++
|
chunksToCopy = append(chunksToCopy, chunkHash)
|
||||||
} else {
|
|
||||||
chunksToCopy++
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
LOG_DEBUG("SNAPSHOT_COPY", "Chunks to copy = %d, to skip = %d, total = %d", chunksToCopy, chunksToSkip, chunksToCopy+chunksToSkip)
|
LOG_INFO("SNAPSHOT_COPY", "Chunks to copy: %d, to skip: %d, total: %d", len(chunksToCopy), len(chunks) - len(chunksToCopy), len(chunks))
|
||||||
LOG_DEBUG("SNAPSHOT_COPY", "Total chunks in source snapshot revisions = %d\n", len(chunks))
|
|
||||||
|
|
||||||
chunkDownloader := CreateChunkDownloader(manager.config, manager.storage, nil, false, threads, false)
|
chunkDownloader := CreateChunkDownloader(manager.config, manager.storage, nil, false, downloadingThreads, false)
|
||||||
|
|
||||||
chunkUploader := CreateChunkUploader(otherManager.config, otherManager.storage, nil, threads,
|
var uploadedBytes int64
|
||||||
|
startTime := time.Now()
|
||||||
|
|
||||||
|
copiedChunks := 0
|
||||||
|
chunkUploader := CreateChunkUploader(otherManager.config, otherManager.storage, nil, uploadingThreads,
|
||||||
func(chunk *Chunk, chunkIndex int, skipped bool, chunkSize int, uploadSize int) {
|
func(chunk *Chunk, chunkIndex int, skipped bool, chunkSize int, uploadSize int) {
|
||||||
if skipped {
|
action := "Skipped"
|
||||||
LOG_INFO("SNAPSHOT_COPY", "Chunk %s (%d/%d) exists at the destination", chunk.GetID(), chunkIndex, len(chunks))
|
if !skipped {
|
||||||
} else {
|
copiedChunks++
|
||||||
LOG_INFO("SNAPSHOT_COPY", "Chunk %s (%d/%d) copied to the destination", chunk.GetID(), chunkIndex, len(chunks))
|
action = "Copied"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
atomic.AddInt64(&uploadedBytes, int64(chunkSize))
|
||||||
|
|
||||||
|
elapsedTime := time.Now().Sub(startTime).Seconds()
|
||||||
|
speed := int64(float64(atomic.LoadInt64(&uploadedBytes)) / elapsedTime)
|
||||||
|
remainingTime := int64(float64(len(chunksToCopy) - chunkIndex - 1) / float64(chunkIndex + 1) * elapsedTime)
|
||||||
|
percentage := float64(chunkIndex + 1) / float64(len(chunksToCopy)) * 100.0
|
||||||
|
LOG_INFO("COPY_PROGRESS", "%s chunk %s (%d/%d) %sB/s %s %.1f%%",
|
||||||
|
action, chunk.GetID(), chunkIndex + 1, len(chunksToCopy),
|
||||||
|
PrettySize(speed), PrettyTime(remainingTime), percentage)
|
||||||
otherManager.config.PutChunk(chunk)
|
otherManager.config.PutChunk(chunk)
|
||||||
})
|
})
|
||||||
|
|
||||||
chunkUploader.Start()
|
chunkUploader.Start()
|
||||||
|
|
||||||
totalCopied := 0
|
for _, chunkHash := range chunksToCopy {
|
||||||
totalSkipped := 0
|
chunkDownloader.AddChunk(chunkHash)
|
||||||
chunkIndex := 0
|
}
|
||||||
|
for i, chunkHash := range chunksToCopy {
|
||||||
for chunkHash, isSnapshot := range chunks {
|
|
||||||
chunkIndex++
|
|
||||||
chunkID := manager.config.GetChunkIDFromHash(chunkHash)
|
chunkID := manager.config.GetChunkIDFromHash(chunkHash)
|
||||||
newChunkID := otherManager.config.GetChunkIDFromHash(chunkHash)
|
newChunkID := otherManager.config.GetChunkIDFromHash(chunkHash)
|
||||||
if _, found := otherChunks[newChunkID]; !found {
|
LOG_DEBUG("SNAPSHOT_COPY", "Copying chunk %s to %s", chunkID, newChunkID)
|
||||||
LOG_DEBUG("SNAPSHOT_COPY", "Copying chunk %s to %s", chunkID, newChunkID)
|
chunk := chunkDownloader.WaitForChunk(i)
|
||||||
i := chunkDownloader.AddChunk(chunkHash)
|
newChunk := otherManager.config.GetChunk()
|
||||||
chunk := chunkDownloader.WaitForChunk(i)
|
newChunk.Reset(true)
|
||||||
newChunk := otherManager.config.GetChunk()
|
newChunk.Write(chunk.GetBytes())
|
||||||
newChunk.Reset(true)
|
newChunk.isSnapshot = chunks[chunkHash]
|
||||||
newChunk.Write(chunk.GetBytes())
|
chunkUploader.StartChunk(newChunk, i)
|
||||||
newChunk.isSnapshot = isSnapshot
|
|
||||||
chunkUploader.StartChunk(newChunk, chunkIndex)
|
|
||||||
totalCopied++
|
|
||||||
} else {
|
|
||||||
LOG_INFO("SNAPSHOT_COPY", "Chunk %s (%d/%d) skipped at the destination", chunkID, chunkIndex, len(chunks))
|
|
||||||
totalSkipped++
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
chunkDownloader.Stop()
|
chunkDownloader.Stop()
|
||||||
chunkUploader.Stop()
|
chunkUploader.Stop()
|
||||||
|
|
||||||
LOG_INFO("SNAPSHOT_COPY", "Copy complete, %d total chunks, %d chunks copied, %d skipped", totalCopied+totalSkipped, totalCopied, totalSkipped)
|
LOG_INFO("SNAPSHOT_COPY", "Copied %d new chunks and skipped %d existing chunks", copiedChunks, len(chunks) - copiedChunks)
|
||||||
|
|
||||||
for _, snapshot := range snapshots {
|
for _, snapshot := range snapshots {
|
||||||
if revisionMap[snapshot.ID][snapshot.Revision] == false {
|
if revisionMap[snapshot.ID][snapshot.Revision] == false {
|
||||||
|
|||||||
Reference in New Issue
Block a user