Skip to content

HBASE-29675 Add bounds check/descriptive OffsetOutOfBoundsException to BinaryComponentComparator - #7389

Open
droudnitsky wants to merge 2 commits into
apache:masterfrom
droudnitsky:master
Open

HBASE-29675 Add bounds check/descriptive OffsetOutOfBoundsException to BinaryComponentComparator#7389
droudnitsky wants to merge 2 commits into
apache:masterfrom
droudnitsky:master

Conversation

@droudnitsky

@droudnitskydroudnitsky commented Oct 15, 2025

Copy link
Copy Markdown
Contributor

https://issues.apache.org/jira/browse/HBASE-29675

Add a bounds check before doing byte array comparison in BinaryComponentComparator and throw a descriptive OffsetOutOfBoundsException which subclasses ArrayIndexOutOfBoundsException instead of doing an unchecked byte array comparison and throwing a nondescript ArrayIndexOutOfBoundsException which is difficult for clients to decipher root cause from.

@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 29sDocker mode activated.
_ Prechecks _
+1 💚dupname0m 1sNo 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 16sMaven dependency ordering for branch
+1 💚mvninstall3m 7smaster passed
+1 💚compile8m 27smaster passed
+1 💚checkstyle1m 10smaster passed
+1 💚spotbugs9m 39smaster passed
+1 💚spotless0m 44sbranch has no errors when running spotless:check.
_ Patch Compile Tests _
+0 🆗mvndep0m 16sMaven dependency ordering for patch
+1 💚mvninstall3m 10sthe patch passed
+1 💚compile8m 21sthe patch passed
+1 💚javac8m 21sthe patch passed
+1 💚blanks0m 0sThe patch has no blanks issues.
-0 ⚠️checkstyle1m 11s/results-checkstyle-root.txtroot: The patch generated 1 new + 4 unchanged - 0 fixed = 5 total (was 4)
+1 💚xmllint0m 0sNo new issues.
+1 💚spotbugs10m 4sthe patch passed
+1 💚hadoopcheck12m 0sPatch does not cause any errors with Hadoop 3.3.6 3.4.1.
+1 💚spotless0m 45spatch has no errors when running spotless:check.
_ Other Tests _
+1 💚asflicense0m 28sThe patch does not generate ASF License warnings.
68m 9s
SubsystemReport/Notes
DockerClientAPI=1.43 ServerAPI=1.43 base: https://ci-hbase.apache.org/job/HBase-PreCommit-GitHub-PR/job/PR-7389/2/artifact/yetus-general-check/output/Dockerfile
GITHUB PR#7389
Optional Testsdupname asflicense javac spotbugs checkstyle codespell detsecrets compile hadoopcheck hbaseanti spotless xmllint
unameLinux c3422ad41524 5.4.0-1103-aws #111~18.04.1-Ubuntu SMP Tue May 23 20:04:10 UTC 2023 x86_64 x86_64 x86_64 GNU/Linux
Build toolmaven
Personalitydev-support/hbase-personality.sh
git revisionmaster / cc1d916
Default JavaEclipse Adoptium-17.0.11+9
Max. process+thread count190 (vs. ulimit of 30000)
modulesC: hbase-client hbase-server . U: .
Console outputhttps://ci-hbase.apache.org/job/HBase-PreCommit-GitHub-PR/job/PR-7389/2/console
versionsgit=2.34.1 maven=3.9.8 spotbugs=4.7.3 xmllint=20913
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 29sDocker 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 26sMaven dependency ordering for branch
+1 💚mvninstall3m 41smaster passed
+1 💚compile2m 16smaster passed
+1 💚javadoc2m 42smaster passed
+1 💚shadedjars6m 17sbranch has no errors when building our shaded downstream artifacts.
_ Patch Compile Tests _
+0 🆗mvndep0m 16sMaven dependency ordering for patch
+1 💚mvninstall3m 2sthe patch passed
+1 💚compile2m 14sthe patch passed
+1 💚javac2m 14sthe patch passed
+1 💚javadoc2m 38sthe patch passed
+1 💚shadedjars6m 11spatch has no errors when building our shaded downstream artifacts.
_ Other Tests _
-1 ❌unit236m 28s/patch-unit-root.txtroot in the patch failed.
272m 47s
SubsystemReport/Notes
DockerClientAPI=1.43 ServerAPI=1.43 base: https://ci-hbase.apache.org/job/HBase-PreCommit-GitHub-PR/job/PR-7389/2/artifact/yetus-jdk17-hadoop3-check/output/Dockerfile
GITHUB PR#7389
Optional Testsjavac javadoc unit compile shadedjars
unameLinux f943f76dc312 5.4.0-1103-aws #111~18.04.1-Ubuntu SMP Tue May 23 20:04:10 UTC 2023 x86_64 x86_64 x86_64 GNU/Linux
Build toolmaven
Personalitydev-support/hbase-personality.sh
git revisionmaster / cc1d916
Default JavaEclipse Adoptium-17.0.11+9
Test Resultshttps://ci-hbase.apache.org/job/HBase-PreCommit-GitHub-PR/job/PR-7389/2/testReport/
Max. process+thread count4806 (vs. ulimit of 30000)
modulesC: hbase-client hbase-server . U: .
Console outputhttps://ci-hbase.apache.org/job/HBase-PreCommit-GitHub-PR/job/PR-7389/2/console
versionsgit=2.34.1 maven=3.9.8
Powered byApache Yetus 0.15.0 https://yetus.apache.org

