-
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
IndexOutOfBoundsException when accessing partition where the column was deleted #3731
base: trunk
Are you sure you want to change the base?
Conversation
sunil9977
commented
Dec 9, 2024
- Added a null check for foundValue before processing it.
- Added appropriate test case for the same.
Please add a Jira ticket to the patch. |
Jira ticket - https://issues.apache.org/jira/browse/CASSANDRA-20108 |
test/unit/org/apache/cassandra/cql3/validation/operations/SelectTest.java
Show resolved
Hide resolved
@@ -705,7 +705,7 @@ public boolean isSatisfiedBy(TableMetadata metadata, DecoratedKey partitionKey, | |||
if (column.type.isCounter()) | |||
{ | |||
ByteBuffer foundValue = getValue(metadata, partitionKey, row); | |||
if (foundValue == null) | |||
if (foundValue == null || foundValue.remaining() == 0) |
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.
this patch is missing org.apache.cassandra.db.filter.RowFilter.MapElementExpression#isSatisfiedBy
as well.
Rather than updating every code path, why not push this logic into getValue
?
In getValue
we can do
default:
Cell<?> cell = row.getCell(column);
if (cell == null) return null;
ByteBuffer bb = cell.buffer();
return bb.hasRemaining() ? bb : null;
with that one change all 4 code paths impacted are now seeing null
(which they already handle)
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 am also curious why we have empty bytes rather than null
in this case... this implies that the pattern of
Cell<?> cell = row.getCell(column);
return cell == null ? null : cell.buffer();
is unsafe and has always been wrong... yet this pattern is common...
$ grep -r 'cell == null ? null : cell.buffer()' src/
src//java/org/apache/cassandra/db/marshal/MapType.java: return cell == null ? null : cell.buffer();
src//java/org/apache/cassandra/db/marshal/ListType.java: return cell == null ? null : cell.buffer();
src//java/org/apache/cassandra/db/marshal/UserType.java: return cell == null ? null : cell.buffer();
src//java/org/apache/cassandra/db/filter/RowFilter.java: return cell == null ? null : cell.buffer();
src//java/org/apache/cassandra/index/internal/CassandraIndex.java: cell == null ? null : cell.buffer()
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.
looking at callers of org.apache.cassandra.db.rows.Cell#buffer
there are 62+ code paths that are similar pattern...
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.
looked into it... we do org.apache.cassandra.cql3.terms.Constants.Deleter
when we delete a column. This is defined as
builder.addCell(BufferCell.tombstone(column, timestamp, nowInSec, path));
which is
public static BufferCell tombstone(ColumnMetadata column, long timestamp, long nowInSec, CellPath path)
{
return new BufferCell(column, timestamp, NO_TTL, nowInSec, ByteBufferUtil.EMPTY_BYTE_BUFFER, path);
}
so delete column is defined as write empty bytes... that explains the empty bytes at least
@@ -529,7 +530,7 @@ protected ByteBuffer getValue(TableMetadata metadata, DecoratedKey partitionKey, | |||
return row.clustering().bufferAt(column.position()); | |||
default: | |||
Cell<?> cell = row.getCell(column); | |||
return cell == null ? null : cell.buffer(); | |||
return Cell.getValidCellBuffer(cell, nowInSeconds()); |
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.
sorry, been bad about getting back to this... im not a fan of nowInSeconds
being called here as we should really do this once per query... im trying to reviewer the higher level calling code to see if there is something we can do to improve this...
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.
org.apache.cassandra.db.filter.RowFilter#filter(org.apache.cassandra.schema.TableMetadata, long)
we are passed in a nowInSeconds
! It looks like we can refactor the caller to pass this value in, that way we don't call nowInSeconds
for every cell (this can become a problem)
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.
once we plumb nowInSeconds
from the caller through here, then I am +1 to this patch
I applied the patch to the branch that found the problem, and applied the feedback I gave and the test that found the issue is passing now! Here is the modified version of this PR with my feedback applied
|