-
Notifications
You must be signed in to change notification settings - Fork 751
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
[GOBBLIN-1891] Create selftuning buffered ORC writer #3751
Conversation
Codecov Report
@@ Coverage Diff @@
## master #3751 +/- ##
============================================
+ Coverage 47.08% 47.15% +0.07%
- Complexity 10864 10895 +31
============================================
Files 2147 2148 +1
Lines 84825 85032 +207
Branches 9412 9440 +28
============================================
+ Hits 39936 40095 +159
- Misses 41264 41294 +30
- Partials 3625 3643 +18
... and 37 files with indirect coverage changes 📣 We’re building smart automated test selection to slash your CI/CD build times. Learn more |
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.
Initial pass and some early feedback
...dules/gobblin-orc/src/main/java/org/apache/gobblin/writer/GenericRecordToOrcValueWriter.java
Outdated
Show resolved
Hide resolved
gobblin-modules/gobblin-orc/src/main/java/org/apache/gobblin/writer/GobblinBaseOrcWriter.java
Outdated
Show resolved
Hide resolved
gobblin-modules/gobblin-orc/src/main/java/org/apache/gobblin/writer/GobblinBaseOrcWriter.java
Outdated
Show resolved
Hide resolved
gobblin-modules/gobblin-orc/src/main/java/org/apache/gobblin/writer/GobblinBaseOrcWriter.java
Outdated
Show resolved
Hide resolved
gobblin-modules/gobblin-orc/src/main/java/org/apache/gobblin/writer/GobblinBaseOrcWriter.java
Outdated
Show resolved
Hide resolved
gobblin-modules/gobblin-orc/src/main/java/org/apache/gobblin/writer/GobblinBaseOrcWriter.java
Outdated
Show resolved
Hide resolved
gobblin-modules/gobblin-orc/src/main/java/org/apache/gobblin/writer/GobblinBaseOrcWriter.java
Outdated
Show resolved
Hide resolved
gobblin-modules/gobblin-orc/src/main/java/org/apache/gobblin/writer/GobblinBaseOrcWriter.java
Outdated
Show resolved
Hide resolved
gobblin-modules/gobblin-orc/src/main/java/org/apache/gobblin/writer/GobblinBaseOrcWriter.java
Outdated
Show resolved
Hide resolved
d2fc245
to
f9c0fde
Compare
gobblin-modules/gobblin-orc/src/main/java/org/apache/gobblin/writer/GobblinBaseOrcWriter.java
Outdated
Show resolved
Hide resolved
if (this.orcFileWriter == null) { | ||
initializeOrcFileWriter(); | ||
} | ||
this.flush(); |
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.
we don't need to flush when we just initialize the writer?
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.
If the rowbatch is empty then it will ignore flushing, mostly just because I reuse the tuneBatchSize both during first initialization and every tune frequency
this.flush(); | ||
this.rowBatch.ensureSize(this.batchSize); | ||
} | ||
if (this.orcFileWriter == null) { |
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.
duplicate code?
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.
The issue is that because I don't lazy init the writer during flush, I need to initialize the writer during the first tune. I need to initialize before the first flush essentially, maybe this makes more sense in the flush function
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.
Oh I guess you can remove line 280-282 and in change 286 to
if (this.orcFileWriter == null) {
initializeOrcFileWriter();
} else{
this.flush();
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 think to handle every edge case (in scenarios where low vol writers get closed before first tune) will always lazy init instead inside of the flush function, this way won't need to dupe the logic.
...n-modules/gobblin-orc/src/main/java/org/apache/gobblin/writer/OrcConverterMemoryManager.java
Outdated
Show resolved
Hide resolved
… properties, fix some more bugs
…o be more accurate to what it's for
long converterBufferColSize = 0; | ||
if (col instanceof ListColumnVector) { | ||
ListColumnVector listColumnVector = (ListColumnVector) col; | ||
converterBufferColSize += listColumnVector.child.isNull.length; |
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.
Still have some question here, likely I miss understand something:
- Is listColumnVector.child.isNull.length means the length of the current list? I'm confused by the "child" here
- on line 52, why it's + by not *?
- Seems like you try to get the length here, but don't we interested in the memory size here?
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.
- It should be the length of the current list,
child
refers to the element within the columnVector. Since this is columnar need to think of the child schema, how many of that child is within this column. Ah good point, I guess this will catch scenarios of a list within a list, will modify.Originally I thought we had to address (2) but the columnar nature of ORC means that you just need to ensure that only that particular column's child (being the element in the array) can hold all the items in the array's length. So addition is fine here.- I wanted to measure the space filled by the resizes, which is primarily represented by an array size increase of a boolean array. I'm not sure if Java measures null in an array with the size of the expected object * length. Edited: Okay after looking into the library implementation most of the primitive ORC types use a null value that maps to a default java primitive type, so there is a size associated which can be roughly estimated. Will use that as the benchmark and it should lead to improvements in accuracy
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.
one small comment
...n-modules/gobblin-orc/src/main/java/org/apache/gobblin/writer/OrcConverterMemoryManager.java
Show resolved
Hide resolved
...n-modules/gobblin-orc/src/main/java/org/apache/gobblin/writer/OrcConverterMemoryManager.java
Outdated
Show resolved
Hide resolved
e45c7fb
to
0a637eb
Compare
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.
+1 thanks for the work here!
Dear Gobblin maintainers,
Please accept this PR. I understand that it will not be reviewed until I have checked off all the steps below!
JIRA
Description
The current ORCWriter which converts Avro to ORC frequently runs into OOM issues on large schemas. This is theorized to be partially due to the way that the converter allocates memory for large lists and maps, it uses a resize algorithm that multiplies the last array size by 3. This can lead to a lot of extra space, along with the large records already stored within the buffer and the file writer, will cause memory issues.
This PR introduces a few components/ideas to manage memory:
GobblinBaseOrcWriter
should account for the memory available in the JVM (which is available through the Java runtime APIs) minus the size of the records that can be stored in the underlying file writer and the size of the Avro to ORC converter due to resizes. It should then divide this number by the average size of a recordFuture work/ Improvements around this:
smartResize
algorithm which should effectively be a bounded exponential decay function, so that records that are marginally larger than the previous calculated records do not cause the converter to balloon out of control. This should improve performance.Tests
Unit tests around memory manager
Tested with Kafka ingestion pipelines
Commits