This message was automatically generated.

@droudnitsky

Copy link
Copy Markdown
ContributorAuthor

Test failures are in TestReplicationMetricsforUI , not related

// increment the number of requests that were exceptions.
metrics.exception(e);

if (e instanceof DoNotRetryRuntimeException) throw new DoNotRetryIOException(e);

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

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

Hi @Apache9 would appreciate your advice here - do you think theres a better way to do this, possibly without introducing a new DoNotRetryRuntimeException ?

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.

Let me check the related code to see if there are other ways to throw checked exception out...
If not, maybe this is the only way?

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

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

Thank you Duo for the response, the exception happens in compareTo method from Comparable interface that doesn't allow for a checked exception, so yes you are correct its not possible to throw a checked exception directly.

One option I was considering , was instead of adding a generic DoNotRetryRuntimeException that is wrapped in DoNotRetryIOException in the RpcServer layer, is to try to do this catching/conversion to checked exception in a deeper layer closer to where the comparator is applied, but I did not find a clean place or a clean way to do this, so I think adding a generic DoNotRetryRuntimeException is the best/most maintainable way, and I am thinking it can be reused in other places if needed where its not possible to throw a checked exception but we need to be able to bubble a DoNotRetryIOException to the client.

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 problem is a RuntimeException usually means there is a code bug, so we may fail to clean up something in the call stack which causes problem, as we do not expect there will be a RuntimeException...
This is what I actually concern about.

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

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

Yes I see your point here, its not good to rely on runtime exception in the general case of issues deep in the call stack. The issue with the comparator that this is trying to address stems from the design of the comparator - I think its unique in that as far as I'm aware its the only comparator which breaks/cannot function if the shape of the data on the server clashes with the parameter the user specified when they created the comparator.

Ideally , I think this should have been designed/implemented in a way that allows the filter to skip rows/cells which are too short to be able to do the comparison, so the filter can function normally regardless of the length of the data on the server and skip where needed instead of erroring out, but because it was implemented as a comparator, AFAIK we cannot do this kind of skipping, the compareTo interface does not allow it, the only option is to error out if its not possible to do the comparison in compareTo.

To your point this seems to be a unique problem that we do not want/need a generalized solution for, given that in most cases RuntimeException is due to a code bug and not due to a normal case where is a mismatch between the parameter the user is providing and the data on the server. I am thinking to remove the generic DoNotRetryRuntimeException that I added , and in RpcServer only check for the specific OffsetOutOfBoundsException that only this comparator throws, and wrap it in DoNotRetryIOException. What do you think ? This way this special runtime exception handling only applies to this specific case and nothing else.

@Apache9

Copy link
Copy Markdown
Contributor

Checked the code, this is only used in CompareFilter?

Actually, all the filterXXX method in Filter can throw an IOException, and I think usually throwing RuntimeException in Comparator implementation does not hurt the whole system, so maybe a possible way is to catch RuntimeException in Filter implemention, and convert it to a DoNotRetryIOException to indicate that there is a misconfigured filter or code bug.

WDYT?

Thanks.

@droudnitsky

Copy link
Copy Markdown
ContributorAuthor

Checked the code, this is only used in CompareFilter?

Yes those are the only types of filters one can use the problematic byte array comparator with.

Actually, all the filterXXX method in Filter can throw an IOException, and I think usually throwing RuntimeException in Comparator implementation does not hurt the whole system, so maybe a possible way is to catch RuntimeException in Filter implemention, and convert it to a DoNotRetryIOException to indicate that there is a misconfigured filter or code bug.

Ah yes thank you - I see filter methods can throw IOException in the kinds of cases such as this one - "Concrete implementers can signal a failure condition in their code by throwing an {@link IOException}" - so the comparator cannot throw IOException , but filter applying the comparator would be the next best layer that can throw checked exception where its appropriate to do the catching/wrapping of runtime exceptions coming from comparator below. I think this is a much better generalized approach to handle issues at/below filter layer.

I think its safe to assume that if a runtime exception occurs during filter application its extremely likely to happen again if the same scan RPC is retried with the same filters/data. Do you think its appropriate to treat any runtime exception that occurs during any filter application as DoNotRetryIOException ? Or limit the runtime exception handling only to CompareFilter? I think its best to handle all runtime exceptions for all filters this way if it makes sense, in order to keep things consistent and cover all possible code bugs/misconfigured filters/comparators that can lead to runtime exception.

@droudnitsky

Copy link
Copy Markdown
ContributorAuthor

Looked into these two options mentioned above:

Option 1 - Treat any runtime exception that occurs specifically when applying a comparator for CompareFilter as DoNotRetryIOException - does not require a very big change since all the filters which extend CompareFilter (e.g RowFilter) use the same methods to invoke the comparator, and this limits the set of runtime exceptions that we rethrow as checked exceptions to only runtime exceptions coming from comparators being used by CompareFilter - I think it makes a lot of sense to cover all comparator runtime exceptions there - I will create a new jira/PR for handling this general CompareFilter case and keep this PR limited to adding a bounds check/useful error message to BinaryComponentComparator

Option 2 - More generally wrap any runtime exception that happens during filter application as DoNotRetryIOException - requires more extensive changes , and would change the behavior of how runtime exceptions are handled across the entire filter layer. If my assumption that if a runtime exception occurs during filter application its extremely likely to happen again if the same scan RPC is retried with the same filters/data, this makes sense, but then maybe limit this only to user scans to prevent RPC retries/give client a clean exception, and for system scans keep the same behavior and not wrap the exception since system scans don't suffer from RPC retry problem and as to not change server behavior. @Apache9 wondering what you think about this

@droudnitsky

Copy link
Copy Markdown
ContributorAuthor

Checked the code, this is only used in CompareFilter?

Yes those are the only types of filters one can use the problematic byte array comparator with.

This was not accurate - any filter which takes a ByteArrayComparable can use the problematic byte array comparator, and majority of those (5/8) filters are an extension of CompareFilter , but there are 3 outlier ColumnValue filters I found which do not extend CompareFilter:
org.apache.hadoop.hbase.filter.ColumnValueFilter
org.apache.hadoop.hbase.filter.SingleColumnValueExcludeFilter
org.apache.hadoop.hbase.filter.SingleColumnValueFilter

So those 3 filters need special handling , and the other 5 filters can be handled through CompareFilter. Used chatgpt + ClassGraph to identify all filters which take a ByteArrayComparable -

 String pkg = "org.apache.hadoop.hbase.filter"; // <-- the package to scan
List<Class<?>> hits = new ArrayList<>();
try (ScanResult scan = new ClassGraph()
.acceptPackages(pkg)
.enableClassInfo()
.scan()) {
for (ClassInfo ci : scan.getAllClasses()) {
Class<?> cls = ci.loadClass();
for (Constructor<?> ctor : cls.getDeclaredConstructors()) {
for (Class<?> pt : ctor.getParameterTypes()) {
if (pt.getName().equals("org.apache.hadoop.hbase.filter.ByteArrayComparable") // FQCN safer
|| pt.getSimpleName().equals("ByteArrayComparable")) { // simple name
hits.add(cls);
break;
}
}
}
}
}
System.out.println("matches: " + hits.size());
org.apache.hadoop.hbase.filter.ColumnValueFilter
org.apache.hadoop.hbase.filter.CompareFilter
org.apache.hadoop.hbase.filter.DependentColumnFilter
org.apache.hadoop.hbase.filter.FamilyFilter
org.apache.hadoop.hbase.filter.QualifierFilter
org.apache.hadoop.hbase.filter.RowFilter
org.apache.hadoop.hbase.filter.SingleColumnValueExcludeFilter
org.apache.hadoop.hbase.filter.SingleColumnValueFilter
org.apache.hadoop.hbase.filter.ValueFilter

