zoho: use cursor listing for improved performance

Cursor listing enables us to list up to 1,000 items per call
(previously it was 10) and uses one less transaction per call.

See: https://forum.rclone.org/t/second-followup-on-the-older-topic-rclone-invokes-more-number-of-workdrive-s-files-listing-api-calls-which-exceeds-the-throttling-limit/45697/4
This commit is contained in:
Nick Craig-Wood 2024-06-11 19:26:38 +01:00
parent d068e0b1a9
commit 61c18e3b60
2 changed files with 23 additions and 6 deletions

View File

@ -70,8 +70,17 @@ type ItemInfo struct {
Item Item `json:"data"` Item Item `json:"data"`
} }
// Links contains Cursor information
type Links struct {
Cursor struct {
HasNext bool `json:"has_next"`
Next string `json:"next"`
} `json:"cursor"`
}
// ItemList contains multiple Zoho Items // ItemList contains multiple Zoho Items
type ItemList struct { type ItemList struct {
Links Links `json:"links"`
Items []Item `json:"data"` Items []Item `json:"data"`
} }

View File

@ -454,18 +454,18 @@ type listAllFn func(*api.Item) bool
// //
// If the user fn ever returns true then it early exits with found = true // If the user fn ever returns true then it early exits with found = true
func (f *Fs) listAll(ctx context.Context, dirID string, directoriesOnly bool, filesOnly bool, fn listAllFn) (found bool, err error) { func (f *Fs) listAll(ctx context.Context, dirID string, directoriesOnly bool, filesOnly bool, fn listAllFn) (found bool, err error) {
const listItemsLimit = 1000
opts := rest.Opts{ opts := rest.Opts{
Method: "GET", Method: "GET",
Path: "/files/" + dirID + "/files", Path: "/files/" + dirID + "/files",
ExtraHeaders: map[string]string{"Accept": "application/vnd.api+json"}, ExtraHeaders: map[string]string{"Accept": "application/vnd.api+json"},
Parameters: url.Values{}, Parameters: url.Values{
"page[limit]": {strconv.Itoa(listItemsLimit)},
"page[next]": {"0"},
},
} }
opts.Parameters.Set("page[limit]", strconv.Itoa(10))
offset := 0
OUTER: OUTER:
for { for {
opts.Parameters.Set("page[offset]", strconv.Itoa(offset))
var result api.ItemList var result api.ItemList
var resp *http.Response var resp *http.Response
err = f.pacer.Call(func() (bool, error) { err = f.pacer.Call(func() (bool, error) {
@ -495,7 +495,15 @@ OUTER:
break OUTER break OUTER
} }
} }
offset += 10 if !result.Links.Cursor.HasNext {
break
}
// Fetch the next from the URL in the response
nextURL, err := url.Parse(result.Links.Cursor.Next)
if err != nil {
return found, fmt.Errorf("failed to parse next link as URL: %w", err)
}
opts.Parameters.Set("page[next]", nextURL.Query().Get("page[next]"))
} }
return return
} }