-
Notifications
You must be signed in to change notification settings - Fork 121
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
Conversation
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
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nice work!
common/src/main/java/com/linecorp/centraldogma/common/TooManyRequestsException.java
Outdated
Show resolved
Hide resolved
common/src/main/java/com/linecorp/centraldogma/common/TooManyRequestsException.java
Outdated
Show resolved
Hide resolved
common/src/main/java/com/linecorp/centraldogma/common/TooManyRequestsException.java
Outdated
Show resolved
Hide resolved
common/src/main/java/com/linecorp/centraldogma/common/TooManyRequestsException.java
Outdated
Show resolved
Hide resolved
it/src/test/java/com/linecorp/centraldogma/it/ReplicationWriteQuotaTest.java
Outdated
Show resolved
Hide resolved
...ain/java/com/linecorp/centraldogma/server/internal/replication/ZooKeeperCommandExecutor.java
Outdated
Show resolved
Hide resolved
server/src/test/java/com/linecorp/centraldogma/server/internal/replication/Replica.java
Outdated
Show resolved
Hide resolved
server/src/test/java/com/linecorp/centraldogma/server/internal/replication/Replica.java
Outdated
Show resolved
Hide resolved
server/src/test/java/com/linecorp/centraldogma/server/internal/replication/Replica.java
Outdated
Show resolved
Hide resolved
Could you also check the test failure? |
Oops... |
Codecov Report
@@ 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 Continue to review full report at Codecov.
|
There was a problem hiding this 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) -> { |
There was a problem hiding this comment.
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?
There was a problem hiding this comment.
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. :-)
...ain/java/com/linecorp/centraldogma/server/internal/replication/ZooKeeperCommandExecutor.java
Show resolved
Hide resolved
count.setCount(requestQuota); | ||
} | ||
final InterProcessSemaphoreV2 semaphore = semaphoreAndCount.getKey(); | ||
final Lease lease = semaphore.acquire(200, TimeUnit.MILLISECONDS); |
There was a problem hiding this comment.
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, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
``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, | |
Yes. The benchmark code included quota limitation. |
https://travis-ci.com/github/line/centraldogma/builds/206964876#L716 |
Hmm... I'm not sure, let me check it more. |
There was a problem hiding this 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; |
There was a problem hiding this comment.
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
?
There was a problem hiding this comment.
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
.
Can't we use Armeria's request metrics with status? |
Thanks for reviewing! 🙇♂️ |
https://travis-ci.com/github/line/centraldogma/builds/207208682#L703 |
Oops... I missed this message. 😅 |
🎉 🎉 🎉 |
Thanks for reviewing! |
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:
QuotaConfig
toCentralDogmaConfig
for a global configuration. e.g.QuotaConfig
toRepositoryMetadata
for a repoistory level configrationRateLimiter
to limit requests for a standalone modeInterProcessSemaphoreV2
to limit requests for a replication mode/metadata/{projectName}/repos/{repoName}/quota/write
Result: