chrislu
2 years ago
3 changed files with 165 additions and 4 deletions
@ -0,0 +1,148 @@ |
|||
package command |
|||
|
|||
import ( |
|||
"github.com/seaweedfs/seaweedfs/weed/glog" |
|||
"github.com/seaweedfs/seaweedfs/weed/pb/filer_pb" |
|||
"github.com/seaweedfs/seaweedfs/weed/util" |
|||
"sync" |
|||
) |
|||
|
|||
type MetadataProcessFunc func(resp *filer_pb.SubscribeMetadataResponse) error |
|||
|
|||
type MetadataProcessor struct { |
|||
activeJobs map[int64]*filer_pb.SubscribeMetadataResponse |
|||
activeJobsLock sync.Mutex |
|||
activeJobsCond *sync.Cond |
|||
concurrencyLimit int |
|||
fn MetadataProcessFunc |
|||
processedTsWatermark int64 |
|||
} |
|||
|
|||
func NewMetadataProcessor(fn MetadataProcessFunc, concurrency int) *MetadataProcessor { |
|||
t := &MetadataProcessor{ |
|||
fn: fn, |
|||
activeJobs: make(map[int64]*filer_pb.SubscribeMetadataResponse), |
|||
concurrencyLimit: concurrency, |
|||
} |
|||
t.activeJobsCond = sync.NewCond(&t.activeJobsLock) |
|||
return t |
|||
} |
|||
|
|||
func (t *MetadataProcessor) AddSyncJob(resp *filer_pb.SubscribeMetadataResponse) { |
|||
if filer_pb.IsEmpty(resp) { |
|||
return |
|||
} |
|||
|
|||
t.activeJobsLock.Lock() |
|||
defer t.activeJobsLock.Unlock() |
|||
|
|||
for len(t.activeJobs) >= t.concurrencyLimit || t.conflictsWith(resp) { |
|||
t.activeJobsCond.Wait() |
|||
} |
|||
t.activeJobs[resp.TsNs] = resp |
|||
go func() { |
|||
|
|||
util.RetryForever("metadata processor", func() error { |
|||
return t.fn(resp) |
|||
}, func(err error) bool { |
|||
glog.Errorf("process %v: %v", resp, err) |
|||
return true |
|||
}) |
|||
|
|||
t.activeJobsLock.Lock() |
|||
defer t.activeJobsLock.Unlock() |
|||
|
|||
delete(t.activeJobs, resp.TsNs) |
|||
|
|||
// if is the oldest job, write down the watermark
|
|||
isOldest := true |
|||
for t, _ := range t.activeJobs { |
|||
if resp.TsNs > t { |
|||
isOldest = false |
|||
break |
|||
} |
|||
} |
|||
if isOldest { |
|||
t.processedTsWatermark = resp.TsNs |
|||
} |
|||
t.activeJobsCond.Signal() |
|||
}() |
|||
} |
|||
|
|||
func (t *MetadataProcessor) conflictsWith(resp *filer_pb.SubscribeMetadataResponse) bool { |
|||
for _, r := range t.activeJobs { |
|||
if shouldWaitFor(resp, r) { |
|||
return true |
|||
} |
|||
} |
|||
return false |
|||
} |
|||
|
|||
// a is one possible job to schedule
|
|||
// b is one existing active job
|
|||
func shouldWaitFor(a *filer_pb.SubscribeMetadataResponse, b *filer_pb.SubscribeMetadataResponse) bool { |
|||
aPath, aNewPath, aIsDirectory := extractPathsFromMetadata(a) |
|||
bPath, bNewPath, bIsDirectory := extractPathsFromMetadata(b) |
|||
|
|||
if pairShouldWaitFor(aPath, bPath, aIsDirectory, bIsDirectory) { |
|||
return true |
|||
} |
|||
if aNewPath != "" { |
|||
if pairShouldWaitFor(aNewPath, bPath, aIsDirectory, bIsDirectory) { |
|||
return true |
|||
} |
|||
} |
|||
if bNewPath != "" { |
|||
if pairShouldWaitFor(aPath, bNewPath, aIsDirectory, bIsDirectory) { |
|||
return true |
|||
} |
|||
} |
|||
if aNewPath != "" && bNewPath != "" { |
|||
if pairShouldWaitFor(aNewPath, bNewPath, aIsDirectory, bIsDirectory) { |
|||
return true |
|||
} |
|||
} |
|||
return false |
|||
} |
|||
|
|||
func pairShouldWaitFor(aPath, bPath util.FullPath, aIsDirectory, bIsDirectory bool) bool { |
|||
if bIsDirectory { |
|||
if aIsDirectory { |
|||
return aPath.IsUnder(bPath) || bPath.IsUnder(aPath) |
|||
} else { |
|||
return aPath.IsUnder(bPath) |
|||
} |
|||
} else { |
|||
if aIsDirectory { |
|||
return bPath.IsUnder(aPath) |
|||
} else { |
|||
return aPath == bPath |
|||
} |
|||
} |
|||
} |
|||
|
|||
func extractPathsFromMetadata(resp *filer_pb.SubscribeMetadataResponse) (path, newPath util.FullPath, isDirectory bool) { |
|||
oldEntry := resp.EventNotification.OldEntry |
|||
newEntry := resp.EventNotification.NewEntry |
|||
// create
|
|||
if filer_pb.IsCreate(resp) { |
|||
path = util.FullPath(resp.Directory).Child(newEntry.Name) |
|||
isDirectory = newEntry.IsDirectory |
|||
return |
|||
} |
|||
if filer_pb.IsDelete(resp) { |
|||
path = util.FullPath(resp.Directory).Child(oldEntry.Name) |
|||
isDirectory = oldEntry.IsDirectory |
|||
return |
|||
} |
|||
if filer_pb.IsUpdate(resp) { |
|||
path = util.FullPath(resp.Directory).Child(newEntry.Name) |
|||
isDirectory = newEntry.IsDirectory |
|||
return |
|||
} |
|||
// renaming
|
|||
path = util.FullPath(resp.Directory).Child(oldEntry.Name) |
|||
isDirectory = oldEntry.IsDirectory |
|||
newPath = util.FullPath(resp.EventNotification.NewParentPath).Child(newEntry.Name) |
|||
return |
|||
} |
Write
Preview
Loading…
Cancel
Save
Reference in new issue