Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add quota limits for push command #545

Merged
merged 11 commits into from
Dec 10, 2020
Merged

Add quota limits for push command #545

merged 11 commits into from
Dec 10, 2020

Conversation

ikhoon
Copy link
Contributor

@ikhoon ikhoon commented Nov 20, 2020

Motivation:

Central Dogma is a read centric application which means
heavy writes could be a risk to the reliabilty of the system.
By limiting write requests, Central Dogma is able to respond flexibly to burst traffic.

Modifications:

  • Add QuotaConfig to CentralDogmaConfig for a global configuration. e.g.
    "writeQuotaPerRepository": {
      "requestQuota" : 5,
      "timeWindowSeconds": 1
    }
  • Add QuotaConfig to RepositoryMetadata for a repoistory level configration
  • Use Guava's RateLimiter to limit requests for a standalone mode
  • Use InterProcessSemaphoreV2 to limit requests for a replication mode
  • Add a write qouta REST API to MetadataService
    • PATCH /metadata/{projectName}/repos/{repoName}/quota/write
  • Refactor test code to build Zookeeper cluster easily

Result:

Motivation:

Central Dogma is a read centric application which means
heavy writes could be a risk to the reliabilty of the system.
By limiting write requests, Central Dogma is able to respond flexibly to burst traffic.

Modifications:

- Add `QuotaConfig` to `CentralDogmaConfig` for a global configration
  ```json
  "writeQuotaPerRepository": {
    "requestQuota" : 10,
    "timeWindowSeconds": 1
  }
  ```
- Add `QuotaConfig` to `RepositoryMetadata` for a repoistory level configration
- Use Guava's `RateLimiter` to limit requests for a standalone mode
- Use `InterProcessSemaphoreV2` to limit requests for a replication mode
- Add a write qouta REST API to MetadataService
  - PATCH `/metadata/{projectName}/repos/{repoName}/quota/write`
- Refactor test code to build Zookeeper cluster easily

Result:

- You can now set API quotas to push requests for repositories.
- Fixes line#528
@ikhoon ikhoon added this to the 0.46.1 milestone Nov 20, 2020
@ikhoon ikhoon modified the milestones: 0.46.1, 0.47.0 Dec 2, 2020
Copy link
Contributor

@minwoox minwoox left a comment

Choose a reason for hiding this comment

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

Nice work!

@minwoox
Copy link
Contributor

minwoox commented Dec 3, 2020

Could you also check the test failure?
https://travis-ci.com/github/line/centraldogma/builds/203727256#L912

@ikhoon
Copy link
Contributor Author

ikhoon commented Dec 3, 2020

Could you also check the test failure?
travis-ci.com/github/line/centraldogma/builds/203727256#L912

Oops...

@codecov
Copy link

codecov bot commented Dec 4, 2020

Codecov Report

Merging #545 (5176fa4) into master (3927148) will increase coverage by 0.37%.
The diff coverage is 76.78%.

Impacted file tree graph

@@             Coverage Diff              @@
##             master     #545      +/-   ##
============================================
+ Coverage     69.75%   70.13%   +0.37%     
- Complexity     3187     3267      +80     
============================================
  Files           325      328       +3     
  Lines         12796    13007     +211     
  Branches       1376     1402      +26     
============================================
+ Hits           8926     9122     +196     
- Misses         3002     3010       +8     
- Partials        868      875       +7     
Impacted Files Coverage Δ Complexity Δ
...ogma/server/command/ForwardingCommandExecutor.java 46.66% <0.00%> (-7.18%) 5.00 <0.00> (ø)
...er/internal/admin/auth/SessionTokenAuthorizer.java 100.00% <ø> (+5.55%) 6.00 <0.00> (ø)
.../centraldogma/common/TooManyRequestsException.java 40.00% <40.00%> (ø) 3.00 <3.00> (?)
.../com/linecorp/centraldogma/server/QuotaConfig.java 40.00% <40.00%> (ø) 5.00 <5.00> (?)
...ecorp/centraldogma/server/CentralDogmaBuilder.java 53.96% <66.66%> (+3.96%) 23.00 <1.00> (+4.00)
...ntraldogma/server/metadata/RepositoryMetadata.java 65.51% <66.66%> (-2.49%) 10.00 <3.00> (+1.00) ⬇️
...rver/internal/replication/SettableSharedCount.java 73.68% <73.68%> (ø) 6.00 <6.00> (?)
...internal/replication/ZooKeeperCommandExecutor.java 77.73% <82.02%> (+0.72%) 90.00 <24.00> (+20.00)
...ogma/server/command/StandaloneCommandExecutor.java 86.95% <90.00%> (+1.62%) 60.00 <24.00> (+23.00)
...com/linecorp/centraldogma/server/CentralDogma.java 76.20% <100.00%> (+0.72%) 47.00 <0.00> (+1.00)
... and 21 more

