| // Copyright (C) 2018 The Android Open Source Project |
| // |
| // Licensed under the Apache License, Version 2.0 (the "License"); |
| // you may not use this file except in compliance with the License. |
| // You may obtain a copy of the License at |
| // |
| // http://www.apache.org/licenses/LICENSE-2.0 |
| // |
| // Unless required by applicable law or agreed to in writing, software |
| // distributed under the License is distributed on an "AS IS" BASIS, |
| // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| // See the License for the specific language governing permissions and |
| // limitations under the License. |
| |
| package com.google.gerrit.server.quota; |
| |
| import com.google.gerrit.entities.Account; |
| import com.google.gerrit.entities.Change; |
| import com.google.gerrit.entities.Project; |
| import com.google.gerrit.server.CurrentUser; |
| import com.google.inject.ImplementedBy; |
| |
| /** |
| * Backend interface to perform quota requests on. By default, this interface is backed by {@link |
| * DefaultQuotaBackend} which calls all plugins that implement {@link QuotaEnforcer}. A different |
| * implementation might be bound in tests. Plugins are not supposed to implement this interface, but |
| * bind a {@link QuotaEnforcer} implementation instead. |
| * |
| * <p>All quota requests require a quota group and a user. Enriching them with a top-level entity |
| * {@code Change, Project, Account} is optional but should be done if the request is targeted. |
| * |
| * <p>Example usage: |
| * |
| * <pre> |
| * quotaBackend.currentUser().project(projectName).requestToken("/projects/create").throwOnError(); |
| * quotaBackend.user(user).requestToken("/restapi/config/put").throwOnError(); |
| * QuotaResponse.Aggregated result = quotaBackend.currentUser().account(accountId).requestToken("/restapi/accounts/emails/validate"); |
| * QuotaResponse.Aggregated result = quotaBackend.currentUser().project(projectName).requestTokens("/projects/git/upload", numBytesInPush); |
| * </pre> |
| * |
| * <p>All quota groups must be documented in {@code quota.txt} and detail the metadata that is |
| * provided (i.e. the parameters used to scope down the quota request). |
| */ |
| @ImplementedBy(DefaultQuotaBackend.class) |
| public interface QuotaBackend { |
| /** Constructs a request for the current user. */ |
| WithUser currentUser(); |
| |
| /** |
| * See {@link #currentUser()}. Use this method only if you can't guarantee that the request is for |
| * the current user (e.g. impersonation). |
| */ |
| WithUser user(CurrentUser user); |
| |
| /** |
| * An interface capable of issuing quota requests. Scope can be futher reduced by providing a |
| * top-level entity. |
| */ |
| interface WithUser extends WithResource { |
| /** Scope the request down to an account. */ |
| WithResource account(Account.Id account); |
| |
| /** Scope the request down to a project. */ |
| WithResource project(Project.NameKey project); |
| |
| /** Scope the request down to a change. */ |
| WithResource change(Change.Id change, Project.NameKey project); |
| } |
| |
| /** An interface capable of issuing quota requests. */ |
| interface WithResource { |
| /** Issues a single quota request for {@code 1} token. */ |
| default QuotaResponse.Aggregated requestToken(String quotaGroup) { |
| return requestTokens(quotaGroup, 1); |
| } |
| |
| /** Issues a single quota request for {@code numTokens} tokens. */ |
| QuotaResponse.Aggregated requestTokens(String quotaGroup, long numTokens); |
| |
| /** |
| * Issues a single quota request for {@code numTokens} tokens but signals the implementations |
| * not to deduct any quota yet. Can be used to do pre-flight requests where necessary |
| */ |
| QuotaResponse.Aggregated dryRun(String quotaGroup, long tokens); |
| |
| /** |
| * Requests a minimum number of tokens available in implementations. This is a pre-flight check |
| * for the exceptional case when the requested number of tokens is not known in advance but |
| * boundary can be specified. For instance, when the commit is received its size is not known |
| * until the transfer happens however one can specify how many bytes can be accepted to meet the |
| * repository size quota. |
| * |
| * <p>By definition, this is not an allocating request, therefore, it should be followed by the |
| * call to {@link #requestTokens(String, long)} when the size gets determined so that quota |
| * could be properly adjusted. It is in developer discretion to ensure that it gets called. |
| * There might be a case when particular quota gets temporarily overbooked when multiple |
| * requests are performed but the following calls to {@link #requestTokens(String, long)} will |
| * fail at the moment when a quota is exhausted. It is not a subject of quota backend to reclaim |
| * tokens that were used due to overbooking. |
| */ |
| QuotaResponse.Aggregated availableTokens(String quotaGroup); |
| } |
| } |