Skip to content

Optimize Actors.IsActor check - #1116

Merged
bluekeyes merged 1 commit into
palantir:developfrom
mercari:upstream-optimize-actors-permissions
Nov 25, 2025
Merged

bluekeyes merged 1 commit into
palantir:developfrom
mercari:upstream-optimize-actors-permissions

Conversation

@south-mer

@south-mer south-mer commented Nov 25, 2025 •

Copy link
Copy Markdown
Contributor

Related to #1030.

Before this PR

In Actors.IsActor, GitHubContext.CollaboratorPermission is called unnecessarily, which consumes extra rate limits.

After this PR

In Actors.IsActor, GitHubContext.CollaboratorPermission is only called when needed.

As the table below shows, the behavior should be the same as before, except for the unnecessary API call eliminated where Permissions is not used.

userPerm != pull.PermissionNone userPerm == pull.PermissionNone
len(permissions) > 0 API called, check is performed returns false, nil
len(permissions) == 0 no API calls, no check performed, returns false, nil returns false, nil

Possible downsides?

Whether the call to CollaboratorPermission has any "side effects" needs to be carefully considered, especially in the case where the user does not have permission in the repository.

The actual implementation of CollaboratorPermission (not the test one) is here:

policy-bot/pull/github.go

Lines 648 to 698 in a4ac11a

func (ghc *GitHubContext) CollaboratorPermission(user string) (Permission, error) {
if ghc.permissions == nil {
ghc.permissions = make(map[string]Permission)
}
if p, ok := ghc.permissions[user]; ok {
return p, nil
}
// Use GraphQL because the v3 API to get collaborator permissions does not
// support maintain and triage permissions as of 2021-05-07.
var q struct {
Repository struct {
Collaborators struct {
PageInfo v4PageInfo
Edges []struct {
Permission string
}
Nodes []v4Actor
} `graphql:"collaborators(query: $user, first: 100, after: $cursor)"`
} `graphql:"repository(owner: $owner, name: $name)"`
}
qvars := map[string]interface{}{
"owner": githubv4.String(ghc.owner),
"name": githubv4.String(ghc.repo),
"user": githubv4.String(user),
"cursor": (*githubv4.String)(nil),
}
// The "query" argument does substring matching, so we might need to
// iterate through multiple users before we find the one we're looking for.
var perm Permission
for {
if err := ghc.v4client.Query(ghc.ctx, &q, qvars); err != nil {
return PermissionNone, errors.Wrap(err, "failed to get collaborator permission")
}
if idx := findUserIndex(user, q.Repository.Collaborators.Nodes); idx >= 0 {
p, err := ParsePermission(q.Repository.Collaborators.Edges[idx].Permission)
if err != nil {
return PermissionNone, err
}
perm = p
break
}
if !q.Repository.Collaborators.PageInfo.UpdateCursor(qvars, "cursor") {
break
}
}
ghc.permissions[user] = perm
return perm, nil
}

To breakdown the implementation:

  • L649-654 returns the cached result where it is already cached.
  • L656-674 builds the query and the variables.
  • L676-694 loops through all collaborators to check the user's permission. In particular,
    • (L680-682) It fetches a page of collaborators that match the name of the queried user.
      • When the collaborator does not exist in the repository, GitHub API returns empty result but does not err.
    • (L683-690) if the user exists as a collaborator, then the permission is parsed. This only errs if there is an invalid permission.
    • (L691-693) If there is no next page, break the loop.
  • L696 writes to the cache.
  • L697 returns the permission.

Therefore, we believe that removing the call to CollaboratorPermission when GetPermissions() is empty, should not have any downsides.

We would like the review to double check this assumption, in addition to all the usual reviews.

@changelog-app

changelog-app Bot commented Nov 25, 2025 •

Copy link
Copy Markdown

Generate changelog in changelog/@unreleased

Type (Select exactly one)

  • Feature (Adding new functionality)
  • Improvement (Improving existing functionality)
  • Fix (Fixing an issue with existing functionality)
  • Break (Creating a new major version by breaking public APIs)
  • Deprecation (Removing functionality in a non-breaking way)
  • Migration (Automatically moving data/functionality to a new system)

Description

Reduce GitHub API queries when evaluating rules that do not use collaborator permissions

Check the box to generate changelog(s)

  • Generate changelog entry

@changelog-app

changelog-app Bot commented Nov 25, 2025 •

Copy link
Copy Markdown

✅ Successfully generated changelog entry!

Need to regenerate?

Simply interact with the changelog bot comment again to regenerate these entries.

🔄 Changelog entries were re-generated at Tue, 25 Nov 2025 17:56:06 UTC!


📋Changelog Preview

💡 Improvements

  • Reduce GitHub API queries when evaluating rules that do not use collaborator permissions (#1116)

@bluekeyes bluekeyes left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks! I agree that CollaboratorPermission does not have any side effects that are relevant to the behavior here.

@bluekeyes
bluekeyes merged commit fdb8cf3 into palantir:develop Nov 25, 2025
9 checks passed
@south-mer
south-mer deleted the upstream-optimize-actors-permissions branch January 7, 2026 10:17
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants