peter-toth commented on code in PR #59324:
URL: https://github.com/apache/spark/pull/59324#discussion_r4267722003


##########
sql/core/src/main/scala/org/apache/spark/sql/execution/datasources/parquet/ParquetStorageFilter.scala:
##########
@@ -264,39 +217,26 @@ object ParquetStorageFilter {
   }
 
   /**
-   * Rethrows an error the reader met while applying a storage filter, if it 
must not fall back on
-   * it by giving the filter up. `fromRead` says the error came from decoding 
the key pages rather
-   * than from evaluating the filter. The checks run in the executor's own 
order:
-   *  - a fatal error goes out as it is, kill or no kill.
-   *  - a pending task kill goes out wrapped when the error is one the 
executor's kill case takes,
-   *    see [[throwIfKilled]]. The error may be the kill's interrupt surfacing 
through a UDF that
-   *    wrapped it. `PythonRunner` reads a kill off the task context in the 
same way.
-   *  - a fatal error deeper in the cause chain, found by 
`Executor.isFatalError` as deep as the
-   *    executor would look had a plain plan raised `e`, goes out as itself, 
so the executor finds
-   *    it at the top whatever wraps it on the way.
+   * Rethrows an error the reader met while applying a storage filter, unless 
it may give the filter
+   * up on it. `fromRead` says the error came from decoding the key pages. In 
the executor's order:
+   *  - a fatal error goes out as it is;
+   *  - a pending task kill goes out wrapped, see [[throwIfKilled]], since the 
error may be the

Review Comment:
   Done in 
[1410d4684cb](https://github.com/apache/spark/pull/59324/commits/1410d4684cb7dffceb36fb7ecce3c0168e67fda3):
 "... goes out wrapped when the executor would report the error as a kill, see 
[[throwIfKilled]] ...".



##########
sql/core/src/main/java/org/apache/spark/sql/execution/datasources/parquet/LateMaterializationParquetRecordReader.java:
##########
@@ -190,11 +151,10 @@ public class LateMaterializationParquetRecordReader 
extends VectorizedParquetRec
 
   /**
    * Whether {@code parquet.filter.columnindex.enabled} is false, so this 
file's page index must not
-   * be used. That turns off phase 0 and every partial read. A wrong index 
would pair a row's key
-   * with another row's values, or return rows the filter rejects once 
splicing is given up. The
-   * post-scan Filter catches neither, since it takes the reader's mark on 
those rows. Parquet's
-   * partial read does not check the conf itself. Read off {@link 
#readOptions}, so it agrees with
-   * the row count {@link #fileReader} reported.
+   * be used. That turns off phase 0 and every partial read, which parquet 
does not check itself.
+   * It is the way out of a wrong page index, which would pair a row's key 
with another row's
+   * values, and which the post-scan Filter cannot catch on the rows this 
reader marks checked.
+   * Read off {@link #readOptions}, so it agrees with {@link #fileReader}.

Review Comment:
   Done in 
[1410d4684cb](https://github.com/apache/spark/pull/59324/commits/1410d4684cb7dffceb36fb7ecce3c0168e67fda3):
 "so it agrees with the row count {@link #fileReader} reported." I left the 
splicing case out. The comment gives one example of what a wrong index does, 
and the conf doc covers wrong rows in general.



##########
sql/core/src/main/java/org/apache/spark/sql/execution/datasources/parquet/LateMaterializationParquetRecordReader.java:
##########
@@ -117,25 +98,17 @@ public class LateMaterializationParquetRecordReader 
extends VectorizedParquetRec
       
SparkLoggerFactory.getLogger(LateMaterializationParquetRecordReader.class);
 
   /**
-   * What one buffered variable-length value costs the vector that holds it, 
as a multiple of the
-   * value's own bytes. Those bytes go in the byte child, with one null byte 
per element of that
-   * child, and up to as much again because {@link 
WritableColumnVector#reserve} doubles the child
-   * when it grows, by default. A factor rather than an exact figure, so with 
that default this
-   * half of the charge over-counts rather than under-counts, since nothing 
above this buffer can
-   * spill it. A {@code hugeVectorReserveRatio} above 2 makes it under-count 
for a child past
-   * {@code hugeVectorThreshold}. That threshold is off by default. A 
fixed-width key needs no
-   * factor, since its accumulator is allocated at capacity and never grows.
+   * What one buffered variable-length value costs, as a multiple of its own 
bytes. The byte child
+   * holds a null byte per byte, and {@link WritableColumnVector#reserve} 
doubles it when it grows.
+   * It over-counts on purpose, since nothing can spill this buffer. That 
holds at the default
+   * growth. A {@code hugeVectorReserveRatio} above 2 makes it under-count past
+   * {@code hugeVectorThreshold}, which is off by default.
    */
   private static final long VARIABLE_LENGTH_BYTES_FACTOR = 4L;
 
   /**
-   * What one surviving row range costs to hold, which is the half of the 
budget a row group
-   * buffering nothing still pays. Scattered survivors make one range each, 
and phase 2 needs the
-   * whole set to select its pages. One set is held, which every column reader 
of the row group
-   * walks with a cursor of its own. The figure is parquet's {@code 
RowRanges.Range}, two longs,
-   * plus its object header and a list slot. That is what is held. At the end 
of phase 1 the
-   * builder's list can have up to half as many slots again, and {@code 
build()} copies it, so the
-   * peak is somewhat higher.
+   * What one surviving row range costs to hold, parquet's {@code 
RowRanges.Range} with its object
+   * header and list slot. Scattered survivors make one range each.

Review Comment:
   I'd leave this one. It guards against a change to how the readers take the 
ranges, rather than explaining the current code.



##########
sql/core/src/main/java/org/apache/spark/sql/execution/datasources/parquet/LateMaterializationParquetRecordReader.java:
##########
@@ -523,75 +450,61 @@ private void initializeLateMaterialization() throws 
IOException {
   }
 
   /**
-   * Loads the next row group with rows left to emit, through the three phases 
of the class javadoc.
-   * Phase 2 reads the non-key columns over the survivors while splicing, the 
whole projection over
-   * the survivors once splicing is given up, and the whole projection over
-   * {@code pushedFilterRanges} once the filter is. Returns false when the row 
groups run out, which
-   * ends a read the filter narrowed.
+   * Loads the next row group with rows left to emit, through the phases of 
the class javadoc.
+   * Returns false when the row groups run out.
    */
   @Override
   protected boolean loadNextRowGroup() throws IOException {
     if (keyColumns == null) return super.loadNextRowGroup();
-    // The previous row group is fully emitted by the time this is called, so 
its pages are done
-    // with. Released here rather than at the next assignment, so a row group 
the filter empties,
-    // or the end of the file, does not keep the previous one's pages alive. 
Its column readers go
-    // too. They hold the row ranges it was read over, which the budget no 
longer counts, and the
-    // decoders of their last page.
+    // The previous row group is fully emitted, so its pages and column 
readers go now rather than
+    // at the next assignment, which a row group the filter empties would 
delay.

Review Comment:
   Done in 
[1410d4684cb](https://github.com/apache/spark/pull/59324/commits/1410d4684cb7dffceb36fb7ecce3c0168e67fda3),
 with the reason that holds for every row group: "The next assignment comes 
only after the next row group's phase 1, whose budget does not count them."



##########
sql/core/src/main/java/org/apache/spark/sql/execution/datasources/parquet/LateMaterializationParquetRecordReader.java:
##########
@@ -57,56 +57,37 @@
 /**
  * A {@link VectorizedParquetRecordReader} that reads a row group in phases 
under a
  * {@code ParquetStorageFilter}, so the projected columns of the rows the 
filter rejects are never
- * read. The filter is a deterministic conjunct of the post-scan {@code 
Filter}, which still holds
- * it, so any phase below may stop short and read the file the way a plain 
scan would.
+ * read. The post-scan {@code Filter} still holds the filter, so any phase may 
stop short and read

Review Comment:
   Done in 
[1410d4684cb](https://github.com/apache/spark/pull/59324/commits/1410d4684cb7dffceb36fb7ecce3c0168e67fda3),
 with the same wording.



##########
sql/core/src/main/java/org/apache/spark/sql/execution/datasources/parquet/LateMaterializationParquetRecordReader.java:
##########
@@ -477,14 +409,10 @@ private void initializeLateMaterialization() throws 
IOException {
       return;
     }
 
-    // The non-key columns phase 2 reads. If this file has none of them, the 
filter saves nothing,
-    // since phase 1 already reads every column the file has for every row. 
The reader then
-    // declines it. The planner already declines a projection of key columns 
alone, so this catches
-    // a file missing every projected non-key leaf, which also covers the 
row-index column. A struct
-    // the file has, but none of whose requested fields it has, counts only 
under the legacy
-    // `returnNullStructIfAllFieldsMissing`, since otherwise the clipped 
schema reads one of its
-    // other fields to tell a null struct from one whose requested fields are 
all null. The checked
-    // column is left out too, since a file column of its name is never 
decoded.
+    // The non-key columns phase 2 reads. A file with none of them gains 
nothing from the filter,
+    // so the reader declines it. A struct none of whose requested fields the 
file has counts as
+    // missing only under the legacy `returnNullStructIfAllFieldsMissing`, 
since otherwise the
+    // clipped schema reads one of its other fields. The checked column never 
counts.

Review Comment:
   Done in 
[1410d4684cb](https://github.com/apache/spark/pull/59324/commits/1410d4684cb7dffceb36fb7ecce3c0168e67fda3).



##########
sql/core/src/main/scala/org/apache/spark/sql/execution/datasources/parquet/ParquetStorageFilter.scala:
##########
@@ -264,39 +217,26 @@ object ParquetStorageFilter {
   }
 
   /**
-   * Rethrows an error the reader met while applying a storage filter, if it 
must not fall back on
-   * it by giving the filter up. `fromRead` says the error came from decoding 
the key pages rather
-   * than from evaluating the filter. The checks run in the executor's own 
order:
-   *  - a fatal error goes out as it is, kill or no kill.
-   *  - a pending task kill goes out wrapped when the error is one the 
executor's kill case takes,
-   *    see [[throwIfKilled]]. The error may be the kill's interrupt surfacing 
through a UDF that
-   *    wrapped it. `PythonRunner` reads a kill off the task context in the 
same way.
-   *  - a fatal error deeper in the cause chain, found by 
`Executor.isFatalError` as deep as the
-   *    executor would look had a plain plan raised `e`, goes out as itself, 
so the executor finds
-   *    it at the top whatever wraps it on the way.
+   * Rethrows an error the reader met while applying a storage filter, unless 
it may give the filter
+   * up on it. `fromRead` says the error came from decoding the key pages. In 
the executor's order:
+   *  - a fatal error goes out as it is;
+   *  - a pending task kill goes out wrapped, see [[throwIfKilled]], since the 
error may be the
+   *    kill's interrupt surfacing through a UDF;
+   *  - a fatal error deeper in the cause chain, as deep as the executor 
looks, goes out as itself.
    *
-   * An `InternalError` among those goes out in an `UnknownError`, a fatal 
error of another type.
-   * `FileScanRDD` would take a bare `InternalError` from a reader for a 
corrupt file under
-   * `ignoreCorruptFiles`. A `NonFatal` wrapper does not work either. With a 
kill pending, the
-   * executor reports one as killed and keeps running. A plain plan raises it 
from the post-scan
-   * Filter, where neither happens, and the executor stops. It stops on the 
`UnknownError` too, at
-   * any `spark.executor.killOnFatalError.depth` of 1 or more, since that is 
at the top.
+   * An `InternalError` among those goes out in an `UnknownError`, another 
fatal error. A bare one
+   * would be taken for a corrupt file under `ignoreCorruptFiles`, and a 
`NonFatal` wrapper would
+   * let the executor run on under a pending kill.
    *
    * An error from decoding differs in three ways:
-   *  - a plain read raises it from the reader, where `FileScanRDD` wraps a 
`NonFatal` one in
-   *    FAILED_READ_FILE before the executor looks. So a fatal error deeper in 
the chain of a
-   *    `NonFatal` one has to sit one level shallower to count, while any 
other error is searched
-   *    as deep as the executor does. Under `ignoreCorruptFiles` a plain read 
may skip the rest of
-   *    the file on it instead.
+   *  - `FileScanRDD` wraps a `NonFatal` one before the executor looks, so a 
fatal cause counts one

Review Comment:
   Done in 
[1410d4684cb](https://github.com/apache/spark/pull/59324/commits/1410d4684cb7dffceb36fb7ecce3c0168e67fda3).



##########
sql/core/src/main/java/org/apache/spark/sql/execution/datasources/parquet/LateMaterializationParquetRecordReader.java:
##########
@@ -57,56 +57,37 @@
 /**
  * A {@link VectorizedParquetRecordReader} that reads a row group in phases 
under a
  * {@code ParquetStorageFilter}, so the projected columns of the rows the 
filter rejects are never
- * read. The filter is a deterministic conjunct of the post-scan {@code 
Filter}, which still holds
- * it, so any phase below may stop short and read the file the way a plain 
scan would.
+ * read. The post-scan {@code Filter} still holds the filter, so any phase may 
stop short and read
+ * the rows the way a plain scan would.
  *
- * <p>Per row group, all three phases driven by the one {@link #fileReader}:
+ * <p>Per row group:
  * <ul>
- *   <li>phase 0 asks the column index which rows the pushed data filter 
allows, metadata only;</li>
- *   <li>phase 1 reads the key columns the filter references over those rows, 
evaluates the filter
- *       per row, and buffers the surviving key values;</li>
- *   <li>phase 2 reads the remaining projected columns over the surviving row 
ranges.</li>
+ *   <li>phase 0 asks the column index which rows the pushed data filter 
allows;</li>
+ *   <li>phase 1 reads the key columns over those rows, evaluates the filter, 
and buffers the
+ *       surviving key values;</li>
+ *   <li>phase 2 reads the other projected columns over the surviving row 
ranges.</li>
  * </ul>
+ * The emit path splices the buffered key values into the batch, so the key 
columns are read once.
+ * A row group whose buffer passes {@code maxSplicedRowGroupBytes} gives 
splicing up, and phase 2
+ * reads its key columns again. If the row ranges alone pass it, the filter is 
given up too.
  *
- * <p>The emit path then splices the buffered key vectors into the batch, so 
phase 2 never reads a
- * key column again. A row group whose buffer has passed
- * {@code spark.sql.parquet.storageFilterPushdown.maxSplicedRowGroupBytes} 
gives splicing up, and
- * phase 2 reads its key columns again with the rest. If the survivors' row 
ranges alone pass it,
- * the filter is given up for that row group too.
- *
- * <p>A whole file is read without splicing in these cases:
+ * <p>Some files and row groups get less:
  * <ul>
- *   <li>every key column is missing from it under schema evolution. The 
predicate is then constant
- *       over the file and is evaluated once, against the constants the reader 
materializes for
- *       those columns. True keeps the file unfiltered, false skips it;</li>
- *   <li>the file has none of the projected non-key columns, or none is 
projected. Phase 2 would
- *       have nothing to read, so the reader declines the filter and reads the 
file plainly;</li>
- *   <li>{@code parquet.filter.columnindex.enabled} is false, so its page 
index is not used. The
- *       filter then only skips row groups it rejects whole.</li>
+ *   <li>a file with none of the key columns has a constant predicate, 
evaluated once. False skips
+ *       the file, and true reads it whole;</li>

Review Comment:
   Done in 
[1410d4684cb](https://github.com/apache/spark/pull/59324/commits/1410d4684cb7dffceb36fb7ecce3c0168e67fda3).



-- 
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.

To unsubscribe, e-mail: [email protected]

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