Github user mateiz commented on a diff in the pull request:
https://github.com/apache/spark/pull/1165#discussion_r15331032
--- Diff: core/src/main/scala/org/apache/spark/storage/MemoryStore.scala ---
@@ -141,6 +189,104 @@ private class MemoryStore(blockManager: BlockManager,
maxMemory: Long)
}
/**
+ * Unroll the given block in memory safely.
+ *
+ * The safety of this operation refers to avoiding potential OOM
exceptions caused by
+ * unrolling the entirety of the block in memory at once. This is
achieved by periodically
+ * checking whether the memory restrictions for unrolling blocks are
still satisfied,
+ * stopping immediately if not. This check is a safeguard against the
scenario in which
+ * there is not enough free memory to accommodate the entirety of a
single block.
+ *
+ * This method returns either an array with the contents of the entire
block or an iterator
+ * containing the values of the block (if the array would have exceeded
available memory).
+ */
+ def unrollSafely(
+ blockId: BlockId,
+ values: Iterator[Any],
+ droppedBlocks: ArrayBuffer[(BlockId, BlockStatus)])
+ : Either[Array[Any], Iterator[Any]] = {
+
+ // Number of elements unrolled so far
+ var elementsUnrolled = 0
+ // Whether there is still enough memory for us to continue unrolling
this block
+ var keepUnrolling = true
+ // Initial per-thread memory to request for unrolling blocks (bytes).
Exposed for testing.
+ val initialMemoryThreshold =
conf.getLong("spark.storage.unrollMemoryThreshold", 1024 * 1024)
+ // How often to check whether we need to request more memory. Exposed
for testing.
+ val memoryCheckPeriod =
conf.getLong("spark.storage.unrollCheckPeriod", 16)
+ // Memory currently reserved by this thread (bytes)
+ var memoryThreshold = initialMemoryThreshold
+ // Memory to request as a multiple of current vector size
+ val memoryGrowthFactor = 1.5
+
+ val threadId = Thread.currentThread().getId
+ val unrollMemoryMap = SparkEnv.get.unrollMemoryMap
+ var vector = new SizeTrackingVector[Any]
+
+ // Request memory for our vector and return whether request is granted.
+ // This involves synchronizing across all threads and can be expensive
if called frequently.
+ def requestMemory(memoryToRequest: Long): Boolean = {
+ unrollMemoryMap.synchronized {
--- End diff --
Can we access unrollMemoryMap using putLock as well? Otherwise there's a
risk of deadlocks if some methods grab the locks in one order, and some grab
them in the other order. I don't think that happens here but it would be better
to just remove the risk.
---
If your project is set up for it, you can reply to this email and have your
reply appear on GitHub as well. If your project does not have this feature
enabled and wishes so, or if the feature is enabled but not working, please
contact infrastructure at [email protected] or file a JIRA ticket
with INFRA.
---