{"action":"created","issue":{"url":"https://api.github.com/repos/apache/spark/issues/10024","repository_url":"https://api.github.com/repos/apache/spark","labels_url":"https://api.github.com/repos/apache/spark/issues/10024/labels{/name}","comments_url":"https://api.github.com/repos/apache/spark/issues/10024/comments","events_url":"https://api.github.com/repos/apache/spark/issues/10024/events","html_url":"https://github.com/apache/spark/pull/10024","id":119284022,"number":10024,"title":"[SPARK-4452][Core]Shuffle data structures can starve others on the same thread for memory","user":{"login":"lianhuiwang","id":545478,"avatar_url":"https://avatars.githubusercontent.com/u/545478?v=3","gravatar_id":"","url":"https://api.github.com/users/lianhuiwang","html_url":"https://github.com/lianhuiwang","followers_url":"https://api.github.com/users/lianhuiwang/followers","following_url":"https://api.github.com/users/lianhuiwang/following{/other_user}","gists_url":"https://api.github.com/users/lianhuiwang/gists{/gist_id}","starred_url":"https://api.github.com/users/lianhuiwang/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/lianhuiwang/subscriptions","organizations_url":"https://api.github.com/users/lianhuiwang/orgs","repos_url":"https://api.github.com/users/lianhuiwang/repos","events_url":"https://api.github.com/users/lianhuiwang/events{/privacy}","received_events_url":"https://api.github.com/users/lianhuiwang/received_events","type":"User","site_admin":false},"labels":[],"state":"open","locked":false,"assignee":null,"milestone":null,"comments":6,"created_at":"2015-11-28T10:13:25Z","updated_at":"2016-04-18T15:14:58Z","closed_at":null,"pull_request":{"url":"https://api.github.com/repos/apache/spark/pulls/10024","html_url":"https://github.com/apache/spark/pull/10024","diff_url":"https://github.com/apache/spark/pull/10024.diff","patch_url":"https://github.com/apache/spark/pull/10024.patch"},"body":"In #9241 It implemented a mechanism to call spill() on those SQL operators that support spilling if there is not enough memory for execution. \r\nBut ExternalSorter and AppendOnlyMap in Spark core are not worked. So this PR make them benefit from #9241. Now when there is not enough memory for execution, it can get memory by spilling ExternalSorter and AppendOnlyMap in Spark core.\r\n@davies @andrewor14 @JoshRosen  Can you take a look at this PR? thanks."},"comment":{"url":"https://api.github.com/repos/apache/spark/issues/comments/211424963","html_url":"https://github.com/apache/spark/pull/10024#issuecomment-211424963","issue_url":"https://api.github.com/repos/apache/spark/issues/10024","id":211424963,"user":{"login":"squito","id":71240,"avatar_url":"https://avatars.githubusercontent.com/u/71240?v=3","gravatar_id":"","url":"https://api.github.com/users/squito","html_url":"https://github.com/squito","followers_url":"https://api.github.com/users/squito/followers","following_url":"https://api.github.com/users/squito/following{/other_user}","gists_url":"https://api.github.com/users/squito/gists{/gist_id}","starred_url":"https://api.github.com/users/squito/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/squito/subscriptions","organizations_url":"https://api.github.com/users/squito/orgs","repos_url":"https://api.github.com/users/squito/repos","events_url":"https://api.github.com/users/squito/events{/privacy}","received_events_url":"https://api.github.com/users/squito/received_events","type":"User","site_admin":false},"created_at":"2016-04-18T15:14:58Z","updated_at":"2016-04-18T15:14:58Z","body":"Hi @lianhuiwang thanks for submitting this patch.\r\n\r\nI just have a really high-level question first.  If I understand how this works correctly, the idea is that:\r\n1) as data is added to `Spillable`s, they work exactly the same as before at first, so after all records have been inserted, they've still got a bunch of data in memory.\r\n2) When an iterator is requested, as before its still an iterator that merges data has been spilled to disk, and data that is still-in-memory\r\n3) If the `TaskMemoryManager` requests more memory while that iterator is in flight, then the `Spillable`s look at the position of the current iterator over the in-memory data, and spill only the _remaining_ data to disk\r\n4) The `Spillable` then free the memory to the `TaskMemoryManager`, and have the in-flight iterator switch to using the new spill file.\r\n\r\nIs this a correct understanding?  If so, this seems to hinge on one key assumption: that the `Spillable`s are never iterated over multiple times.  If they were iterated multiple times, then the second iteration would be incorrect -- some of the initial data in the in-memory structure would be lost on the second iteration.\r\n\r\nI think this assumption is sound -- it is implied by \"destructive\"SortedIterator in the internals, though I think the actual wrapping `Spillable`s might allow multiple iteration now.  I've been trying to think of a case where this assumption would be violated but cant' come up with anything.  (If the same shuffle data is read multiple times in different tasks, the work on the shuffle-read side is simply repeated each time, I'm pretty sure there isn't any sharing).  But nonetheless if that is the case, I think this deserves both a lot of comments explaining how this works, assertions which make sure this assumption is not violated, and a number of tests.\r\n\r\nFWIW, I started down the path of writing something similar with*out* that assumption -- when a spill was requested on an in-flight iterator, then the *entire* in-memory structure would get spilled to disk, and the in-flight iterator would switch to the spilled data, and advance to the same location in the spilled data that it was on the in-memory data.  This was pretty convoluted, and as I started writing tests I realized there were corner cases that needed work.  So I decided to submit the simpler change instead.  It seems much easier to it your way.    I do have some test which I think I can add as well -- lemme dig those up and send them later today."}}