-
Notifications
You must be signed in to change notification settings - Fork 3.6k
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
Update Broker service configuration dynamically #186
Conversation
e7ba703
to
206ba5d
Compare
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.
Change looks good. A couple of comments:
-
We should make very clear that the configuration in ZK overrides whatever is on the host config file. Eg: when we startup we should printing info log of what config we found on ZK
-
We should have 2 way of using dynamic fields
- Either we keep reading the value from ServiceConfiguration and thus at some point we pick up the updated value. We don't need mutexes, since there's no strict requirement on change visibility.
- Or we register the listener
* @param configValue | ||
* : configuration value | ||
*/ | ||
private void updateServiecConfiguration(String configName, String configValue) { |
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.
Typo in updateServiecConfiguration
*/ | ||
private void updateServiecConfiguration(String configName, String configValue) { | ||
try { | ||
Field field = ServiceConfiguration.class.getDeclaredField(configName); |
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.
Instead of doing the reflection check on each request, could we doing just once (at startup) and keep a Set<>
of keys that are dynamically updatable?
@@ -62,6 +62,7 @@ | |||
private long zooKeeperSessionTimeoutMillis = 30000; | |||
// Time to wait for broker graceful shutdown. After this time elapses, the | |||
// process will be killed | |||
@FieldContext(dynamic = true) |
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'd leave this PR to implement the "mechanism" and then we can make the actual variables dynamic in subsequent PRs.
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.
Yes, but in order to write test-cases and perform testing of this feature I take one of this simple attribute in this PR. It helps to write e2e testcases for this feature.
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.
Sure
|
||
/** | ||
* Update dynamic-ServiceConfiguration with value present into zk-configuraiton-map and register listeners on | ||
* dynamic-ServiceConfiguration field to take appropriate action on change of zk-configuraiton-map. |
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.
zk-configuration-map
// update brokerShutdownTimeoutMs value on listener notification | ||
registerConfigurationListener("brokerShutdownTimeoutMs", new Consumer<String>() { | ||
@Override | ||
public void accept(String shutdownTime) { |
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.
can abbreviate into a lambda here
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.
Actually, for this example, we shouldn't even need to register for notification, right?
We should make sure that the value ServiceConfiguration
instance is updated, and then always read it from there, instead of caching it in a variable.
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.
Actually, for this example, we shouldn't even need to register for notification, right?
Actually, we are not updating ServiceConfiguration
instance's field value but we are calling registered listener for this field and it's registered-listener's responsibility to update the value and take appropriate action.
Because in case of #181 : Only when lookupRequestPermits
field-value changes we want to take certain action such as update Semaphore
's permits. Now, we have watch on entire dynamicConfigMap and listener can be triggered on every configKey-value change. So, registered listener can compare existing value and new value, and if it's changed then it can take appropriate action and update ServiceConfiguration
.
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.
addressed it on commit can you please take a look of it.
* @param listener | ||
* : listener which takes appropriate action on config-value change | ||
*/ | ||
public void registerConfigurationListener(String configKey, Consumer<String> listener) { |
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.
Can we make the listener to be working for string, ints, longs? (Maybe leveraging the FieldParser
)
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.
yes, but we can have non-primitive ParameterizedTypes also for which we have to pass Field
to parse the value. So, made appropriate change.
field.setAccessible(true); | ||
field.set(pulsar().getConfiguration(), FieldParser.value(value,field)); | ||
if (log.isDebugEnabled()) { | ||
log.debug("Successfully updated {}/{}", key, value); |
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.
We should print a info log line, for all the overridden variables
CmdBrokers(PulsarAdmin admin) { | ||
super("brokers", admin); | ||
jcommander.addCommand("list", new List()); | ||
jcommander.addCommand("namespaces", new Namespaces()); | ||
jcommander.addCommand("updateConfig", new UpdateConfigurationCmd()); |
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.
we've been using -
as separator in other commands. eg: update-config
3593728
to
f4adac9
Compare
@merlimat : addressed all the changes, added one more change: |
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.
Change looks very good, just a couple of minor comments
@@ -62,6 +62,7 @@ | |||
private long zooKeeperSessionTimeoutMillis = 30000; | |||
// Time to wait for broker graceful shutdown. After this time elapses, the | |||
// process will be killed | |||
@FieldContext(dynamic = true) |
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.
Sure
@@ -48,9 +48,23 @@ void run() throws Exception { | |||
} | |||
} | |||
|
|||
@Parameters(commandDescription = "Update dynamic-serviceConfiguration of broker") |
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.
Can we also add a command to get the list of "updatable" config fields?
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.
Also, it would be useful to have command to print all the config that are stored in ZK. To know what is being overridden.
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.
added both commands:
- getUpdatableConfigName
- getAllConfigurations to get key,value for all configs
* @param value | ||
* @throws PulsarAdminException | ||
*/ | ||
void updateConfiguration(String configName, String configValue) throws PulsarAdminException; |
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.
Instead of "updating" configuration, in this case we would be effectively "overriding" the local broker configuration with a "more important" configuration stored in ZK
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.
so, you mean we should rename the method to overrideDynamicConfiguration
or we want to make any functionality change?
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 renaming. I'm not 100% sure about the name, just wanted to make clear that it doesn't update the config in a broker, rather it writes config in ZK that supersedes whatever all the brokers have locally.
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.
updateDynamicConfigurationInZk
, persistDynamicConfigValue
, storeDynamicConfigValue
?
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 have updated the document to call it out clearly and also renamed it as updateDynamicConfiguration
. Do you think if we rename it to something else.?
getPermitZkNodeMethod.setAccessible(true); | ||
getPermitZkNodeMethod.invoke(pulsar.getBrokerService()); | ||
// verify value is updated | ||
assertEquals(pulsar.getConfiguration().getBrokerShutdownTimeoutMs(), shutdownTime); |
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 notification from ZK might be delayed in the background thread. In addition since we're doing "dirty" reads (avoid synchronization), it might take a bit more after the new value is visible on the getter method. We should have a retry loop here in the test to give a bit of time.
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 have added this logic in testUpdateDynamicConfigurationWithZkWatch
.
This test-case directly triggers registerListener because pulsarService started when znode was not created and can't set the watch if node is not exist. so, in this testcase it explicitly triggers registerListener call.
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.
Ok, tough is there also the other test that work with the zk watch and verifies we apply the change?
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.
yes, testUpdateDynamicConfigurationWithZkWatch first creates znode and any further config change happens by zk-watch. I have added retry loop there to wait for refreshed value.
public void testUpdateDynamicLocalConfiguration() throws Exception { | ||
|
||
// (1) try to update dynamic field | ||
final long shutdownTime = 10; |
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.
Can we verify that shutdown time is != 10 in the initial config?
224004d
to
67a124e
Compare
691ccb0
to
012a47b
Compare
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.
👍
* Expose EventTime consistently as a non-pointer * Expanded docs
Motivation
as discussed at #184 : sometime it requires to update Broker-serviceConfiguration dynamically with the help of generic api.
Modifications
Result
Broker can update serviceConfiguration dynamically and can execute appropriate action on configuration-value change.