@droudnitsky

Copy link
Copy Markdown
ContributorAuthor

Opened this PR to handle runtime exceptions coming from a comparator at the filter layer - https://github.com/apache/hbase/pull/7397/files

@Apache9

Copy link
Copy Markdown
Contributor

Checked the code, this is only used in CompareFilter?

Yes those are the only types of filters one can use the problematic byte array comparator with.

Actually, all the filterXXX method in Filter can throw an IOException, and I think usually throwing RuntimeException in Comparator implementation does not hurt the whole system, so maybe a possible way is to catch RuntimeException in Filter implemention, and convert it to a DoNotRetryIOException to indicate that there is a misconfigured filter or code bug.

Ah yes thank you - I see filter methods can throw IOException in the kinds of cases such as this one - "Concrete implementers can signal a failure condition in their code by throwing an {@link IOException}" - so the comparator cannot throw IOException , but filter applying the comparator would be the next best layer that can throw checked exception where its appropriate to do the catching/wrapping of runtime exceptions coming from comparator below. I think this is a much better generalized approach to handle issues at/below filter layer.

I think its safe to assume that if a runtime exception occurs during filter application its extremely likely to happen again if the same scan RPC is retried with the same filters/data. Do you think its appropriate to treat any runtime exception that occurs during any filter application as DoNotRetryIOException ? Or limit the runtime exception handling only to CompareFilter? I think its best to handle all runtime exceptions for all filters this way if it makes sense, in order to keep things consistent and cover all possible code bugs/misconfigured filters/comparators that can lead to runtime exception.

I think first we'd better only handle the offset out of bounds for BinaryComponentComparator, so maybe we can still introduce a new type of RuntimeException(maybe sub class of ArrayIndexOutOfBoundsIndex?), and in the filter implementation we only catch this exception and convert it to a DoNotRetryIOException, for others, maybe we can wrap it as a general HBaseIOException to let client retry?

WDYT?

Thanks.

@droudnitsky

Copy link
Copy Markdown
ContributorAuthor

Thank you Duo I think your proposal is good and incrementally improves things without changing too much behavior, so I plan to:

  1. Wrap any runtime exception coming from a comparator at the filter layer as a general HBaseIOException to let client retry - I will make small adjustment to https://github.com/apache/hbase/pull/7397/files - this will be able to be easily extended to treat certain exceptions as DoNotRetryIOException instead of HBaseIOException
  2. Add new a RuntimeException which sub classes ArrayIndexOutOfBoundsIndex, and for that specific exception we will wrap it as DoNotRetryIOException, this will be a small extension of the work done in 1, I will keep that in this PR.

If we find there are other comparators with such issues as BinaryComponentComparator we can handle them on a case by case basis instead of treating all runtime exceptions as non retryable.

@droudnitskydroudnitsky changed the title HBASE-29654 Handle offset out of bounds gracefully in BinaryComponentComparatorHBASE-29675 Add bounds check/descriptive OffsetOutOfBoundsException to BinaryComponentComparatorOct 19, 2025
@droudnitsky

droudnitsky commented Oct 19, 2025

Copy link
Copy Markdown
ContributorAuthor

So I will split this up into three smaller tasks / PRs which should be easier to review/can be reviewed independently of one another:

  1. HBASE-29675 / this PR - Add a more descriptive OffsetOutOfBoundsException to BinaryComponentComparator which subclasses ArrayIndexOutOfBoundsIndex - provides a much clearer exception message without changing any other behavior - simple change which can be reviewed/merged without dependency on anything else
  2. HBASE-29672 / HBASE-29672 Handle runtime comparison failures during filtering gracefully #7397 - Only for filters which take a comparator, wrap runtime exceptions from comparator layer in HBaseIOException with a clear message for the client which will be retried, this PR is also ready/independent of PR 1
  3. HBASE-29676 - Handle the specific OffsetOutOfBoundsException as DoNotRetryIOException - will be a very small PR once 1 & 2 are merged to add this special case

@Apache9 when you have the time would you be able to kindly take a look at this PR which is ready as well as #7397

@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 18sMaven dependency ordering for branch
+1 💚mvninstall4m 58smaster passed
+1 💚compile1m 50smaster passed
+1 💚javadoc1m 3smaster passed
+1 💚shadedjars7m 18sbranch has no errors when building our shaded downstream artifacts.
_ Patch Compile Tests _
+0 🆗mvndep0m 15sMaven dependency ordering for patch
+1 💚mvninstall4m 27sthe patch passed
+1 💚compile1m 45sthe patch passed
+1 💚javac1m 45sthe patch passed
+1 💚javadoc1m 1sthe patch passed
+1 💚shadedjars7m 11spatch has no errors when building our shaded downstream artifacts.
_ Other Tests _
+1 💚unit2m 46shbase-client in the patch passed.
-1 ❌unit18m 17s/patch-unit-hbase-server.txthbase-server in the patch failed.
53m 21s
SubsystemReport/Notes
DockerClientAPI=1.48 ServerAPI=1.48 base: https://ci-hbase.apache.org/job/HBase-PreCommit-GitHub-PR/job/PR-7389/3/artifact/yetus-jdk17-hadoop3-check/output/Dockerfile
GITHUB PR#7389
Optional Testsjavac javadoc unit compile shadedjars
unameLinux c50703a0f3f1 6.8.0-1024-aws #26~22.04.1-Ubuntu SMP Wed Feb 19 06:54:57 UTC 2025 x86_64 x86_64 x86_64 GNU/Linux
Build toolmaven
Personalitydev-support/hbase-personality.sh
git revisionmaster / a420fd0
Default JavaEclipse Adoptium-17.0.11+9
Test Resultshttps://ci-hbase.apache.org/job/HBase-PreCommit-GitHub-PR/job/PR-7389/3/testReport/
Max. process+thread count775 (vs. ulimit of 30000)
modulesC: hbase-client hbase-server U: .
Console outputhttps://ci-hbase.apache.org/job/HBase-PreCommit-GitHub-PR/job/PR-7389/3/console
versionsgit=2.34.1 maven=3.9.8
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 🆗reexec1m 47sDocker 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 18sMaven dependency ordering for branch
+1 💚mvninstall5m 16smaster passed
+1 💚compile5m 35smaster passed
+1 💚checkstyle1m 52smaster passed
+1 💚spotbugs3m 14smaster passed
+1 💚spotless1m 16sbranch has no errors when running spotless:check.
_ Patch Compile Tests _
+0 🆗mvndep0m 14sMaven dependency ordering for patch
+1 💚mvninstall4m 58sthe patch passed
+1 💚compile6m 28sthe patch passed
+1 💚javac6m 28sthe patch passed
+1 💚blanks0m 0sThe patch has no blanks issues.
+1 💚checkstyle1m 55sthe patch passed
+1 💚spotbugs4m 0sthe patch passed
+1 💚hadoopcheck14m 58sPatch does not cause any errors with Hadoop 3.3.6 3.4.1.
+1 💚spotless1m 1spatch has no errors when running spotless:check.
_ Other Tests _
+1 💚asflicense0m 23sThe patch does not generate ASF License warnings.
62m 9s
SubsystemReport/Notes
DockerClientAPI=1.48 ServerAPI=1.48 base: https://ci-hbase.apache.org/job/HBase-PreCommit-GitHub-PR/job/PR-7389/3/artifact/yetus-general-check/output/Dockerfile
GITHUB PR#7389
Optional Testsdupname asflicense javac spotbugs checkstyle codespell detsecrets compile hadoopcheck hbaseanti spotless
unameLinux 288ad6066b7a 6.8.0-1024-aws #26~22.04.1-Ubuntu SMP Wed Feb 19 06:54:57 UTC 2025 x86_64 x86_64 x86_64 GNU/Linux
Build toolmaven
Personalitydev-support/hbase-personality.sh
git revisionmaster / a420fd0
Default JavaEclipse Adoptium-17.0.11+9
Max. process+thread count71 (vs. ulimit of 30000)
modulesC: hbase-client hbase-server U: .
Console outputhttps://ci-hbase.apache.org/job/HBase-PreCommit-GitHub-PR/job/PR-7389/3/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.

@droudnitsky

Copy link
Copy Markdown
ContributorAuthor

Test failure is in org.apache.hadoop.hbase.master.http.TestMasterStatusUtil.testGetFragmentationInfoTurnedOn and 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

@droudnitsky@Apache-HBase@Apache9