Skip to content

Conversation

@kadirozde
Copy link
Contributor

IndexTool uses UngroupedAggregateRegionObserver and IndexRegionObserver coprocessors to build an index table on the server side. UngroupedAggregateRegionObserver scans a data table regions and reconstructs the put mutations for each scanned row and applies them on the data table with the REPLAY_INDEX_REBUILD_WRITES option. These mutations are then only used to rebuild index rows by IndexRegionObserver. This PR adds an inline verification feature to UngroupedAggregateRegionObserver which reads these index rows and verifies their content to make sure they are build correctly.

@kadirozde kadirozde requested a review from gjacoby126 January 7, 2020 08:42
Copy link
Contributor

@gjacoby126 gjacoby126 left a comment

Choose a reason for hiding this comment

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

Looks good, mostly nits.

Copy link
Contributor

@priyankporwal priyankporwal left a comment

Choose a reason for hiding this comment

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

LGTM. Thanks @kadirozde!

} else if (Bytes.compareTo(cell.getQualifierArray(), cell.getQualifierOffset(), cell.getQualifierLength(),
IndexTool.ERROR_MESSAGE_BYTES, 0, IndexTool.ERROR_MESSAGE_BYTES.length) == 0) {
errorMessageCell = cell;

Copy link
Contributor

Choose a reason for hiding this comment

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

nit: extra line

null, -1, true, false);
// The index tool output table should report that there is a missing index row
Cell cell = getErrorMessageFromIndexToolOutputTable(conn, dataTableFullName, "_IDX_" + dataTableFullName);
byte[] expectedValueBytes = Bytes.toBytes("Missing index rows - Expected: 1 Actual: 0");
Copy link
Contributor

Choose a reason for hiding this comment

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

Ideally could be columns for expected rows and actual rows and could check that, but that can be future work.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I have a test for the mismatch on the column value in the following test for the only-verify option. It is almost impossible to generate missing columns or wrong column values with the verify option since index rows are corrected during rebuild

IndexRegionObserver.setIgnoreIndexRebuildForTesting(false);
Admin admin = conn.unwrap(PhoenixConnection.class).getQueryServices().getAdmin();
TableName indexToolOutputTable = TableName.valueOf(IndexTool.OUTPUT_TABLE_NAME_BYTES);
admin.disableTable(indexToolOutputTable);
Copy link
Contributor

Choose a reason for hiding this comment

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

disabling / dropping takes time, and this test suite uses its own cluster -- any reason we need to drop?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Having an empty output table to start with simplifies the tests.

if (directApi) {
args.add("-direct");
}
if (verify) {
Copy link
Contributor

Choose a reason for hiding this comment

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

what if I pass in verify=true, onlyVerify=true? Shouldn't there be an exception?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I can add an assert here.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

After more thinking on this, I should not add an assert here as it is acceptable for the implementation to set both at this moment

String errorMsg) {
try (Table hTable = ServerUtil.ConnectionFactory.getConnection(ServerUtil.ConnectionType.INDEX_WRITER_CONNECTION,
env).getTable(TableName.valueOf(IndexTool.OUTPUT_TABLE_NAME))) {
byte[] rowKey = new byte[Long.BYTES];
Copy link
Contributor

Choose a reason for hiding this comment

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

Shouldn't the row key also have the table being rebuilt? What if rebuilds are going on simultaneously on two different tables? Unlikely they'd be exact same ms, but possible.

Also, if the row key is just a timestamp, you'll hotspot (though hopefully this table will be low volume)

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Yes, it is possible but very unlikely to get the same timestamp especially when IndexTool runs are not automated. I intentionally chose a very simple row key to make the table human readable and easy to query using the HBase shell. There will be one table row for each mismatch row. The excepted volume will be low. I suggest not to optimize this for very unlikely scenarios.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I thought about this again. Because of the "only-verify" option where the tool reports every mismatch row instead of just one, I need to make the row key unique. So, I am thinking about including the data row key in the in the row key for the output table.

byte[] qualifier = CellUtil.cloneQualifier(expectedCell);
Cell actualCell = indexRow.getColumnLatestCell(family, qualifier);
if (actualCell == null) {
exceptionMessage = "Index verify failed - Missing cell " + indexHTable.getName();
Copy link
Contributor

Choose a reason for hiding this comment

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

nit: some extract methods around here would make this easier to read. maybe one function for cell not found and another for checking all the columns?

scan.setTimeRange(0, scn);
scan.setAttribute(BaseScannerRegionObserver.INDEX_REBUILD_PAGING, TRUE_BYTES);
if (getVerifyIndex(configuration)) {
scan.setAttribute(BaseScannerRegionObserver.INDEX_REBUILD_VERIFY, TRUE_BYTES);
Copy link
Contributor

Choose a reason for hiding this comment

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

what happens if both set?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Verify wins


if (verify) {
PhoenixConfigurationUtil.setVerifyIndex(configuration, true);
}
Copy link
Contributor

Choose a reason for hiding this comment

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

what if both set?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

if both "verify" and "only-verify" are set then as the help for the command says the only-verify option will be ignored: "To verify every data table row has the corresponding index row with the correct content (without building the index table). If the verify option is set then the only-verify option will be ignored");

}
HTableDescriptor tableDescriptor = new
HTableDescriptor(TableName.valueOf(OUTPUT_TABLE_NAME));
tableDescriptor.setValue("DISABLE_TABLE_SOR", "true");
Copy link
Contributor

Choose a reason for hiding this comment

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

Not a property that open source HBase or Phoenix recognizes.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I will remove it in the open source versions.

int length = Long.BYTES + dataRowKey.length;
rowKey = new byte[length];
Bytes.putLong(rowKey, 0, scan.getTimeRange().getMax());
for (int i = Long.BYTES, j = 0; i < length; i++, j++) {
Copy link
Contributor

Choose a reason for hiding this comment

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

Nit: System.arraycopy() might perform better than byte-by-byte copy. I assume it's java equivalent of memcpy.

Copy link
Contributor

Choose a reason for hiding this comment

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

See also Bytes.putBytes

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Both suggestions work. I will go with Bytes.putBytes as it makes the code more readable.

Copy link
Contributor

@gjacoby126 gjacoby126 left a comment

Choose a reason for hiding this comment

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

+1, thanks @kadirozde

@kadirozde kadirozde closed this Jan 13, 2020
@kadirozde kadirozde deleted the 5658 branch January 13, 2020 20:31
jpisaac pushed a commit to jpisaac/phoenix that referenced this pull request Jun 10, 2022
…r crash (apache#672)

* PHOENIX-6504 Ban thirdparty guava imports to prevent RegionServer crash

* Remove thirdparty guava from phoenix-pherf
Sign up for free to 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.

5 participants