Skip to content

KAFKA-6576: Configurable Quota Management (KIP-257)#4699

Merged
rajinisivaram merged 4 commits into
apache:trunkfrom
rajinisivaram:KAFKA-6576-configurable-quota
Apr 6, 2018
Merged

KAFKA-6576: Configurable Quota Management (KIP-257)#4699
rajinisivaram merged 4 commits into
apache:trunkfrom
rajinisivaram:KAFKA-6576-configurable-quota

Conversation

@rajinisivaram

Copy link
Copy Markdown
Contributor

Enable quota calculation to be customized using a configurable callback. See KIP-257 for details.

Committer Checklist (excluded from commit message)

  • Verify design and implementation
  • Verify test coverage and CI build status
  • Verify documentation (including upgrade notes)

@rajinisivaram
rajinisivaram force-pushed the KAFKA-6576-configurable-quota branch from 47cff9b to 0c6b425 Compare March 16, 2018 19:08
@rajinisivaram
rajinisivaram force-pushed the KAFKA-6576-configurable-quota branch 2 times, most recently from 65678dc to 0e12bc3 Compare March 29, 2018 11:22
@rajinisivaram

Copy link
Copy Markdown
Contributor Author

retest this please

@junrao junrao left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

@rajinisivaram : Thanks for the patch. Left a few comments below.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Typo "is takes ".

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

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.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

@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?

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

@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.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

@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.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

@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.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

The comment in line 472 probably needs to be adjusted.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

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?

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

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.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Not sure, I changed it and tests still passed, so I guess it is to remove it.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

.toMap is not needed.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

metric.value is deprecated. Could we use the new api? There are a few other places like that.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Could we add some doc of this class? For example, how is the naming convention of group for quota and topics.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Perhaps add a comment on what brokerId is used for.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

"some.prop" is not a valid prop. So perhaps rename invalidProps to invalidReconfigurableProps ?

@rajinisivaram
rajinisivaram force-pushed the KAFKA-6576-configurable-quota branch from 0e12bc3 to 1590947 Compare April 5, 2018 12:03
@rajinisivaram

Copy link
Copy Markdown
Contributor Author

@junrao Thanks for the review. I have moved the public interface to clients and converted it to Java. I have made most of the updates from the review, left a question about the interface.

@junrao junrao left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

@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);

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Do we need quotaType? It seems that it's unrelated to metric tags.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

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.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

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)),

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

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.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

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

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Is it useful to have CustomQuotas since it seems it's never used?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

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);

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Is there a case when updateQuota() can return false? Ditto for removeQuota().

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

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")

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

We probably want to rename validProps to validReconfigurableProps for consistency?

@rajinisivaram

Copy link
Copy Markdown
Contributor Author

@junrao Thanks for the review. I have updated the code and also made some changes to CustomQuotaCallbackTest to make it complete faster (the tests check only the metrics on the broker-side to avoid waiting for throttled requests to complete). Client-side metrics are verified by the other quota tests anyway.

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 junrao left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

@rajinisivaram : Thanks for the updated patch. LGTM

@rajinisivaram

Copy link
Copy Markdown
Contributor Author

@junrao Thanks for the reviews, merging to trunk.

@rajinisivaram
rajinisivaram merged commit 77ebd32 into apache:trunk Apr 6, 2018
ying-zheng pushed a commit to ying-zheng/kafka that referenced this pull request Jul 6, 2018
Enable quota calculation to be customized using a configurable callback. See KIP-257 for details.

Reviewers: Jun Rao <junrao@gmail.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants