Skip to content
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

[HUDI-3919] [UBER] Support out of order rollback blocks in AbstractHoodieLogRecordReader #5341

Closed

Conversation

suryaprasanna
Copy link
Contributor

@suryaprasanna suryaprasanna commented Apr 18, 2022

What is the purpose of the pull request

This pull request adds support for out of order rollback blocks in AbstractHoodieLogRecordReader.

Brief change log

  • Change the iteration logic in AbstractHoodieLogRecordReader to support handling various multiwriter scenarios.
  • Fix issue when rollback blocks are farther from their target blocks
  • Include test case to verify out of order rollback blocks

Verify this pull request

This pull request change is already covered by existing tests, and also modified a new test case to verify the changes.

Committer checklist

  • Has a corresponding JIRA in PR title & commit

  • Commit message is descriptive of the change

  • CI is green

  • Necessary doc changes done or have another open PR

  • For large changes, please consider breaking it into sub-tasks under an umbrella JIRA.

@suryaprasanna suryaprasanna force-pushed the merged-log-block-dev branch 2 times, most recently from e7b633c to 8cdf174 Compare April 19, 2022 17:01
@suryaprasanna suryaprasanna changed the title [HUDI-3580] [UBER] Add support for compacted log blocks [HUDI-3919] [UBER] Support out of order rollback blocks in AbstractHoodieLogRecordReader Apr 19, 2022
@nsivabalan
Copy link
Contributor

@alexeykudinkin : can you review this

@nsivabalan nsivabalan self-assigned this May 3, 2022
@@ -218,7 +221,45 @@ protected synchronized void scanInternal(Option<KeySpec> keySpecOpt) {
logFilePaths.stream().map(logFile -> new HoodieLogFile(new Path(logFile))).collect(Collectors.toList()),
readerSchema, readBlocksLazily, reverseReader, bufferSize, enableRecordLookups, keyField, internalSchema);

