Skip to content

HBASE-29850 Add support for dynamic attributes using RequestAttributesFactory - #7665

Open
krconv wants to merge 3 commits into
apache:masterfrom
HubSpot:HBASE-29850-request-attribute-factory
Open

HBASE-29850 Add support for dynamic attributes using RequestAttributesFactory#7665
krconv wants to merge 3 commits into
apache:masterfrom
HubSpot:HBASE-29850-request-attribute-factory

Conversation

@krconv

Copy link
Copy Markdown

Summary

Adds RequestAttributesFactory to AsyncTableBuilder for generating request attributes dynamically per-request.

Motivation

It is possible to do this for the HBase 2 Table client by overriding the RpcControllerFactory on the connection, but that doesn't work reliably with AsyncTable because retries happen on Netty threads, losing thread-local context. This change provides a hook that is guaranteed to be called on the initiating thread.

Changes

  • Add RequestAttributesFactory interface with a single create(Map<String, byte[]>) method
  • Add setRequestAttributesFactory() to AsyncTableBuilder
  • Factory is invoked at the start of each operation (get, put, scan, batch, etc.)
  • getRequestAttributes() returns static attributes; factory is only used for actual requests

Usage

AsyncTable<?> table = conn.getTableBuilder(tableName)
.setRequestAttribute("static.key", value)
.setRequestAttributesFactory(attrs -> {
Map<String, byte[]> newAttrs = newHashMap<>(attrs);
newAttrs.put("dynamic.key", getValueFromThreadLocal());
returnnewAttrs;
})
.build();

@krconvkrconv changed the title HBASE-29850 Add an ability to generate request attributes per requestHBASE-29850 Add support for dynamic per-request attributes in AsyncTableJan 22, 2026
@Apache-HBase

This comment has been minimized.

@Apache-HBase

This comment has been minimized.

@Apache9

Copy link
Copy Markdown
Contributor

So the requirements here is to use different request attribute for different requests?

I think the design of the request attribute API is for static attributes, if we want to support dynamic attribute, we'd better redesign the APIs, like what you propose here, use a factory.

Out of interest, what is your usage for this feature?

Thanks.

@krconv

Copy link
Copy Markdown
Author

Yes—our requirement is to set a specific request attribute to a value calculated from thread-local context.

We use hbase.quota.user.override.key (which configures throttling to use a request attribute) to throttle based on a logical “upstream caller” rather than the connection user. For example, a high-volume nightly job (EmailJobs-nightlyPurgeJob) calls ObjectsWebService-web, which in this case is the component that actually holds the HBase AsyncTable client and issues deletes to a table objects-1. The service propagates the job identity via thread-local HTTP metadata and sets it as a request attribute so that the job is throttled independently, and can be slowed down without impacting user traffic and other lower-volume jobs. This matters because we share a single AsyncTable per table/JVM—without dynamic attributes, one noisy caller can cause throttling across all traffic handled by that service.

This is exactly what we do with the Table client using the RpcControllerFactory as a workaround, and it works well in our experience. With the AsyncTable though, the RpcControllerFactory is called from various threads during retries, so we don't have a way to propagate any thread-local context reliably.

@Apache9

Apache9 commented Jan 29, 2026

Copy link
Copy Markdown
Contributor

OK, got it. Seems reasonable.

So what about this:

  1. We add a setRequestAttributeFactory method, as you proposed in this PR, but take a Supplier<Map<String, byte[]>> as parameter.
  2. Introduce a FixedRequestAttributeFactory(not a native English speaker, maybe you can have a better name...) class, which always return the same request attribute map, as the default implementation. Also use Builder pattern for creating this factory class.
  3. Deprecated the old addRequestAttribute method in AsyncTableBuilder, let users use the new setRequestAttributeFactory method. But when users call the old addRequestAttribute method, we internally use FixedRequestAttributeFactory's Builder to create a FixedRequestAttributeFactory in the end.
  4. Do the same for sync client interfaces.

WDYT?

Thanks.

@krconv

krconv commented Jan 29, 2026

Copy link
Copy Markdown
Author

What do you think of also deprecating AsyncTable#getRequestAttributes() without a replacement, if the behavior of it is no longer well defined (i.e. the return value could change based on which thread it is called from)? I like your suggestion to merge the two request attribute setters on AsyncTableBuilder into one, but the complication I see is that now getRequestAttributes() no longer has any static set of attributes to return. I'm not sure why getRequestAttributes() would be useful to anyone, but nevertheless it might be harder for a user to migrate away from using that.

@Apache9

Copy link
Copy Markdown
Contributor

