KAFKA-6576: Configurable Quota Management (KIP-257)#4699
Conversation
47cff9b to
0c6b425
Compare
65678dc to
0e12bc3
Compare
|
retest this please |
junrao
left a comment
There was a problem hiding this comment.
@rajinisivaram : Thanks for the patch. Left a few comments below.
There was a problem hiding this comment.
Since we already have quotaLimit(), could quota() just return the metric tags? The caller can then use the metric tags to obtain the quota limit. This way, there is only one way to get the quota limit.
There was a problem hiding this comment.
@junrao We need to go through the quota configs at different levels to determine the metric tags. quotaLimit also does something similar. So it is cheaper to return the limit with the tags, and since this is used for every request, it may be worth returning limit and tags together. With two calls, we also need to handle changes between the two invocations. Alternatively, we could do something different for default callback handler alone since custom handlers are less likely to have several levels. What do you think?
There was a problem hiding this comment.
@rajinisivaram : Hmm, my understanding is that quota() only needs to be called when the quota metric doesn't already exist. So, typically, quota() is only called on the first request from a given client and not on every request.
There was a problem hiding this comment.
@junrao At the moment, we call quota() on every request since need to work out which metric tags to use and to do that we need to know which level of configs are defined. But, you are right, after the first request, we already know the limit since the metric exists, so we don't need to call two methods every time. So agree, we can change the first method to just return metric tags. Will update the PR, thanks.
There was a problem hiding this comment.
@junrao I have changed the first method to quotaMetricTags to obtain just the tags and updated the code to use quotaLimit to get the limit in all cases. Thanks.
There was a problem hiding this comment.
The comment in line 472 probably needs to be adjusted.
There was a problem hiding this comment.
Ideally, the callback shouldn't access internal objects such as allMetrics. So perhaps the logic under singleUpdate should be handled in the same place where multiple updates are handled?
There was a problem hiding this comment.
This is not introduced in this patch. But I am wondering why we need to pass in a separate MetricConfig to throttleTime() since clientMetric contains MetricConfig already.
There was a problem hiding this comment.
Not sure, I changed it and tests still passed, so I guess it is to remove it.
There was a problem hiding this comment.
metric.value is deprecated. Could we use the new api? There are a few other places like that.
There was a problem hiding this comment.
Could we add some doc of this class? For example, how is the naming convention of group for quota and topics.
There was a problem hiding this comment.
Perhaps add a comment on what brokerId is used for.
There was a problem hiding this comment.
"some.prop" is not a valid prop. So perhaps rename invalidProps to invalidReconfigurableProps ?
0e12bc3 to
1590947
Compare
|
@junrao Thanks for the review. I have moved the public interface to |
junrao
left a comment
There was a problem hiding this comment.
@rajinisivaram : Thanks for the updated patch. A few more comments below.
| * @param quotaType Type of quota requested | ||
| * @return quota metric tags that indicate which other clients share this quota | ||
| */ | ||
| Map<String, String> quotaMetricTags(KafkaPrincipal principal, String clientId, ClientQuotaType quotaType); |
There was a problem hiding this comment.
Do we need quotaType? It seems that it's unrelated to metric tags.
There was a problem hiding this comment.
When multiple levels of quotas are used, the tags are dependent on which level config is chosen (e.g for {user, client} quota, both are included, for {user} quota, empty client-id is used). Since we allow configuring different levels of quota for Produce/Fetch/Request, they can potentially use different tags for the same client.
There was a problem hiding this comment.
Apart from updateClusterMetadata, all other methods are processed by individual ClientQuotaManagers and they pass in the quota type. I have moved the quota type as the first parameter of all these methods to keep them consistent.
| clientRateMetricName(clientQuotaEntity.sanitizedUser, clientQuotaEntity.clientId), | ||
| Some(getQuotaMetricConfig(clientQuotaEntity.quota)), | ||
| clientRateMetricName(metricTags), | ||
| Some(getQuotaMetricConfig(metricTags)), |
There was a problem hiding this comment.
This can be a bit tricky. If we call getQuotaMetricConfig() here, we will be paying the cost of calling ClientQuotaCallback.quotaLimit() on every request. We probably want to pass in a method and only call it when needed.
There was a problem hiding this comment.
I was thinking that the by-name parameter does lazy evaluation and hence the method doesn't get invoked on every request. I could be wrong, but I added a test and it seems to be true. What do you think?
| else QuotaTypes.ClientIdQuotaEnabled | ||
| private val clientQuotaType = quotaTypeToClientQuotaType(quotaType) | ||
| @volatile private var quotaTypesEnabled = clientQuotaCallback match { | ||
| case Some(_) => QuotaTypes.CustomQuotas |
There was a problem hiding this comment.
Is it useful to have CustomQuotas since it seems it's never used?
There was a problem hiding this comment.
It is used to avoid optimizing updates, the other values are used to check if only a single-level of quota is used. Have added a comment.
| * @param newValue The new quota value | ||
| * @return true if quotas have changed and metric configs may need to be updated | ||
| */ | ||
| boolean updateQuota(ClientQuotaEntity quotaEntity, ClientQuotaType quotaType, double newValue); |
There was a problem hiding this comment.
Is there a case when updateQuota() can return false? Ditto for removeQuota().
There was a problem hiding this comment.
I was using the return value with the default callback handler earlier, but now that it has been tidied up, we don't need it. Changed both updateQuota and removeQuota to void returns.
| val origProps = TestUtils.createBrokerConfig(0, TestUtils.MockZkConnect, port = 8181) | ||
| val config = KafkaConfig(origProps) | ||
| val invalidReconfigurableProps = Set(KafkaConfig.LogCleanerThreadsProp, KafkaConfig.BrokerIdProp, "some.prop") | ||
| val validProps = Set(KafkaConfig.LogCleanerThreadsProp, KafkaConfig.LogCleanerDedupeBufferSizeProp, "some.prop") |
There was a problem hiding this comment.
We probably want to rename validProps to validReconfigurableProps for consistency?
|
@junrao Thanks for the review. I have updated the code and also made some changes to I also added a new method to the callback interface to check if quotas need resetting. Since we now don't get quota limits from the callback handler on each request, we need to check if limits may have changed (e.g. with a callback that uses an external database to store quotas). Thoughts? |
junrao
left a comment
There was a problem hiding this comment.
@rajinisivaram : Thanks for the updated patch. LGTM
|
@junrao Thanks for the reviews, merging to trunk. |
Enable quota calculation to be customized using a configurable callback. See KIP-257 for details. Reviewers: Jun Rao <junrao@gmail.com>
Enable quota calculation to be customized using a configurable callback. See KIP-257 for details.
Committer Checklist (excluded from commit message)