@@ -1033,6 +1033,8 @@ public abstract static class Sink implements FileIO.Sink<GenericRecord> {
10331033
10341034 abstract @ Nullable ValueProvider <Integer > getMinRowCountForPageSizeCheck ();
10351035
1036+ abstract @ Nullable ValueProvider <Integer > getMaxRowCountForPageSizeCheck ();
1037+
10361038 abstract @ Nullable Class <? extends GenericData > getAvroDataModelClass ();
10371039
10381040 abstract Builder toBuilder ();
@@ -1056,6 +1058,9 @@ abstract static class Builder {
10561058 abstract Builder setMinRowCountForPageSizeCheck (
10571059 ValueProvider <Integer > minRowCountForPageSizeCheck );
10581060
1061+ abstract Builder setMaxRowCountForPageSizeCheck (
1062+ ValueProvider <Integer > maxRowCountForPageSizeCheck );
1063+
10591064 abstract Builder setAvroDataModelClass (Class <? extends GenericData > modelClass );
10601065
10611066 abstract Sink build ();
@@ -1130,6 +1135,39 @@ public Sink withMinRowCountForPageSizeCheck(
11301135 return toBuilder ().setMinRowCountForPageSizeCheck (minRowCountForPageSizeCheck ).build ();
11311136 }
11321137
1138+ /**
1139+ * Specify the maximum number of rows to buffer before a page size check is forced. By default
1140+ * Parquet estimates the next check from the average row size and may defer it (up to {@code
1141+ * 10000} rows); a run of small rows followed by large rows can then let the column page buffer
1142+ * overflow {@code Integer.MAX_VALUE} before the deferred check fires. Setting this (e.g. {@code
1143+ * 1}) caps the interval so a check -- and flush -- happens at least this often regardless of
1144+ * the estimate. Pair it with {@link #withMinRowCountForPageSizeCheck(int)} to bound the buffer
1145+ * for tables whose row sizes vary widely.
1146+ */
1147+ public Sink withMaxRowCountForPageSizeCheck (int maxRowCountForPageSizeCheck ) {
1148+ checkArgument (
1149+ maxRowCountForPageSizeCheck > 0 , "maxRowCountForPageSizeCheck must be positive" );
1150+ return toBuilder ()
1151+ .setMaxRowCountForPageSizeCheck (
1152+ ValueProvider .StaticValueProvider .of (maxRowCountForPageSizeCheck ))
1153+ .build ();
1154+ }
1155+
1156+ /**
1157+ * Like {@link #withMaxRowCountForPageSizeCheck(int)}, but accepts a {@link ValueProvider} so
1158+ * the value can be supplied at runtime (required for classic Dataflow templates).
1159+ */
1160+ public Sink withMaxRowCountForPageSizeCheck (
1161+ ValueProvider <Integer > maxRowCountForPageSizeCheck ) {
1162+ checkNotNull (maxRowCountForPageSizeCheck , "maxRowCountForPageSizeCheck can not be null" );
1163+ if (maxRowCountForPageSizeCheck .isAccessible ()) {
1164+ Integer value = maxRowCountForPageSizeCheck .get ();
1165+ checkNotNull (value , "maxRowCountForPageSizeCheck value cannot be null" );
1166+ checkArgument (value > 0 , "maxRowCountForPageSizeCheck must be positive" );
1167+ }
1168+ return toBuilder ().setMaxRowCountForPageSizeCheck (maxRowCountForPageSizeCheck ).build ();
1169+ }
1170+
11331171 /**
11341172 * Define the Avro data model; see {@link AvroParquetWriter.Builder#withDataModel(GenericData)}.
11351173 */
@@ -1170,6 +1208,15 @@ public void open(WritableByteChannel channel) throws IOException {
11701208 }
11711209 }
11721210
1211+ ValueProvider <Integer > maxRowCountProvider = getMaxRowCountForPageSizeCheck ();
1212+ if (maxRowCountProvider != null ) {
1213+ Integer maxRowCount = maxRowCountProvider .get ();
1214+ if (maxRowCount != null ) {
1215+ checkArgument (maxRowCount > 0 , "maxRowCountForPageSizeCheck must be positive" );
1216+ builder = builder .withMaxRowCountForPageSizeCheck (maxRowCount );
1217+ }
1218+ }
1219+
11731220 if (modelClass != null ) {
11741221 try {
11751222 builder .withDataModel (buildModelObject (modelClass ));
0 commit comments