What do you think of also deprecating AsyncTable#getRequestAttributes() without a replacement, if the behavior of it is no longer well defined (i.e. the return value could change based on which thread it is called from)? I like your suggestion to merge the two request attribute setters on AsyncTableBuilder into one, but the complication I see is that now getRequestAttributes() no longer has any static set of attributes to return. I'm not sure why getRequestAttributes() would be useful to anyone, but nevertheless it might be harder for a user to migrate away from using that.

We can just call request attribute factory's method to get the request attribute for this method? And we can add more javadoc to mention that, since now we allow dynamic request attribute when requesting, the return value may not be the same when you implement a dynamic request attribute factory.

@krconv
krconvforce-pushed the HBASE-29850-request-attribute-factory branch from 38f36f5 to d708dbaCompareJanuary 30, 2026 11:44

@krconvkrconv left a comment

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

I've updated the AsyncTable codepath to reflect the latest suggestions. If this looks good, I'll fix tests and extend to the Table codepath as well. Thank you!

* {@link #setRequestAttributesFactory(RequestAttributesFactory)}.
* @param key the attribute key
* @param value the attribute value
* @deprecated Since 3.0.0, will be removed in 4.0.0. Please use

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

I think master is 4.0.0; should it get deprecated for a later version or should I remove it from master?

private final Map<String, byte[]> requestAttributes = new HashMap<>();

/**
* Sets a request attribute. If value is null, the attribute is removed.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

The underlying Protobuf ByteString will throw a NPE if value is null, so instead this factory removes the entry if it's null.

* @see AsyncTableBuilder#setRequestAttributesFactory(RequestAttributesFactory)
*/
@InterfaceAudience.Public
public final class FixedRequestAttributesFactory implements RequestAttributesFactory {

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

The other ideas I had were ImmutableRequestAttributesFactory or StaticRequestAttributesFactory, but I think FixedRequestAttributesFactory is most clear because Immutable- might be interpreted as "the returned collection is immutable" instead of that it will return the same collection every time; and Static- clashes with the reserved word in my opinion

* @see AsyncTableBuilder#setRequestAttributesFactory(RequestAttributesFactory)
*/
@InterfaceAudience.Public
public interface RequestAttributesFactory {

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

I think it still makes sense to have this factory, mainly for documenting it's behavior; open to replacing with a Supplier<String, byte[]> though

@Apache-HBase

This comment has been minimized.

@Apache-HBase

This comment has been minimized.

@krconv
krconv marked this pull request as draft January 30, 2026 13:33
@krconv
krconvforce-pushed the HBASE-29850-request-attribute-factory branch from 250275b to fe649a9CompareFebruary 5, 2026 18:19
@Apache-HBase

This comment has been minimized.

@Apache-HBase

This comment has been minimized.

@Apache-HBase

Copy link
Copy Markdown

🎊 +1 overall

VoteSubsystemRuntimeLogfileComment
+0 🆗reexec0m 38sDocker mode activated.
_ Prechecks _
+1 💚dupname0m 0sNo case conflicting files found.
+0 🆗codespell0m 0scodespell was not available.
+0 🆗detsecrets0m 0sdetect-secrets was not available.
+1 💚@author0m 0sThe patch does not contain any @author tags.
+1 💚hbaseanti0m 0sPatch does not have any anti-patterns.
_ master Compile Tests _
+0 🆗mvndep0m 11sMaven dependency ordering for branch
+1 💚mvninstall2m 34smaster passed
+1 💚compile3m 15smaster passed
+1 💚checkstyle1m 0smaster passed
+1 💚spotbugs1m 46smaster passed
+1 💚spotless0m 39sbranch has no errors when running spotless:check.
_ Patch Compile Tests _
+0 🆗mvndep0m 11sMaven dependency ordering for patch
+1 💚mvninstall2m 17sthe patch passed
+1 💚compile3m 12sthe patch passed
+1 💚javac3m 12sthe patch passed
+1 💚blanks0m 0sThe patch has no blanks issues.
+1 💚checkstyle0m 59sthe patch passed
+1 💚spotbugs1m 55sthe patch passed
+1 💚hadoopcheck8m 35sPatch does not cause any errors with Hadoop 3.3.6 3.4.1.
+1 💚spotless0m 34spatch has no errors when running spotless:check.
_ Other Tests _
+1 💚asflicense0m 15sThe patch does not generate ASF License warnings.
33m 34s
SubsystemReport/Notes
DockerClientAPI=1.53 ServerAPI=1.53 base: https://ci-hbase.apache.org/job/HBase-PreCommit-GitHub-PR/job/PR-7665/4/artifact/yetus-general-check/output/Dockerfile
GITHUB PR#7665
Optional Testsdupname asflicense javac spotbugs checkstyle codespell detsecrets compile hadoopcheck hbaseanti spotless
unameLinux b64badd76e4b 6.14.0-1018-aws #18~24.04.1-Ubuntu SMP Mon Nov 24 19:46:27 UTC 2025 x86_64 x86_64 x86_64 GNU/Linux
Build toolmaven
Personalitydev-support/hbase-personality.sh
git revisionmaster / fe649a9
Default JavaEclipse Adoptium-17.0.11+9
Max. process+thread count86 (vs. ulimit of 30000)
modulesC: hbase-client hbase-server U: .
Console outputhttps://ci-hbase.apache.org/job/HBase-PreCommit-GitHub-PR/job/PR-7665/4/console
versionsgit=2.34.1 maven=3.9.8 spotbugs=4.7.3
Powered byApache Yetus 0.15.0 https://yetus.apache.org

This message was automatically generated.

@Apache-HBase

Copy link
Copy Markdown

💔 -1 overall

VoteSubsystemRuntimeLogfileComment
+0 🆗reexec0m 12sDocker mode activated.
-0 ⚠️yetus0m 3sUnprocessed flag(s): --brief-report-file --spotbugs-strict-precheck --author-ignore-list --blanks-eol-ignore-file --blanks-tabs-ignore-file --quick-hadoopcheck
_ Prechecks _
_ master Compile Tests _
+0 🆗mvndep0m 11sMaven dependency ordering for branch
+1 💚mvninstall2m 33smaster passed
+1 💚compile1m 2smaster passed
+1 💚javadoc0m 36smaster passed
+1 💚shadedjars4m 23sbranch has no errors when building our shaded downstream artifacts.
_ Patch Compile Tests _
+0 🆗mvndep0m 13sMaven dependency ordering for patch
+1 💚mvninstall2m 16sthe patch passed
+1 💚compile1m 1sthe patch passed
+1 💚javac1m 1sthe patch passed
+1 💚javadoc0m 34sthe patch passed
+1 💚shadedjars4m 20spatch has no errors when building our shaded downstream artifacts.
_ Other Tests _
+1 💚unit1m 24shbase-client in the patch passed.
-1 ❌unit215m 20s/patch-unit-hbase-server.txthbase-server in the patch failed.
238m 12s
SubsystemReport/Notes
DockerClientAPI=1.53 ServerAPI=1.53 base: https://ci-hbase.apache.org/job/HBase-PreCommit-GitHub-PR/job/PR-7665/4/artifact/yetus-jdk17-hadoop3-check/output/Dockerfile
GITHUB PR#7665
Optional Testsjavac javadoc unit compile shadedjars
unameLinux 25334e861f0c 6.14.0-1018-aws #18~24.04.1-Ubuntu SMP Mon Nov 24 19:46:27 UTC 2025 x86_64 x86_64 x86_64 GNU/Linux
Build toolmaven
Personalitydev-support/hbase-personality.sh
git revisionmaster / fe649a9
Default JavaEclipse Adoptium-17.0.11+9
Test Resultshttps://ci-hbase.apache.org/job/HBase-PreCommit-GitHub-PR/job/PR-7665/4/testReport/
Max. process+thread count4776 (vs. ulimit of 30000)
modulesC: hbase-client hbase-server U: .
Console outputhttps://ci-hbase.apache.org/job/HBase-PreCommit-GitHub-PR/job/PR-7665/4/console
versionsgit=2.34.1 maven=3.9.8
Powered byApache Yetus 0.15.0 https://yetus.apache.org

This message was automatically generated.

@krconv
krconvforce-pushed the HBASE-29850-request-attribute-factory branch from fe649a9 to 684ebb9CompareFebruary 6, 2026 19:16
@krconv
krconvforce-pushed the HBASE-29850-request-attribute-factory branch from 684ebb9 to 28b6367CompareFebruary 6, 2026 19:36
@krconv
krconv marked this pull request as ready for review February 7, 2026 09:57
@krconv

Copy link
Copy Markdown
Author

I've fixed the unit test failure, but I believe the new GitHub-based workflows will need to be approved for this PR to re-run them

@krconv
krconvforce-pushed the HBASE-29850-request-attribute-factory branch 2 times, most recently from 204f04d to 28b6367CompareFebruary 10, 2026 14:52
@krconvkrconv changed the title HBASE-29850 Add support for dynamic per-request attributes in AsyncTableHBASE-29850 Add support for dynamic attributes using RequestAttributesFactoryFeb 13, 2026
@krconv

Copy link
Copy Markdown
Author

@Apache9 Can you take another look at this? I believe it is done, and a good addition to the client. Failing tests are unrelated

Sign up for freeto 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.

3 participants

@krconv@Apache-HBase@Apache9