fs: add ChangeNotify and backend support for it (#2094)
* fs: rename DirChangeNotify to ChangeNotify * cache: switch to ChangeNotify * ChangeNotify: keep order of notifications
This commit is contained in:
parent
b3f55d6bda
commit
70f07fd3ac
35 changed files with 447 additions and 230 deletions
|
@ -16,7 +16,6 @@ import (
|
|||
"net/url"
|
||||
"os"
|
||||
"path"
|
||||
"sort"
|
||||
"strconv"
|
||||
"strings"
|
||||
"sync"
|
||||
|
@ -1182,14 +1181,13 @@ func (f *Fs) DirMove(src fs.Fs, srcRemote, dstRemote string) error {
|
|||
return nil
|
||||
}
|
||||
|
||||
// DirChangeNotify polls for changes from the remote and hands the path to the
|
||||
// given function. Only changes that can be resolved to a path through the
|
||||
// DirCache will handled.
|
||||
// ChangeNotify calls the passed function with a path that has had changes.
|
||||
// If the implementation uses polling, it should adhere to the given interval.
|
||||
//
|
||||
// Automatically restarts itself in case of unexpected behaviour of the remote.
|
||||
//
|
||||
// Close the returned channel to stop being notified.
|
||||
func (f *Fs) DirChangeNotify(notifyFunc func(string), pollInterval time.Duration) chan bool {
|
||||
func (f *Fs) ChangeNotify(notifyFunc func(string, fs.EntryType), pollInterval time.Duration) chan bool {
|
||||
quit := make(chan bool)
|
||||
go func() {
|
||||
select {
|
||||
|
@ -1197,7 +1195,7 @@ func (f *Fs) DirChangeNotify(notifyFunc func(string), pollInterval time.Duration
|
|||
return
|
||||
default:
|
||||
for {
|
||||
f.dirchangeNotifyRunner(notifyFunc, pollInterval)
|
||||
f.changeNotifyRunner(notifyFunc, pollInterval)
|
||||
fs.Debugf(f, "Notify listener service ran into issues, restarting shortly.")
|
||||
time.Sleep(pollInterval)
|
||||
}
|
||||
|
@ -1206,11 +1204,8 @@ func (f *Fs) DirChangeNotify(notifyFunc func(string), pollInterval time.Duration
|
|||
return quit
|
||||
}
|
||||
|
||||
func (f *Fs) dirchangeNotifyRunner(notifyFunc func(string), pollInterval time.Duration) {
|
||||
func (f *Fs) changeNotifyRunner(notifyFunc func(string, fs.EntryType), pollInterval time.Duration) {
|
||||
var err error
|
||||
var changeList *drive.ChangeList
|
||||
var pageToken string
|
||||
|
||||
var startPageToken *drive.StartPageToken
|
||||
err = f.pacer.Call(func() (bool, error) {
|
||||
startPageToken, err = f.svc.Changes.GetStartPageToken().SupportsTeamDrives(f.isTeamDrive).Do()
|
||||
|
@ -1220,12 +1215,14 @@ func (f *Fs) dirchangeNotifyRunner(notifyFunc func(string), pollInterval time.Du
|
|||
fs.Debugf(f, "Failed to get StartPageToken: %v", err)
|
||||
return
|
||||
}
|
||||
pageToken = startPageToken.StartPageToken
|
||||
pageToken := startPageToken.StartPageToken
|
||||
|
||||
for {
|
||||
fs.Debugf(f, "Checking for changes on remote")
|
||||
var changeList *drive.ChangeList
|
||||
|
||||
err = f.pacer.Call(func() (bool, error) {
|
||||
changesCall := f.svc.Changes.List(pageToken).Fields("nextPageToken,newStartPageToken,changes(fileId,file/parents)")
|
||||
changesCall := f.svc.Changes.List(pageToken).Fields("nextPageToken,newStartPageToken,changes(fileId,file(name,parents,mimeType))")
|
||||
if *driveListChunk > 0 {
|
||||
changesCall = changesCall.PageSize(*driveListChunk)
|
||||
}
|
||||
|
@ -1237,28 +1234,47 @@ func (f *Fs) dirchangeNotifyRunner(notifyFunc func(string), pollInterval time.Du
|
|||
return
|
||||
}
|
||||
|
||||
pathsToClear := make([]string, 0)
|
||||
type entryType struct {
|
||||
path string
|
||||
entryType fs.EntryType
|
||||
}
|
||||
var pathsToClear []entryType
|
||||
for _, change := range changeList.Changes {
|
||||
if path, ok := f.dirCache.GetInv(change.FileId); ok {
|
||||
pathsToClear = append(pathsToClear, path)
|
||||
if change.File != nil && change.File.MimeType != driveFolderType {
|
||||
pathsToClear = append(pathsToClear, entryType{path: path, entryType: fs.EntryObject})
|
||||
} else {
|
||||
pathsToClear = append(pathsToClear, entryType{path: path, entryType: fs.EntryDirectory})
|
||||
}
|
||||
continue
|
||||
}
|
||||
|
||||
if change.File != nil {
|
||||
for _, parent := range change.File.Parents {
|
||||
if path, ok := f.dirCache.GetInv(parent); ok {
|
||||
pathsToClear = append(pathsToClear, path)
|
||||
if change.File != nil && change.File.MimeType != driveFolderType {
|
||||
// translate the parent dir of this object
|
||||
if len(change.File.Parents) > 0 {
|
||||
if path, ok := f.dirCache.GetInv(change.File.Parents[0]); ok {
|
||||
// and append the drive file name to compute the full file name
|
||||
if len(path) > 0 {
|
||||
path = path + "/" + change.File.Name
|
||||
} else {
|
||||
path = change.File.Name
|
||||
}
|
||||
// this will now clear the actual file too
|
||||
pathsToClear = append(pathsToClear, entryType{path: path, entryType: fs.EntryObject})
|
||||
}
|
||||
} else { // a true root object that is changed
|
||||
pathsToClear = append(pathsToClear, entryType{path: change.File.Name, entryType: fs.EntryObject})
|
||||
}
|
||||
}
|
||||
}
|
||||
lastNotifiedPath := ""
|
||||
sort.Strings(pathsToClear)
|
||||
for _, path := range pathsToClear {
|
||||
if lastNotifiedPath != "" && (path == lastNotifiedPath || strings.HasPrefix(path+"/", lastNotifiedPath)) {
|
||||
|
||||
visitedPaths := make(map[string]bool)
|
||||
for _, entry := range pathsToClear {
|
||||
if _, ok := visitedPaths[entry.path]; ok {
|
||||
continue
|
||||
}
|
||||
lastNotifiedPath = path
|
||||
notifyFunc(path)
|
||||
visitedPaths[entry.path] = true
|
||||
notifyFunc(entry.path, entry.entryType)
|
||||
}
|
||||
|
||||
if changeList.NewStartPageToken != "" {
|
||||
|
@ -1567,17 +1583,17 @@ func (o *Object) MimeType() string {
|
|||
|
||||
// Check the interfaces are satisfied
|
||||
var (
|
||||
_ fs.Fs = (*Fs)(nil)
|
||||
_ fs.Purger = (*Fs)(nil)
|
||||
_ fs.CleanUpper = (*Fs)(nil)
|
||||
_ fs.PutStreamer = (*Fs)(nil)
|
||||
_ fs.Copier = (*Fs)(nil)
|
||||
_ fs.Mover = (*Fs)(nil)
|
||||
_ fs.DirMover = (*Fs)(nil)
|
||||
_ fs.DirCacheFlusher = (*Fs)(nil)
|
||||
_ fs.DirChangeNotifier = (*Fs)(nil)
|
||||
_ fs.PutUncheckeder = (*Fs)(nil)
|
||||
_ fs.MergeDirser = (*Fs)(nil)
|
||||
_ fs.Object = (*Object)(nil)
|
||||
_ fs.MimeTyper = &Object{}
|
||||
_ fs.Fs = (*Fs)(nil)
|
||||
_ fs.Purger = (*Fs)(nil)
|
||||
_ fs.CleanUpper = (*Fs)(nil)
|
||||
_ fs.PutStreamer = (*Fs)(nil)
|
||||
_ fs.Copier = (*Fs)(nil)
|
||||
_ fs.Mover = (*Fs)(nil)
|
||||
_ fs.DirMover = (*Fs)(nil)
|
||||
_ fs.DirCacheFlusher = (*Fs)(nil)
|
||||
_ fs.ChangeNotifier = (*Fs)(nil)
|
||||
_ fs.PutUncheckeder = (*Fs)(nil)
|
||||
_ fs.MergeDirser = (*Fs)(nil)
|
||||
_ fs.Object = (*Object)(nil)
|
||||
_ fs.MimeTyper = &Object{}
|
||||
)
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue