tomvanbussel opened a new pull request #29910:
URL: https://github.com/apache/spark/pull/29910


   Backport of #29785 to Spark 2.4
   
   ### What changes were proposed in this pull request?
   
   This PR changes `UnsafeExternalSorter` to no longer allocate any memory 
while spilling. In particular it removes the allocation of a new pointer array 
in `UnsafeInMemorySorter`. Instead the new pointer array is allocated whenever 
the next record is inserted into the sorter.
   
   ### Why are the changes needed?
   
   Without this change the `UnsafeExternalSorter` could throw an OOM while 
spilling. The following sequence of events would have triggered an OOM:
   
   1. `UnsafeExternalSorter` runs out of space in its pointer array and 
attempts to allocate a new large array to replace the old one.
   2. `TaskMemoryManager` tries to allocate the memory backing the new large 
array using `MemoryManager`, but `MemoryManager` is only willing to return most 
but not all of the memory requested.
   3. `TaskMemoryManager` asks `UnsafeExternalSorter` to spill, which causes 
`UnsafeExternalSorter` to spill the current run to disk, to free its record 
pages and to reset its `UnsafeInMemorySorter`.
   4. `UnsafeInMemorySorter` frees the old pointer array, and tries to allocate 
a new small pointer array.
   5. `TaskMemoryManager` tries to allocate the memory backing the small array 
using `MemoryManager`, but `MemoryManager` is unwilling to give it any memory, 
as the `TaskMemoryManager` is still holding on to the memory it got for the new 
large array.
   6. `TaskMemoryManager` again asks `UnsafeExternalSorter` to spill, but this 
time there is nothing to spill.
   7. `UnsafeInMemorySorter` receives less memory than it requested, and causes 
a `SparkOutOfMemoryError` to be thrown, which causes the current task to fail.
   
   With the changes in the PR the following will happen instead:
   
   1. `UnsafeExternalSorter` runs out of space in its pointer array and 
attempts to allocate a new large array to replace the old one.
   2. `TaskMemoryManager` tries to allocate the memory backing the new large 
array using `MemoryManager`, but `MemoryManager` is only willing to return most 
but not all of the memory requested.
   3. `TaskMemoryManager` asks `UnsafeExternalSorter` to spill, which causes 
`UnsafeExternalSorter` to spill the current run to disk, to free its record 
pages and to reset its `UnsafeInMemorySorter`.
   4. `UnsafeInMemorySorter` frees the old pointer array.
   5. `TaskMemoryManager` returns control to 
`UnsafeExternalSorter.growPointerArrayIfNecessary` (either by returning the the 
new large array or by throwing a `SparkOutOfMemoryError`).
   6. `UnsafeExternalSorter` either frees the new large array or it ignores the 
`SparkOutOfMemoryError` depending on what happened in the previous step.
   7. `UnsafeExternalSorter` successfully allocates a new small pointer array 
and operation continues as normal.
   
   ### Does this PR introduce _any_ user-facing change?
   
   No
   
   ### How was this patch tested?
   
   Tests were added in `UnsafeExternalSorterSuite` and 
`UnsafeInMemorySorterSuite`.


----------------------------------------------------------------
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

For queries about this service, please contact Infrastructure at:
[email protected]



---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]

Reply via email to