perforce: support internal rate limiter (#19751)

Co-authored-by: Indradhanush Gupta <indradhanush.gupta@gmail.com>
Co-authored-by: Ryan Slade <ryanslade@gmail.com>
This commit is contained in:
ᴜɴᴋɴᴡᴏɴ 2021-04-08 01:02:07 +08:00 committed by GitHub
parent a0b368222b
commit c925c91e1f
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 49 additions and 1 deletions

View File

@ -380,6 +380,13 @@ func (e *ExternalServiceStore) validatePerforceConnection(ctx context.Context, i
for _, validate := range e.PerforceValidators {
err = multierror.Append(err, validate(c))
}
if c.Depots == nil {
err = multierror.Append(err, errors.New("depots must be set"))
}
err = multierror.Append(err, e.validateDuplicateRateLimits(ctx, id, extsvc.KindPerforce, c))
return err.ErrorOrNil()
}

View File

@ -1812,7 +1812,7 @@ func TestRepos_ListRepoNames_externalRepoPrefixes(t *testing.T) {
svc := &types.ExternalService{
Kind: extsvc.KindPerforce,
DisplayName: "Perforce - Test",
Config: `{"p4.port": "ssl:111.222.333.444:1666", "p4.user": "admin", "p4.passwd": "pa$$word", "repositoryPathPattern": "perforce/{depot}"}`,
Config: `{"p4.port": "ssl:111.222.333.444:1666", "p4.user": "admin", "p4.passwd": "pa$$word", "depots": [], "repositoryPathPattern": "perforce/{depot}"}`,
}
if err := ExternalServices(db).Create(ctx, confGet, svc); err != nil {
t.Fatal(err)

View File

@ -408,6 +408,14 @@ func GetLimitFromConfig(kind string, config interface{}) (rlc RateLimitConfig, e
rlc.IsDefault = false
}
rlc.BaseURL = c.Url
case *schema.PerforceConnection:
// 2/s is the default limit we enforce
rlc.Limit = rate.Limit(5000.0 / 3600.0)
if c != nil && c.RateLimit != nil {
rlc.Limit = limitOrInf(c.RateLimit.Enabled, c.RateLimit.RequestsPerHour)
rlc.IsDefault = false
}
rlc.BaseURL = c.P4Port
default:
return rlc, ErrRateLimitUnsupported{codehostKind: kind}
}

View File

@ -34,6 +34,29 @@
"default": 1000,
"minimum": 1
},
"rateLimit": {
"description": "Rate limit applied when making background API requests to Perforce.",
"title": "PerforceRateLimit",
"type": "object",
"required": ["enabled", "requestsPerHour"],
"properties": {
"enabled": {
"description": "true if rate limiting is enabled.",
"type": "boolean",
"default": true
},
"requestsPerHour": {
"description": "Requests per hour permitted. This is an average, calculated per second.",
"type": "number",
"default": 5000,
"minimum": 0
}
},
"default": {
"enabled": true,
"requestsPerHour": 5000
}
},
"authorization": {
"title": "PerforceAuthorization",
"description": "If non-null, enforces Perforce depot permissions.",

View File

@ -1050,6 +1050,8 @@ type PerforceConnection struct {
P4Port string `json:"p4.port"`
// P4User description: The user to be authenticated for p4 CLI (P4USER).
P4User string `json:"p4.user"`
// RateLimit description: Rate limit applied when making background API requests to Perforce.
RateLimit *PerforceRateLimit `json:"rateLimit,omitempty"`
// RepositoryPathPattern description: The pattern used to generate the corresponding Sourcegraph repository name for a Perforce depot. In the pattern, the variable "{depot}" is replaced with the Perforce depot's path.
//
// For example, if your Perforce depot path is "//Sourcegraph/" and your Sourcegraph URL is https://src.example.com, then a repositoryPathPattern of "perforce/{depot}" would mean that the Perforce depot is available on Sourcegraph at https://src.example.com/perforce/Sourcegraph.
@ -1058,6 +1060,14 @@ type PerforceConnection struct {
RepositoryPathPattern string `json:"repositoryPathPattern,omitempty"`
}
// PerforceRateLimit description: Rate limit applied when making background API requests to Perforce.
type PerforceRateLimit struct {
// Enabled description: true if rate limiting is enabled.
Enabled bool `json:"enabled"`
// RequestsPerHour description: Requests per hour permitted. This is an average, calculated per second.
RequestsPerHour float64 `json:"requestsPerHour"`
}
// PermissionsUserMapping description: Settings for Sourcegraph permissions, which allow the site admin to explicitly manage repository permissions via the GraphQL API. This setting cannot be enabled if repository permissions for any specific external service are enabled (i.e., when the external service's `authorization` field is set).
type PermissionsUserMapping struct {
// BindID description: The type of identifier to identify a user. The default is "email", which uses the email address to identify a user. Use "username" to identify a user by their username. Changing this setting will erase any permissions created for users that do not yet exist.