Continue to review full report at Codecov.

Legend - Click here to learn more
Δ = absolute <relative> (impact), ø = not affected, ? = missing data
Powered by Codecov. Last update 3927148...5176fa4. Read the comment docs.

Copy link
Contributor

@minwoox minwoox left a comment

Choose a reason for hiding this comment

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

Just some nits. 😄

final double permitsPerSecond =
permitsForRepo != 0 ? permitsForRepo : this.permitsPerSecond;
if (permitsPerSecond > 0) {
return writeRateLimiters.compute(projectName + '/' + repoName, (key, rateLimiter) -> {
Copy link
Contributor

Choose a reason for hiding this comment

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

How about adding todo for removing the rate limiter when the repository is removed?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I can address it in this PR. :-)

count.setCount(requestQuota);
}
final InterProcessSemaphoreV2 semaphore = semaphoreAndCount.getKey();
final Lease lease = semaphore.acquire(200, TimeUnit.MILLISECONDS);
Copy link
Contributor

Choose a reason for hiding this comment

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

the average QPS was 7~8 per repository.
This QPS test was executed including this lease mechanism, right?
Then, 200ms will be fine I guess. 😄

- ``writeQuotaPerRepository``

- the maximum allowed write quota per repository. If ``requestQuota`` is set to 10 and
``timeWindowSeconds`` is set to 1, :ref:`pushing-a-commit`` cannot exceeds 10 QPS; if exceeded,
Copy link
Contributor

Choose a reason for hiding this comment

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

Suggested change
``timeWindowSeconds`` is set to 1, :ref:`pushing-a-commit`` cannot exceeds 10 QPS; if exceeded,
``timeWindowSeconds`` is set to 1, :ref:`pushing-a-commit` cannot exceed 10 QPS; if exceeded,

@ikhoon
Copy link
Contributor Author

ikhoon commented Dec 7, 2020

the average QPS was 7~8 per repository.
This QPS test was executed including this lease mechanism, right?
Then, 200ms will be fine I guess. 😄

Yes. The benchmark code included quota limitation.

@minwoox
Copy link
Contributor

minwoox commented Dec 7, 2020

@ikhoon
Copy link
Contributor Author

ikhoon commented Dec 7, 2020

travis-ci.com/github/line/centraldogma/builds/206964876#L716
Is this related? 🤔

Hmm... I'm not sure, let me check it more.

Copy link
Contributor

@minwoox minwoox left a comment

Choose a reason for hiding this comment

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

Great job, @ikhoon! Thanks a lot for working on this. 😄
BTW, how about adding a metric to get to know how frequently TooManyRequestException happens?

@Override
public final void setWriteQuota(String projectName, String repoName, @Nullable QuotaConfig writeQuota) {
final double permitsForRepo = writeQuota != null ? writeQuota.permitsPerSecond() : 0;
final double permitsPerSecond = permitsForRepo != 0 ? permitsForRepo : this.permitsPerSecond;
Copy link
Contributor

Choose a reason for hiding this comment

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

What happen if this.permitsPerSecond is -1?

Copy link
Contributor Author

@ikhoon ikhoon Dec 8, 2020

Choose a reason for hiding this comment

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

If this.permitsPerSecond is -1, this method should not be called.
This method is called by MetadataService.updateWriteQuota() and StandaloneCommandExecutor.getRateLimiter(). They are checked already.

Anyway, it would better return with no-op, if this.permitsPerSecond is -1.

@ikhoon
Copy link
Contributor Author

ikhoon commented Dec 8, 2020

BTW, how about adding a metric to get to know how frequently TooManyRequestException happens?

Can't we use Armeria's request metrics with status?

@ikhoon
Copy link
Contributor Author

ikhoon commented Dec 8, 2020

Great job, @ikhoon! Thanks a lot for working on this. 😄

Thanks for reviewing! 🙇‍♂️

@minwoox
Copy link
Contributor

minwoox commented Dec 8, 2020

@ikhoon
Copy link
Contributor Author

ikhoon commented Dec 9, 2020

travis-ci.com/github/line/centraldogma/builds/207208682#L703
Could you check this NPE?

Oops... I missed this message. 😅

@minwoox minwoox merged commit 082d87b into line:master Dec 10, 2020
@minwoox
Copy link
Contributor

minwoox commented Dec 10, 2020

🎉 🎉 🎉

@ikhoon ikhoon deleted the quota branch December 10, 2020 02:12
@ikhoon
Copy link
Contributor Author

ikhoon commented Dec 10, 2020

Thanks for reviewing!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Limits and quotas on API requests
2 participants