{"action":"created","comment":{"url":"https://api.github.com/repos/apache/spark/pulls/comments/83071819","pull_request_review_id":3936646,"id":83071819,"diff_hunk":"@@ -55,10 +55,16 @@ class TableFileCatalog(\n \n   override def listFiles(filters: Seq[Expression]): Seq[Partition] = partitionSchema match {\n     case Some(partitionSchema) =>\n-      externalCatalog.listPartitionsByFilter(db, table, filters).flatMap {\n+      val catalogTablePartitions = externalCatalog.listPartitionsByFilter(db, table, filters)","path":"sql/core/src/main/scala/org/apache/spark/sql/execution/datasources/TableFileCatalog.scala","position":null,"original_position":5,"commit_id":"14ca61266290ad8b4231ab0355cb0e848f2acfbe","original_commit_id":"14ca61266290ad8b4231ab0355cb0e848f2acfbe","user":{"login":"ericl","id":14922,"avatar_url":"https://avatars.githubusercontent.com/u/14922?v=3","gravatar_id":"","url":"https://api.github.com/users/ericl","html_url":"https://github.com/ericl","followers_url":"https://api.github.com/users/ericl/followers","following_url":"https://api.github.com/users/ericl/following{/other_user}","gists_url":"https://api.github.com/users/ericl/gists{/gist_id}","starred_url":"https://api.github.com/users/ericl/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/ericl/subscriptions","organizations_url":"https://api.github.com/users/ericl/orgs","repos_url":"https://api.github.com/users/ericl/repos","events_url":"https://api.github.com/users/ericl/events{/privacy}","received_events_url":"https://api.github.com/users/ericl/received_events","type":"User","site_admin":false},"body":"Would filterPartitions(filters).listFiles(Nil) work here?","created_at":"2016-10-12T18:42:25Z","updated_at":"2016-10-12T18:42:25Z","html_url":"https://github.com/apache/spark/pull/14690#discussion_r83071819","pull_request_url":"https://api.github.com/repos/apache/spark/pulls/14690","_links":{"self":{"href":"https://api.github.com/repos/apache/spark/pulls/comments/83071819"},"html":{"href":"https://github.com/apache/spark/pull/14690#discussion_r83071819"},"pull_request":{"href":"https://api.github.com/repos/apache/spark/pulls/14690"}}},"pull_request":{"url":"https://api.github.com/repos/apache/spark/pulls/14690","id":81690220,"html_url":"https://github.com/apache/spark/pull/14690","diff_url":"https://github.com/apache/spark/pull/14690.diff","patch_url":"https://github.com/apache/spark/pull/14690.patch","issue_url":"https://api.github.com/repos/apache/spark/issues/14690","number":14690,"state":"open","locked":false,"title":"[SPARK-16980][SQL] Load only catalog table partition metadata required to answer a query","user":{"login":"mallman","id":833693,"avatar_url":"https://avatars.githubusercontent.com/u/833693?v=3","gravatar_id":"","url":"https://api.github.com/users/mallman","html_url":"https://github.com/mallman","followers_url":"https://api.github.com/users/mallman/followers","following_url":"https://api.github.com/users/mallman/following{/other_user}","gists_url":"https://api.github.com/users/mallman/gists{/gist_id}","starred_url":"https://api.github.com/users/mallman/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/mallman/subscriptions","organizations_url":"https://api.github.com/users/mallman/orgs","repos_url":"https://api.github.com/users/mallman/repos","events_url":"https://api.github.com/users/mallman/events{/privacy}","received_events_url":"https://api.github.com/users/mallman/received_events","type":"User","site_admin":false},"body":"(This PR addresses https://issues.apache.org/jira/browse/SPARK-16980.)\r\n\r\n(N.B. I'm submitting this PR as an enhanced version of an internal POC I wrote. I'm looking for preliminary feedback on what I have so far and to discuss some design and implementation issues. This PR is not currently a candidate for merging into master.)\r\n\r\n## What changes were proposed in this pull request?\r\n\r\nIn a new Spark session, when a partitioned Hive table is converted to use Spark's `HadoopFsRelation` in `HiveMetastoreCatalog`, metadata for every partition of that table are retrieved from the metastore and loaded into driver memory. In addition, every partition's metadata files are read from the filesystem to perform schema inference.\r\n\r\nIf a user queries such a table with predicates which prune that table's partitions, we would like to be able to answer that query without consulting partition metadata which are not involved in the query. When querying a table with a large number of partitions for some data from a small number of partitions (maybe even a single partition), the current conversion strategy is highly inefficient. I suspect this scenario is not uncommon in the wild.\r\n\r\nIn addition to being inefficient in running time, the current strategy is inefficient in its use of driver memory. When the sum of the number of partitions of all tables loaded in a driver reaches a certain level (somewhere in the tens of thousands), their cached data exhaust all driver heap memory in the default configuration. I suspect this scenario is less common (in that not too many deployments work with tables with tens of thousands of partitions), however this does illustrate how large the memory footprint of this metadata can be. With tables with hundreds or thousands of partitions, I would expect the `HiveMetastoreCatalog` table cache to represent a significant portion of the driver's heap space.\r\n\r\nThis PR proposes an alternative approach. Basically, it makes four changes:\r\n\r\n1. It adds a new method, `listPartitionsByFilter` to the Catalyst `ExternalCatalog` trait which returns the partition metadata for a given sequence of partition pruning predicates.\r\n1. It refactors the `FileCatalog` type hierarchy to include a new `TableFileCatalog` to efficiently return files only for partitions matching a sequence of partition pruning predicates.\r\n1. It removes partition loading and caching from `HiveMetastoreCatalog`.\r\n1. It adds a new Catalyst optimizer rule, `PruneFileSourcePartitions`, which applies a plan's partition-pruning predicates to prune out unnecessary partition files from a `HadoopFsRelation`'s underlying file catalog.\r\n\r\nThe net effect is that when a query over a partitioned Hive table is planned, the analyzer retrieves the table metadata from `HiveMetastoreCatalog`. As part of this operation, the `HiveMetastoreCatalog` builds a `HadoopFsRelation` with a `TableFileCatalog`. It does not load any partition metadata or scan any files. The optimizer prunes-away unnecessary table partitions by sending the partition-pruning predicates to the relation's `TableFileCatalog `. The `TableFileCatalog` in turn calls the `listPartitionsByFilter` method on its external catalog. This queries the Hive metastore, passing along those filters.\r\n\r\nAs a bonus, performing partition pruning during optimization leads to a more accurate relation size estimate. This, along with c481bdf, can lead to automatic, safe application of the broadcast optimization in a join where it might previously have been omitted.\r\n\r\n## Open Issues\r\n\r\n1. This PR omits partition metadata caching. I can add this once the overall strategy for the cold path is established.\r\n1. This PR removes and omits partitioned Hive table schema reconciliation. As a result, it fails to find Parquet schema columns with upper case letters because of the Hive metastore's case-insensitivity. This issue may be fixed by #14750, but that PR appears to have stalled. @ericl has contributed to this PR a workaround for Parquet wherein schema reconciliation occurs at query execution time instead of planning. Whether ORC requires a similar patch is an open issue.\r\n1. This PR omits an implementation of `listPartitionsByFilter` for the `InMemoryCatalog`.\r\n1. Should we use `TableFileCatalog` for non-partitioned tables, too (to unify the partitioned and non-partitioned table access code paths)? It's called `TableFileCatalog` after all. This is probably something to explore in a future PR.\r\n1. This PR breaks parquet log output redirection during query execution. I can work around this by running `Class.forName(\"org.apache.spark.sql.execution.datasources.parquet.ParquetFileFormat$\")` first thing in a Spark shell session, but I haven't figured out how to fix this properly.\r\n1. This PR includes no new unit tests. We should probably have some. :)\r\n\r\n## How was this patch tested?\r\n\r\nThe current Spark unit tests were run, and some ad-hoc tests were performed to validate that only the necessary partition metadata is loaded.","created_at":"2016-08-17T18:13:53Z","updated_at":"2016-10-12T18:42:25Z","closed_at":null,"merged_at":null,"merge_commit_sha":"a7a5c8783a96064387b778a811a271927e63acd3","assignee":null,"assignees":[],"milestone":null,"commits_url":"https://api.github.com/repos/apache/spark/pulls/14690/commits","review_comments_url":"https://api.github.com/repos/apache/spark/pulls/14690/comments","review_comment_url":"https://api.github.com/repos/apache/spark/pulls/comments{/number}","comments_url":"https://api.github.com/repos/apache/spark/issues/14690/comments","statuses_url":"https://api.github.com/repos/apache/spark/statuses/14ca61266290ad8b4231ab0355cb0e848f2acfbe","head":{"label":"VideoAmp:spark-16980-lazy_partition_fetching","ref":"spark-16980-lazy_partition_fetching","sha":"14ca61266290ad8b4231ab0355cb0e848f2acfbe","user":{"login":"VideoAmp","id":7621972,"avatar_url":"https://avatars.githubusercontent.com/u/7621972?v=3","gravatar_id":"","url":"https://api.github.com/users/VideoAmp","html_url":"https://github.com/VideoAmp","followers_url":"https://api.github.com/users/VideoAmp/followers","following_url":"https://api.github.com/users/VideoAmp/following{/other_user}","gists_url":"https://api.github.com/users/VideoAmp/gists{/gist_id}","starred_url":"https://api.github.com/users/VideoAmp/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/VideoAmp/subscriptions","organizations_url":"https://api.github.com/users/VideoAmp/orgs","repos_url":"https://api.github.com/users/VideoAmp/repos","events_url":"https://api.github.com/users/VideoAmp/events{/privacy}","received_events_url":"https://api.github.com/users/VideoAmp/received_events","type":"Organization","site_admin":false},"repo":{"id":49447912,"name":"spark-public","full_name":"VideoAmp/spark-public","owner":{"login":"VideoAmp","id":7621972,"avatar_url":"https://avatars.githubusercontent.com/u/7621972?v=3","gravatar_id":"","url":"https://api.github.com/users/VideoAmp","html_url":"https://github.com/VideoAmp","followers_url":"https://api.github.com/users/VideoAmp/followers","following_url":"https://api.github.com/users/VideoAmp/following{/other_user}","gists_url":"https://api.github.com/users/VideoAmp/gists{/gist_id}","starred_url":"https://api.github.com/users/VideoAmp/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/VideoAmp/subscriptions","organizations_url":"https://api.github.com/users/VideoAmp/orgs","repos_url":"https://api.github.com/users/VideoAmp/repos","events_url":"https://api.github.com/users/VideoAmp/events{/privacy}","received_events_url":"https://api.github.com/users/VideoAmp/received_events","type":"Organization","site_admin":false},"private":false,"html_url":"https://github.com/VideoAmp/spark-public","description":"Mirror of Apache Spark","fork":true,"url":"https://api.github.com/repos/VideoAmp/spark-public","forks_url":"https://api.github.com/repos/VideoAmp/spark-public/forks","keys_url":"https://api.github.com/repos/VideoAmp/spark-public/keys{/key_id}","collaborators_url":"https://api.github.com/repos/VideoAmp/spark-public/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/VideoAmp/spark-public/teams","hooks_url":"https://api.github.com/repos/VideoAmp/spark-public/hooks","issue_events_url":"https://api.github.com/repos/VideoAmp/spark-public/issues/events{/number}","events_url":"https://api.github.com/repos/VideoAmp/spark-public/events","assignees_url":"https://api.github.com/repos/VideoAmp/spark-public/assignees{/user}","branches_url":"https://api.github.com/repos/VideoAmp/spark-public/branches{/branch}","tags_url":"https://api.github.com/repos/VideoAmp/spark-public/tags","blobs_url":"https://api.github.com/repos/VideoAmp/spark-public/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/VideoAmp/spark-public/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/VideoAmp/spark-public/git/refs{/sha}","trees_url":"https://api.github.com/repos/VideoAmp/spark-public/git/trees{/sha}","statuses_url":"https://api.github.com/repos/VideoAmp/spark-public/statuses/{sha}","languages_url":"https://api.github.com/repos/VideoAmp/spark-public/languages","stargazers_url":"https://api.github.com/repos/VideoAmp/spark-public/stargazers","contributors_url":"https://api.github.com/repos/VideoAmp/spark-public/contributors","subscribers_url":"https://api.github.com/repos/VideoAmp/spark-public/subscribers","subscription_url":"https://api.github.com/repos/VideoAmp/spark-public/subscription","commits_url":"https://api.github.com/repos/VideoAmp/spark-public/commits{/sha}","git_commits_url":"https://api.github.com/repos/VideoAmp/spark-public/git/commits{/sha}","comments_url":"https://api.github.com/repos/VideoAmp/spark-public/comments{/number}","issue_comment_url":"https://api.github.com/repos/VideoAmp/spark-public/issues/comments{/number}","contents_url":"https://api.github.com/repos/VideoAmp/spark-public/contents/{+path}","compare_url":"https://api.github.com/repos/VideoAmp/spark-public/compare/{base}...{head}","merges_url":"https://api.github.com/repos/VideoAmp/spark-public/merges","archive_url":"https://api.github.com/repos/VideoAmp/spark-public/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/VideoAmp/spark-public/downloads","issues_url":"https://api.github.com/repos/VideoAmp/spark-public/issues{/number}","pulls_url":"https://api.github.com/repos/VideoAmp/spark-public/pulls{/number}","milestones_url":"https://api.github.com/repos/VideoAmp/spark-public/milestones{/number}","notifications_url":"https://api.github.com/repos/VideoAmp/spark-public/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/VideoAmp/spark-public/labels{/name}","releases_url":"https://api.github.com/repos/VideoAmp/spark-public/releases{/id}","deployments_url":"https://api.github.com/repos/VideoAmp/spark-public/deployments","created_at":"2016-01-11T19:11:30Z","updated_at":"2016-01-11T19:11:55Z","pushed_at":"2016-10-12T18:33:26Z","git_url":"git://github.com/VideoAmp/spark-public.git","ssh_url":"git@github.com:VideoAmp/spark-public.git","clone_url":"https://github.com/VideoAmp/spark-public.git","svn_url":"https://github.com/VideoAmp/spark-public","homepage":null,"size":232858,"stargazers_count":0,"watchers_count":0,"language":"Scala","has_issues":false,"has_downloads":true,"has_wiki":false,"has_pages":false,"forks_count":0,"mirror_url":null,"open_issues_count":0,"forks":0,"open_issues":0,"watchers":0,"default_branch":"master"}},"base":{"label":"apache:master","ref":"master","sha":"8d33e1e5bfde6d2d1270058e49772427383312b3","user":{"login":"apache","id":47359,"avatar_url":"https://avatars.githubusercontent.com/u/47359?v=3","gravatar_id":"","url":"https://api.github.com/users/apache","html_url":"https://github.com/apache","followers_url":"https://api.github.com/users/apache/followers","following_url":"https://api.github.com/users/apache/following{/other_user}","gists_url":"https://api.github.com/users/apache/gists{/gist_id}","starred_url":"https://api.github.com/users/apache/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/apache/subscriptions","organizations_url":"https://api.github.com/users/apache/orgs","repos_url":"https://api.github.com/users/apache/repos","events_url":"https://api.github.com/users/apache/events{/privacy}","received_events_url":"https://api.github.com/users/apache/received_events","type":"Organization","site_admin":false},"repo":{"id":17165658,"name":"spark","full_name":"apache/spark","owner":{"login":"apache","id":47359,"avatar_url":"https://avatars.githubusercontent.com/u/47359?v=3","gravatar_id":"","url":"https://api.github.com/users/apache","html_url":"https://github.com/apache","followers_url":"https://api.github.com/users/apache/followers","following_url":"https://api.github.com/users/apache/following{/other_user}","gists_url":"https://api.github.com/users/apache/gists{/gist_id}","starred_url":"https://api.github.com/users/apache/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/apache/subscriptions","organizations_url":"https://api.github.com/users/apache/orgs","repos_url":"https://api.github.com/users/apache/repos","events_url":"https://api.github.com/users/apache/events{/privacy}","received_events_url":"https://api.github.com/users/apache/received_events","type":"Organization","site_admin":false},"private":false,"html_url":"https://github.com/apache/spark","description":"Mirror of Apache Spark","fork":false,"url":"https://api.github.com/repos/apache/spark","forks_url":"https://api.github.com/repos/apache/spark/forks","keys_url":"https://api.github.com/repos/apache/spark/keys{/key_id}","collaborators_url":"https://api.github.com/repos/apache/spark/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/apache/spark/teams","hooks_url":"https://api.github.com/repos/apache/spark/hooks","issue_events_url":"https://api.github.com/repos/apache/spark/issues/events{/number}","events_url":"https://api.github.com/repos/apache/spark/events","assignees_url":"https://api.github.com/repos/apache/spark/assignees{/user}","branches_url":"https://api.github.com/repos/apache/spark/branches{/branch}","tags_url":"https://api.github.com/repos/apache/spark/tags","blobs_url":"https://api.github.com/repos/apache/spark/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/apache/spark/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/apache/spark/git/refs{/sha}","trees_url":"https://api.github.com/repos/apache/spark/git/trees{/sha}","statuses_url":"https://api.github.com/repos/apache/spark/statuses/{sha}","languages_url":"https://api.github.com/repos/apache/spark/languages","stargazers_url":"https://api.github.com/repos/apache/spark/stargazers","contributors_url":"https://api.github.com/repos/apache/spark/contributors","subscribers_url":"https://api.github.com/repos/apache/spark/subscribers","subscription_url":"https://api.github.com/repos/apache/spark/subscription","commits_url":"https://api.github.com/repos/apache/spark/commits{/sha}","git_commits_url":"https://api.github.com/repos/apache/spark/git/commits{/sha}","comments_url":"https://api.github.com/repos/apache/spark/comments{/number}","issue_comment_url":"https://api.github.com/repos/apache/spark/issues/comments{/number}","contents_url":"https://api.github.com/repos/apache/spark/contents/{+path}","compare_url":"https://api.github.com/repos/apache/spark/compare/{base}...{head}","merges_url":"https://api.github.com/repos/apache/spark/merges","archive_url":"https://api.github.com/repos/apache/spark/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/apache/spark/downloads","issues_url":"https://api.github.com/repos/apache/spark/issues{/number}","pulls_url":"https://api.github.com/repos/apache/spark/pulls{/number}","milestones_url":"https://api.github.com/repos/apache/spark/milestones{/number}","notifications_url":"https://api.github.com/repos/apache/spark/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/apache/spark/labels{/name}","releases_url":"https://api.github.com/repos/apache/spark/releases{/id}","deployments_url":"https://api.github.com/repos/apache/spark/deployments","created_at":"2014-02-25T08:00:08Z","updated_at":"2016-10-12T18:41:55Z","pushed_at":"2016-10-12T18:34:23Z","git_url":"git://github.com/apache/spark.git","ssh_url":"git@github.com:apache/spark.git","clone_url":"https://github.com/apache/spark.git","svn_url":"https://github.com/apache/spark","homepage":null,"size":594665,"stargazers_count":10327,"watchers_count":10327,"language":"Scala","has_issues":false,"has_downloads":true,"has_wiki":false,"has_pages":false,"forks_count":9599,"mirror_url":"git://git.apache.org/spark.git","open_issues_count":483,"forks":9599,"open_issues":483,"watchers":10327,"default_branch":"master"}},"_links":{"self":{"href":"https://api.github.com/repos/apache/spark/pulls/14690"},"html":{"href":"https://github.com/apache/spark/pull/14690"},"issue":{"href":"https://api.github.com/repos/apache/spark/issues/14690"},"comments":{"href":"https://api.github.com/repos/apache/spark/issues/14690/comments"},"review_comments":{"href":"https://api.github.com/repos/apache/spark/pulls/14690/comments"},"review_comment":{"href":"https://api.github.com/repos/apache/spark/pulls/comments{/number}"},"commits":{"href":"https://api.github.com/repos/apache/spark/pulls/14690/commits"},"statuses":{"href":"https://api.github.com/repos/apache/spark/statuses/14ca61266290ad8b4231ab0355cb0e848f2acfbe"}}}}
{"actor":{"display_login":"ericl"}}