/**
* Traversal of log blocks from log files can be done in two directions.
Copy link
Member

Choose a reason for hiding this comment

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

Please simplify this comment and provide only the current logic.

@@ -245,97 +286,53 @@ protected synchronized void scanInternal(Option<KeySpec> keySpecOpt) {
continue;
}
}
if (logBlock.getBlockType().equals(CORRUPT_BLOCK)) {
Copy link
Member

Choose a reason for hiding this comment

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

Can we handle this too in the switch that follows? Having a common way to handle the various block types is easier to understand as per code flow.

continue;
}

// Rollback blocks contain information of instants that are failed, collect them in a set..
Copy link
Member

Choose a reason for hiding this comment

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

This comments seems more relevant to where the rollback block is being handled later.

}
}

int numBlocksRolledBack = 0;
// This is a reverse traversal on the collected data blocks.
Copy link
Member

Choose a reason for hiding this comment

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

collected data and delete blocks.

How is this reverse traversal? Isnt the for-loop a forward traversal?

@@ -839,20 +839,24 @@ public void testAvroLogRecordReaderWithRollbackTombstone(ExternalSpillableMap.Di
writer.appendBlock(dataBlock);

// Write 2
header = new HashMap<>();
Copy link
Member

Choose a reason for hiding this comment

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

header.clear() also works instead of allocating a new hashmap each time.

@@ -218,7 +221,45 @@ protected synchronized void scanInternal(Option<KeySpec> keySpecOpt) {
logFilePaths.stream().map(logFile -> new HoodieLogFile(new Path(logFile))).collect(Collectors.toList()),
readerSchema, readBlocksLazily, reverseReader, bufferSize, enableRecordLookups, keyField, internalSchema);
Copy link
Member

Choose a reason for hiding this comment

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

Lets also remove the readBlocksLazily argument as it now required to be always true.

@@ -218,7 +221,45 @@ protected synchronized void scanInternal(Option<KeySpec> keySpecOpt) {
logFilePaths.stream().map(logFile -> new HoodieLogFile(new Path(logFile))).collect(Collectors.toList()),
readerSchema, readBlocksLazily, reverseReader, bufferSize, enableRecordLookups, keyField, internalSchema);

Copy link
Member

Choose a reason for hiding this comment

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

Lets also remove the reverseReader as it is no longer supported.

@suryaprasanna
Copy link
Contributor Author

@prashantwason Addressed the review comments. Removed readBlocksLazily and reverseReader flags from AbstractHoodieLogRecordReader and log file reader classes.
Although, I have not removed following configs COMPACTION_LAZY_BLOCK_READ_ENABLE, COMPACTION_REVERSE_LOG_READ_ENABLE from HoodieWriteConfig object.

@hudi-bot
Copy link

CI report:

Bot commands @hudi-bot supports the following commands:
  • @hudi-bot run azure re-run the last Azure build

* This becomes more complicated if we have compacted blocks, which are data blocks created using log compaction.
* TODO: Include support for log compacted blocks. https://issues.apache.org/jira/browse/HUDI-3580
*
* To solve this do traversal twice.
Copy link
Contributor

Choose a reason for hiding this comment

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

I assume we will employ two traversals only when in need. i.e. when minor compactions are enabled. If not, can we avoid it and fallback to old behavior ?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Two traversals is needed to support the multiwriter scenarios where we can have rollback way away from the original block it is targeting. With minor compaction it becomes more tricky since we can have compacted blocks comprising of other compacted blocks. So, tackling the multiwriter scenarios with this PR first.

Copy link
Contributor

@nsivabalan nsivabalan left a comment

Choose a reason for hiding this comment

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

hey Surya, thanks for the patch. Wondering, for single writer scenario, do we think we can retain old behavior. only for multi-writer and minor log compactions, we might have to take the new route.

@@ -232,7 +251,7 @@ protected synchronized void scanInternal(Option<KeySpec> keySpecOpt) {
&& !HoodieTimeline.compareTimestamps(logBlock.getLogBlockHeader().get(INSTANT_TIME), HoodieTimeline.LESSER_THAN_OR_EQUALS, this.latestInstantTime
)) {
// hit a block with instant time greater than should be processed, stop processing further
break;
continue;
Copy link
Contributor

Choose a reason for hiding this comment

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

why continue. blocks greater than latest known instant time can be skipped altogether right?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Thanks for catching this. It is a mistake I am removing this.

this.enableRecordLookups = enableRecordLookups;
this.keyField = keyField;
this.internalSchema = internalSchema == null ? InternalSchema.getEmptyInternalSchema() : internalSchema;
if (this.reverseReader) {
Copy link
Contributor

Choose a reason for hiding this comment

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

why we are removing the reverse reader ? can you help me understand

Copy link
Contributor Author

Choose a reason for hiding this comment

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

My understanding is that when iterating in reverse order there is an issue when we encounter corrupt block. We cannot jump across the corrupt block since we dont have the block size stored at the end for them. So, we end up ignoring all the blocks older than the corrupt block.
That is a reason for removing the reverseReader lookup, since it cannot be handled.
It becomes more complicated when introducing log compaction. There we need to move the compacted blocks to a different slot. So, it is not straight forward traversal. So, removing this logic to reduce the complexity involved.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Please let me know, what do you think?

@@ -414,7 +411,7 @@ public void testHugeLogFileWrite() throws IOException, URISyntaxException, Inter
header.put(HoodieLogBlock.HeaderMetadataType.SCHEMA, getSimpleSchema().toString());
byte[] dataBlockContentBytes = getDataBlock(DEFAULT_DATA_BLOCK_TYPE, records, header).getContentBytes();
HoodieLogBlock.HoodieLogBlockContentLocation logBlockContentLoc = new HoodieLogBlock.HoodieLogBlockContentLocation(new Configuration(), null, 0, dataBlockContentBytes.length, 0);
HoodieDataBlock reusableDataBlock = new HoodieAvroDataBlock(null, Option.ofNullable(dataBlockContentBytes), false,
HoodieDataBlock reusableDataBlock = new HoodieAvroDataBlock(null, Option.ofNullable(dataBlockContentBytes),
Copy link
Contributor

Choose a reason for hiding this comment

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

do we have tests for test out the multi-writer scenario. i.e rollback log block is appended after few other valid log blocks? if not, can we add one.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Sure, I will add them.

@suryaprasanna
Copy link
Contributor Author

hey Surya, thanks for the patch. Wondering, for single writer scenario, do we think we can retain old behavior. only for multi-writer and minor log compactions, we might have to take the new route.

Does that mean we need to pass multiwriter enabled flag to AbstractHoodieLogRecordReader and using that flag toggle the logic between one traversal and two traversals?

@yihua yihua added priority:critical production down; pipelines stalled; Need help asap. writer-core Issues relating to core transactions/write actions labels Sep 13, 2022
@nsivabalan
Copy link
Contributor

Closing in favor of #5958

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
priority:critical production down; pipelines stalled; Need help asap. writer-core Issues relating to core transactions/write actions
Projects
Status: Done
Development

Successfully merging this pull request may close these issues.

None yet

8 participants