-
Notifications
You must be signed in to change notification settings - Fork 3.8k
release mmap immediately after merge indexes #6699
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
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -798,6 +798,40 @@ public File persist( | |
| ); | ||
| } | ||
|
|
||
| @Override | ||
| public File mergeSegmentFiles( | ||
| File[] segmentFiles, | ||
| boolean rollup, | ||
| AggregatorFactory[] metricAggs, | ||
| File outDir, | ||
| IndexSpec indexSpec, | ||
| @Nullable SegmentWriteOutMediumFactory segmentWriteOutMediumFactory | ||
| ) throws IOException | ||
| { | ||
| List<QueryableIndex> indexes = Lists.transform(Arrays.asList(segmentFiles), input -> { | ||
| try { | ||
| return indexIO.loadIndex(input); | ||
| } | ||
| catch (IOException e) { | ||
| throw new ISE(e, "load index %s failed!?", input.getAbsolutePath()); | ||
| } | ||
| }); | ||
|
|
||
| try { | ||
| return mergeQueryableIndex(indexes, rollup, metricAggs, outDir, indexSpec, new BaseProgressIndicator(), segmentWriteOutMediumFactory); | ||
| } | ||
| finally { | ||
| for (QueryableIndex index : indexes) { | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Use Closer to close many objects. |
||
| try { | ||
| index.close(); | ||
| } | ||
| catch (Exception e) { | ||
| log.warn(e, "failed to close index when merge"); | ||
| } | ||
| } | ||
| } | ||
| } | ||
|
|
||
| @Override | ||
| public File mergeQueryableIndex( | ||
| List<QueryableIndex> indexes, | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -82,6 +82,7 @@ | |
| import javax.annotation.Nullable; | ||
| import java.io.Closeable; | ||
| import java.io.File; | ||
| import java.io.FilenameFilter; | ||
| import java.io.IOException; | ||
| import java.nio.channels.FileChannel; | ||
| import java.nio.channels.FileLock; | ||
|
|
@@ -716,8 +717,20 @@ private DataSegment mergeAndPush(final SegmentIdentifier identifier, final Sink | |
| closer.register(segmentAndCloseable.rhs); | ||
| } | ||
|
|
||
| mergedFile = indexMerger.mergeQueryableIndex( | ||
| indexes, | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @kaijianding I agree with @clintropolis, maybe this PR is incomplete, and you planned to free those
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The QueryableIndexes are always new objects loaded from file, and closed when IndexMerger.mergeSegmentFiles() is done. This behavior frees the mmap usage increased during the merge process. This PR separates the QueryableIndex used by query and used by merge process, then we can close the QueryableIndexes used by merge process to release mmap, and leave the QueryableIndexes used by query untouched. This PR is verified in my product environment, it indeed controls the mmap usage
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Again, this is beyond me how just creating (and later closing) some new objects without revoking creating any other objects could improve anything, unless any code around here is lazy, that doesn't seem to be the case.
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. If this is about avoiding refreshing some memory mapped files in memory (although I don't see the mechanism how it helps either), at very least the surrounding
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Will address these comments. The mechanism is like this:
Wish I explain the mechanism clearly. Though this PR is very simple, it indeed help on the mmap usage. There is an alternative solution to recycle mmap: load the mergedFile as QueryableIndex and swap all hydrants' small QueryableIndexes with this single big QueryableIndex to the Sink. and close all hydrants' small QueryableIndexes and delete all hydrant segments to release mmap (the mmap usage is already increased after merge process done)
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. We are standing at the same page now, your understanding is totally correct. The worst case, the complete set of data is queried (all rows/all columns), the exact same footprint is as the merge required in the first place. Back to this PR, it releases mmap after merge is done, as a result, the mmap usage is under control, it won't grow to a huge number when handoff is slow to let the process be killed by yarn.
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Cool, glad we could sort out what is going on, and apologies it took so long for me to understand what the point was 👍 Since this doesn't really seem to particularly have any negative consequences, and since it is doing something useful in some cases at least, I'll have another look at this PR. Also in jdk 10+, I believe it will be possible to open files with
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @clintropolis it's possible to use
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. so my understanding is that, this is useful in case when, on realtime process, very small subset of columns/data in the intermediate persisted segments is being read from queries. "swap after merge" is further useful to reduce inode count, but the swap would be tricky to implement as there might be queries underway on the indexes just merged. I think O_DIRECT is whole other beast and would require significant performance regression testing in this case.
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Hmm, this got marked stale, but i also think it would be ok to merge if @leventov's comments and some additional javadocs explaining what was going on and linking to this discussion were added. Do you have any interest in finishing this @kaijianding? Apologies that it got stalled in review for so long. |
||
| File[] hydrantDirs = persistDir.listFiles( | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
|
||
| new FilenameFilter() | ||
| { | ||
| @Override | ||
| public boolean accept(File dir, String fileName) | ||
| { | ||
| // To avoid reading and listing of "merged" dir | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The comment is unclear, please elaborate
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. "hydrantFiles" maybe? or those files are actually directories? Then IndexMerger.mergeSegmentFiles() should be called mergeSegmentDirs(). |
||
| return !(Ints.tryParse(fileName) == null); | ||
| } | ||
| } | ||
| ); | ||
|
|
||
| mergedFile = indexMerger.mergeSegmentFiles( | ||
| hydrantDirs, | ||
| schema.getGranularitySpec().isRollup(), | ||
| schema.getAggregators(), | ||
| mergedTarget, | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -430,8 +430,20 @@ public void doRun() | |
| closer.register(segmentAndCloseable.rhs); | ||
| } | ||
|
|
||
| mergedFile = indexMerger.mergeQueryableIndex( | ||
| indexes, | ||
| File[] hydrantDirs = persistDir.listFiles( | ||
| new FilenameFilter() | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Consider extracting the repeated fragment as a method |
||
| { | ||
| @Override | ||
| public boolean accept(File dir, String fileName) | ||
| { | ||
| // To avoid reading and listing of "merged" dir | ||
| return !(Ints.tryParse(fileName) == null); | ||
| } | ||
| } | ||
| ); | ||
|
|
||
| mergedFile = indexMerger.mergeSegmentFiles( | ||
| hydrantDirs, | ||
| schema.getGranularitySpec().isRollup(), | ||
| schema.getAggregators(), | ||
| mergedTarget, | ||
|
|
||
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.
Please add javadoc with the rationale for this method, and the difference from the most closely related one,
mergeQueryableIndex().