Practical Sync Examples
File Synchronization Scenarios
graph TD
A[Sync Scenarios] --> B[Backup System]
A --> C[Distributed File Storage]
A --> D[Collaborative Editing]
A --> E[Log File Management]
1. Concurrent File Backup System
package filesync
import (
"os"
"path/filepath"
"sync"
)
type FileBackup struct {
mu sync.Mutex
srcDir string
destDir string
errors []error
}
func (fb *FileBackup) BackupFiles() error {
var wg sync.WaitGroup
err := filepath.Walk(fb.srcDir, func(path string, info os.FileInfo, err error) error {
if err != nil {
return err
}
if !info.IsDir() {
wg.Add(1)
go func(filePath string) {
defer wg.Done()
fb.syncFile(filePath)
}(path)
}
return nil
})
wg.Wait()
return err
}
func (fb *FileBackup) syncFile(sourcePath string) {
fb.mu.Lock()
defer fb.mu.Unlock()
relativePath, _ := filepath.Rel(fb.srcDir, sourcePath)
destPath := filepath.Join(fb.destDir, relativePath)
// Ensure destination directory exists
os.MkdirAll(filepath.Dir(destPath), 0755)
// Copy file with error handling
sourceFile, err := os.Open(sourcePath)
if err != nil {
fb.errors = append(fb.errors, err)
return
}
defer sourceFile.Close()
destFile, err := os.Create(destPath)
if err != nil {
fb.errors = append(fb.errors, err)
return
}
defer destFile.Close()
_, err = io.Copy(destFile, sourceFile)
if err != nil {
fb.errors = append(fb.errors, err)
}
}
2. Distributed File Synchronization
type DistributedFileSync struct {
nodes []string
syncMutex sync.RWMutex
fileVersion map[string]int64
}
func (dfs *DistributedFileSync) SyncFile(filename string, content []byte) error {
dfs.syncMutex.Lock()
defer dfs.syncMutex.Unlock()
currentVersion := dfs.fileVersion[filename]
newVersion := currentVersion + 1
// Sync to multiple nodes
var wg sync.WaitGroup
var syncErrors []error
for _, node := range dfs.nodes {
wg.Add(1)
go func(nodeAddr string) {
defer wg.Done()
err := dfs.sendToNode(nodeAddr, filename, content, newVersion)
if err != nil {
syncErrors = append(syncErrors, err)
}
}(node)
}
wg.Wait()
// Update version if all nodes synced successfully
if len(syncErrors) == 0 {
dfs.fileVersion[filename] = newVersion
}
return nil
}
Synchronization Strategies
Strategy |
Use Case |
Pros |
Cons |
Mutex-based Sync |
Simple file operations |
Easy to implement |
Can create bottlenecks |
Channel-based Sync |
Complex concurrent operations |
More flexible |
Higher complexity |
Atomic Sync |
Simple counter/flag operations |
High performance |
Limited to simple operations |
3. Log File Rotation with Sync
type LogRotator struct {
currentFile *os.File
mu sync.Mutex
maxSize int64
}
func (lr *LogRotator) WriteLog(message string) error {
lr.mu.Lock()
defer lr.mu.Unlock()
// Check file size and rotate if necessary
fileInfo, err := lr.currentFile.Stat()
if err != nil {
return err
}
if fileInfo.Size() > lr.maxSize {
lr.rotateLogFile()
}
_, err = lr.currentFile.WriteString(message + "\n")
return err
}
LabEx Synchronization Best Practices
- Use appropriate sync primitives
- Minimize lock contention
- Handle errors gracefully
- Consider performance implications
Conclusion
Practical file synchronization in Go requires a deep understanding of concurrency patterns, careful error handling, and strategic use of synchronization mechanisms.