Welcome to mirror list, hosted at ThFree Co, Russian Federation.

gitlab.com/gitlab-org/gitaly.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorVasilii Iakliushin <viakliushin@gitlab.com>2022-09-15 15:28:25 +0300
committerVasilii Iakliushin <viakliushin@gitlab.com>2022-09-15 15:34:00 +0300
commiteb82c328b2a3ca312724724eb9849bc25a3b0b24 (patch)
treed47014217cf46f333417b9003eeb5542afc7993e
parent82ad225ea207e669379c39d804271e19b0f88c67 (diff)
Fix pagination cursor generation for TreeEntries
Contributes to https://gitlab.com/gitlab-org/gitlab/-/issues/351443 **Problem** We return the same pagination cursor when users fetch repository tree. The pagination cursor is based on blobs ids. However, empty blobs have the same id (because it's calculated based on the content of the blob). git ls-tree -r -t HEAD ``` 100644 blob e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 funzone/top2_11.md 100644 blob e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 funzone/top2_12.md ``` As a result, pagination doesn't work as expected. **Solution** Use a different algorithm to generate the cursor token. Instead of blob id, we can use a hashed representation of the file name. It should be unique in the scope of the directory. Changelog: changed
-rw-r--r--internal/gitaly/service/commit/tree_entries.go15
-rw-r--r--internal/gitaly/service/commit/tree_entries_test.go26
2 files changed, 33 insertions, 8 deletions
diff --git a/internal/gitaly/service/commit/tree_entries.go b/internal/gitaly/service/commit/tree_entries.go
index 2a2489526..bae63d3d6 100644
--- a/internal/gitaly/service/commit/tree_entries.go
+++ b/internal/gitaly/service/commit/tree_entries.go
@@ -2,6 +2,8 @@ package commit
import (
"context"
+ "crypto/sha1"
+ "encoding/hex"
"errors"
"fmt"
"sort"
@@ -310,7 +312,7 @@ func paginateTreeEntries(entries []*gitalypb.TreeEntry, p *gitalypb.PaginationPa
index = 0
} else {
for i, entry := range entries {
- if entry.GetOid() == start {
+ if generateCursor(entry) == start {
index = i + 1
break
}
@@ -330,5 +332,14 @@ func paginateTreeEntries(entries []*gitalypb.TreeEntry, p *gitalypb.PaginationPa
}
paginated := entries[index : index+limit]
- return paginated, paginated[len(paginated)-1].GetOid(), nil
+ return paginated, generateCursor(paginated[len(paginated)-1]), nil
+}
+
+// Oid is not a unique value and cannot be used for the cursor generation.
+// Instead we use the file name that should be unique
+func generateCursor(entry *gitalypb.TreeEntry) string {
+ h := sha1.New()
+ h.Write(entry.GetPath())
+
+ return hex.EncodeToString(h.Sum(nil))
}
diff --git a/internal/gitaly/service/commit/tree_entries_test.go b/internal/gitaly/service/commit/tree_entries_test.go
index e763619a4..eb71853e1 100644
--- a/internal/gitaly/service/commit/tree_entries_test.go
+++ b/internal/gitaly/service/commit/tree_entries_test.go
@@ -3,6 +3,8 @@
package commit
import (
+ "crypto/sha1"
+ "encoding/hex"
"errors"
"io"
"strconv"
@@ -352,6 +354,7 @@ func TestGetTreeEntries_successful(t *testing.T) {
pageToken string
pageLimit int32
cursor string
+ entryPath []byte
skipFlatPaths bool
}{
{
@@ -433,23 +436,25 @@ func TestGetTreeEntries_successful(t *testing.T) {
entries: sortedAndPaginated,
pageLimit: 4,
sortBy: gitalypb.GetTreeEntriesRequest_TREES_FIRST,
- cursor: "fd90a3d2d21d6b4f9bec2c33fb7f49780c55f0d2",
+ cursor: "1c497fbb3a46b78edf04cc2a2fa33f67e3ffbe2a",
+ entryPath: sortedAndPaginated[3].Path,
},
{
description: "with pagination parameters",
revision: []byte(commitID),
path: []byte("."),
entries: rootEntries[3:6],
- pageToken: "fdaada1754989978413d618ee1fb1c0469d6a664",
+ pageToken: "7445606fbf8f3683cd42bdc54b05d7a0bc2dfc44",
pageLimit: 3,
cursor: rootEntries[5].Oid,
+ entryPath: rootEntries[5].Path,
},
{
description: "with pagination parameters larger than length",
revision: []byte(commitID),
path: []byte("."),
entries: rootEntries[12:],
- pageToken: "b4a3321157f6e80c42b031ecc9ba79f784c8a557",
+ pageToken: "a1f13b3bc20a296e08c212be9c56c706c10abc4f",
pageLimit: 20,
},
{
@@ -457,14 +462,14 @@ func TestGetTreeEntries_successful(t *testing.T) {
revision: []byte(commitID),
path: []byte("."),
entries: rootEntries[2:],
- pageToken: "470ad2fcf1e33798f1afc5781d08e60c40f51e7a",
+ pageToken: "a5cc2925ca8258af241be7e5b0381edf30266302",
pageLimit: -1,
},
{
description: "with pagination limit of 0",
revision: []byte(commitID),
path: []byte("."),
- pageToken: "470ad2fcf1e33798f1afc5781d08e60c40f51e7a",
+ pageToken: "a5cc2925ca8258af241be7e5b0381edf30266302",
pageLimit: 0,
},
{
@@ -475,6 +480,7 @@ func TestGetTreeEntries_successful(t *testing.T) {
entries: rootEntries[0:2],
pageLimit: 2,
cursor: rootEntries[1].Oid,
+ entryPath: rootEntries[1].Path,
},
}
@@ -504,7 +510,15 @@ func TestGetTreeEntries_successful(t *testing.T) {
if testCase.pageLimit > 0 && len(testCase.entries) < len(rootEntries) {
require.NotNil(t, cursor)
- require.Equal(t, testCase.cursor, cursor.NextCursor)
+
+ expectedCursor := ""
+
+ if testCase.cursor != "" {
+ shaHash := sha1.Sum(testCase.entryPath)
+ expectedCursor = hex.EncodeToString(shaHash[:])
+ }
+
+ require.Equal(t, expectedCursor, cursor.NextCursor)
}
})
}