-
Notifications
You must be signed in to change notification settings - Fork 3.4k
HBASE-29526 Dynamic configuration not working for coprocessor #7544
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
base: branch-3
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
|
@@ -17,11 +17,14 @@ | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| */ | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| package org.apache.hadoop.hbase.util; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| import org.apache.commons.lang3.StringUtils; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| import java.util.HashSet; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| import java.util.Set; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| import org.apache.hadoop.conf.Configuration; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| import org.apache.hadoop.hbase.coprocessor.CoprocessorHost; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| import org.apache.yetus.audience.InterfaceAudience; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| import org.apache.hbase.thirdparty.com.google.common.base.Preconditions; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| import org.apache.hbase.thirdparty.com.google.common.base.Strings; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| /** | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| * Helper class for coprocessor host when configuration changes. | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
@@ -32,19 +35,71 @@ public final class CoprocessorConfigurationUtil { | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| private CoprocessorConfigurationUtil() { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| public static boolean checkConfigurationChange(Configuration oldConfig, Configuration newConfig, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| String... configurationKey) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| /** | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| * Check configuration change by comparing current loaded coprocessors with configuration values. | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| * This method is useful when the configuration object has been updated but we need to determine | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| * if coprocessor configuration has actually changed compared to what's currently loaded. | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| * <p> | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| * <b>Note:</b> This method only detects changes in the set of coprocessor class names. It does | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| * <b>not</b> detect changes to priority or path for coprocessors that are already loaded with the | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| * same class name. If you need to update the priority or path of an existing coprocessor, you | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| * must restart the region/regionserver/master. | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| * @param coprocessorHost the coprocessor host to check current loaded coprocessors (can be null) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| * @param conf the configuration to check | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| * @param configurationKey the configuration keys to check | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| * @return true if configuration has changed, false otherwise | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| */ | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| public static boolean checkConfigurationChange(CoprocessorHost<?, ?> coprocessorHost, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| Configuration conf, String... configurationKey) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| Preconditions.checkArgument(configurationKey != null, "Configuration Key(s) must be provided"); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| boolean isConfigurationChange = false; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| Preconditions.checkArgument(conf != null, "Configuration must be provided"); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if ( | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| !conf.getBoolean(CoprocessorHost.COPROCESSORS_ENABLED_CONF_KEY, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| CoprocessorHost.DEFAULT_COPROCESSORS_ENABLED) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| ) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| return false; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if (coprocessorHost == null) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // If no coprocessor host exists, check if any coprocessors are now configured | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| return hasCoprocessorsConfigured(conf, configurationKey); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // Get currently loaded coprocessor class names | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| Set<String> currentlyLoaded = coprocessorHost.getCoprocessorClassNames(); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
Comment on lines
+57
to
+71
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if ( | |
| !conf.getBoolean(CoprocessorHost.COPROCESSORS_ENABLED_CONF_KEY, | |
| CoprocessorHost.DEFAULT_COPROCESSORS_ENABLED) | |
| ) { | |
| return false; | |
| } | |
| if (coprocessorHost == null) { | |
| // If no coprocessor host exists, check if any coprocessors are now configured | |
| return hasCoprocessorsConfigured(conf, configurationKey); | |
| } | |
| // Get currently loaded coprocessor class names | |
| Set<String> currentlyLoaded = coprocessorHost.getCoprocessorClassNames(); | |
| if (coprocessorHost == null) { | |
| // If no coprocessor host exists, check if any coprocessors are now configured | |
| if (!conf.getBoolean(CoprocessorHost.COPROCESSORS_ENABLED_CONF_KEY, | |
| CoprocessorHost.DEFAULT_COPROCESSORS_ENABLED)) { | |
| // Coprocessors are disabled and none are loaded, so no change | |
| return false; | |
| } | |
| return hasCoprocessorsConfigured(conf, configurationKey); | |
| } | |
| // Get currently loaded coprocessor class names | |
| Set<String> currentlyLoaded = coprocessorHost.getCoprocessorClassNames(); | |
| if (!conf.getBoolean(CoprocessorHost.COPROCESSORS_ENABLED_CONF_KEY, | |
| CoprocessorHost.DEFAULT_COPROCESSORS_ENABLED)) { | |
| // Coprocessors are now disabled, but if any are currently loaded, we need to unload them | |
| return !currentlyLoaded.isEmpty(); | |
| } |
Copilot
AI
Dec 16, 2025
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 new checkConfigurationChange method lacks unit test coverage. While integration tests exist in TestRegionServerOnlineConfigChange, they don't cover important edge cases such as: (1) disabling coprocessors when some are loaded, (2) the null coprocessorHost path that calls hasCoprocessorsConfigured, (3) parsing of className|priority|path format, (4) removing coprocessors from configuration, and (5) empty or whitespace-only class names. Consider adding unit tests for CoprocessorConfigurationUtil to ensure these scenarios are properly handled.
Copilot
AI
Dec 16, 2025
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 hasCoprocessorsConfigured method should validate that the coprocessor strings contain actual class names, not just check if the array is non-empty. If the configuration contains empty strings or only whitespace, this method will incorrectly return true even though no valid coprocessors are configured. This is inconsistent with the logic in checkConfigurationChange (lines 78-84) which properly filters empty class names. The method should parse the className|priority|path format and check if the actual class name is non-empty after trimming.
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 precondition check verifies that configurationKey is not null, but doesn't check if the array is empty. If an empty array is passed, the method will always return false (no change) even if coprocessors are currently loaded and should be unloaded. Consider adding a check for array length or documenting that an empty array is allowed and means "no configuration keys to check".