s3tables: implement token-based pagination for namespace listing

This commit is contained in:
Chris Lu
2026-01-28 12:30:33 -08:00
parent 6ff683a627
commit babf1b06ac

View File

@@ -220,14 +220,14 @@ func (h *S3TablesHandler) handleListNamespaces(w http.ResponseWriter, r *http.Re
bucketPath := getTableBucketPath(bucketName) bucketPath := getTableBucketPath(bucketName)
var namespaces []NamespaceSummary var namespaces []NamespaceSummary
var lastFileName string lastFileName := req.ContinuationToken
err = filerClient.WithFilerClient(false, func(client filer_pb.SeaweedFilerClient) error { err = filerClient.WithFilerClient(false, func(client filer_pb.SeaweedFilerClient) error {
for len(namespaces) < maxNamespaces { for len(namespaces) < maxNamespaces {
resp, err := client.ListEntries(r.Context(), &filer_pb.ListEntriesRequest{ resp, err := client.ListEntries(r.Context(), &filer_pb.ListEntriesRequest{
Directory: bucketPath, Directory: bucketPath,
Limit: uint32(maxNamespaces * 2), Limit: uint32(maxNamespaces * 2),
StartFromFileName: lastFileName, StartFromFileName: lastFileName,
InclusiveStartFrom: lastFileName == "", InclusiveStartFrom: lastFileName == "" || lastFileName == req.ContinuationToken,
}) })
if err != nil { if err != nil {
return err return err
@@ -245,6 +245,12 @@ func (h *S3TablesHandler) handleListNamespaces(w http.ResponseWriter, r *http.Re
if entry.Entry == nil { if entry.Entry == nil {
continue continue
} }
// Skip the start item if it was included in the previous page
if len(namespaces) == 0 && req.ContinuationToken != "" && entry.Entry.Name == req.ContinuationToken {
continue
}
hasMore = true hasMore = true
lastFileName = entry.Entry.Name lastFileName = entry.Entry.Name
@@ -300,8 +306,14 @@ func (h *S3TablesHandler) handleListNamespaces(w http.ResponseWriter, r *http.Re
} }
} }
paginationToken := ""
if len(namespaces) >= maxNamespaces {
paginationToken = lastFileName
}
resp := &ListNamespacesResponse{ resp := &ListNamespacesResponse{
Namespaces: namespaces, Namespaces: namespaces,
ContinuationToken: paginationToken,
} }
h.writeJSON(w, http.StatusOK, resp) h.writeJSON(w, http.StatusOK, resp)