Skip to content

Commit b8795b0

Browse files
authored
Merge branch 'main' into impl_comet_native_decimal_bool_cast
2 parents 1614a70 + 0ddd89f commit b8795b0

913 files changed

Lines changed: 137857 additions & 73110 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

.github/actions/java-test/action.yaml

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -41,11 +41,13 @@ inputs:
4141
runs:
4242
using: "composite"
4343
steps:
44-
- name: Run Cargo build
44+
- name: Run Cargo release build
4545
shell: bash
46+
# it is important that we run the Scala tests against a release build rather than a debug build
47+
# to make sure that no tests are relying on overflow checks that are present only in debug builds
4648
run: |
4749
cd native
48-
cargo build
50+
cargo build --release
4951
5052
- name: Cache Maven dependencies
5153
uses: actions/cache@v4
@@ -70,7 +72,7 @@ runs:
7072
SPARK_LOCAL_HOSTNAME: "localhost"
7173
SPARK_LOCAL_IP: "127.0.0.1"
7274
run: |
73-
MAVEN_OPTS="-Xmx4G -Xms2G -XX:+UnlockDiagnosticVMOptions -XX:+ShowMessageBoxOnError -XX:+HeapDumpOnOutOfMemoryError -XX:ErrorFile=./hs_err_pid%p.log" SPARK_HOME=`pwd` ./mvnw -B clean install ${{ inputs.maven_opts }}
75+
MAVEN_OPTS="-Xmx4G -Xms2G -XX:+UnlockDiagnosticVMOptions -XX:+ShowMessageBoxOnError -XX:+HeapDumpOnOutOfMemoryError -XX:ErrorFile=./hs_err_pid%p.log" SPARK_HOME=`pwd` ./mvnw -B -Prelease clean install ${{ inputs.maven_opts }}
7476
- name: Run specified tests
7577
shell: bash
7678
if: ${{ inputs.suites != '' }}
@@ -81,7 +83,7 @@ runs:
8183
run: |
8284
MAVEN_SUITES="$(echo "${{ inputs.suites }}" | paste -sd, -)"
8385
echo "Running with MAVEN_SUITES=$MAVEN_SUITES"
84-
MAVEN_OPTS="-Xmx4G -Xms2G -DwildcardSuites=$MAVEN_SUITES -XX:+UnlockDiagnosticVMOptions -XX:+ShowMessageBoxOnError -XX:+HeapDumpOnOutOfMemoryError -XX:ErrorFile=./hs_err_pid%p.log" SPARK_HOME=`pwd` ./mvnw -B clean install ${{ inputs.maven_opts }}
86+
MAVEN_OPTS="-Xmx4G -Xms2G -DwildcardSuites=$MAVEN_SUITES -XX:+UnlockDiagnosticVMOptions -XX:+ShowMessageBoxOnError -XX:+HeapDumpOnOutOfMemoryError -XX:ErrorFile=./hs_err_pid%p.log" SPARK_HOME=`pwd` ./mvnw -B -Prelease clean install ${{ inputs.maven_opts }}
8587
- name: Upload crash logs
8688
if: failure()
8789
uses: actions/upload-artifact@v4
Lines changed: 4 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -63,7 +63,8 @@ jobs:
6363
key: ${{ runner.os }}-java-maven-${{ hashFiles('**/pom.xml') }}
6464
restore-keys: |
6565
${{ runner.os }}-java-maven-
66-
66+
- name: Build Comet
67+
run: make release
6768
- name: Cache TPC-DS generated data
6869
id: cache-tpcds-sf-1
6970
uses: actions/cache@v4
@@ -76,17 +77,6 @@ jobs:
7677
with:
7778
repository: databricks/tpcds-kit
7879
path: ./tpcds-kit
79-
- name: Build Comet
80-
run: make release
81-
- name: Upload Comet native lib
82-
uses: actions/upload-artifact@v4
83-
with:
84-
name: libcomet-${{ github.run_id }}
85-
path: |
86-
native/target/release/libcomet.so
87-
native/target/release/libcomet.dylib
88-
retention-days: 1 # remove the artifact after 1 day, only valid for this workflow
89-
overwrite: true
9080
- name: Build tpcds-kit
9181
if: steps.cache-tpcds-sf-1.outputs.cache-hit != 'true'
9282
run: |
@@ -132,11 +122,8 @@ jobs:
132122
path: ./tpcds-sf-1
133123
key: tpcds-${{ hashFiles('.github/workflows/benchmark.yml') }}
134124
fail-on-cache-miss: true # it's always be cached as it should be generated by pre-step if not existed
135-
- name: Download Comet native lib
136-
uses: actions/download-artifact@v5
137-
with:
138-
name: libcomet-${{ github.run_id }}
139-
path: native/target/release
125+
- name: Build Comet
126+
run: make release
140127
- name: Run TPC-DS queries (Sort merge join)
141128
if: matrix.join == 'sort_merge'
142129
run: |

.github/workflows/benchmark-tpch.yml

Lines changed: 2 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -71,15 +71,6 @@ jobs:
7171
key: tpch-${{ hashFiles('.github/workflows/benchmark-tpch.yml') }}
7272
- name: Build Comet
7373
run: make release
74-
- name: Upload Comet native lib
75-
uses: actions/upload-artifact@v4
76-
with:
77-
name: libcomet-${{ github.run_id }}
78-
path: |
79-
native/target/release/libcomet.so
80-
native/target/release/libcomet.dylib
81-
retention-days: 1 # remove the artifact after 1 day, only valid for this workflow
82-
overwrite: true
8374
- name: Generate TPC-H (SF=1) table data
8475
if: steps.cache-tpch-sf-1.outputs.cache-hit != 'true'
8576
run: |
@@ -115,11 +106,8 @@ jobs:
115106
path: ./tpch
116107
key: tpch-${{ hashFiles('.github/workflows/benchmark-tpch.yml') }}
117108
fail-on-cache-miss: true # it's always be cached as it should be generated by pre-step if not existed
118-
- name: Download Comet native lib
119-
uses: actions/download-artifact@v5
120-
with:
121-
name: libcomet-${{ github.run_id }}
122-
path: native/target/release
109+
- name: Build Comet
110+
run: make release
123111
- name: Run TPC-H queries
124112
run: |
125113
SPARK_HOME=`pwd` SPARK_TPCH_DATA=`pwd`/tpch/sf1_parquet ./mvnw -B -Prelease -Dsuites=org.apache.spark.sql.CometTPCHQuerySuite test

.github/workflows/pr_build_linux.yml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -103,6 +103,7 @@ jobs:
103103
value: |
104104
org.apache.comet.CometFuzzTestSuite
105105
org.apache.comet.CometFuzzAggregateSuite
106+
org.apache.comet.CometFuzzMathSuite
106107
org.apache.comet.DataGeneratorSuite
107108
- name: "shuffle"
108109
value: |

.github/workflows/pr_build_macos.yml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -68,6 +68,7 @@ jobs:
6868
value: |
6969
org.apache.comet.CometFuzzTestSuite
7070
org.apache.comet.CometFuzzAggregateSuite
71+
org.apache.comet.CometFuzzMathSuite
7172
org.apache.comet.DataGeneratorSuite
7273
- name: "shuffle"
7374
value: |
Lines changed: 146 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,146 @@
1+
/*
2+
* Licensed to the Apache Software Foundation (ASF) under one
3+
* or more contributor license agreements. See the NOTICE file
4+
* distributed with this work for additional information
5+
* regarding copyright ownership. The ASF licenses this file
6+
* to you under the Apache License, Version 2.0 (the
7+
* "License"); you may not use this file except in compliance
8+
* with the License. You may obtain a copy of the License at
9+
*
10+
* http://www.apache.org/licenses/LICENSE-2.0
11+
*
12+
* Unless required by applicable law or agreed to in writing,
13+
* software distributed under the License is distributed on an
14+
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15+
* KIND, either express or implied. See the License for the
16+
* specific language governing permissions and limitations
17+
* under the License.
18+
*/
19+
20+
package org.apache.comet.parquet;
21+
22+
import java.util.concurrent.ConcurrentHashMap;
23+
24+
import org.apache.hadoop.conf.Configuration;
25+
import org.apache.hadoop.fs.Path;
26+
import org.apache.parquet.crypto.DecryptionKeyRetriever;
27+
import org.apache.parquet.crypto.DecryptionPropertiesFactory;
28+
import org.apache.parquet.crypto.FileDecryptionProperties;
29+
import org.apache.parquet.crypto.ParquetCryptoRuntimeException;
30+
31+
// spotless:off
32+
/*
33+
* Architecture Overview:
34+
*
35+
* JVM Side | Native Side
36+
* ┌─────────────────────────────────────┐ | ┌─────────────────────────────────────┐
37+
* │ CometFileKeyUnwrapper │ | │ Parquet File Reading │
38+
* │ │ | │ │
39+
* │ ┌─────────────────────────────┐ │ | │ ┌─────────────────────────────┐ │
40+
* │ │ hadoopConf │ │ | │ │ file1.parquet │ │
41+
* │ │ (Configuration) │ │ | │ │ file2.parquet │ │
42+
* │ └─────────────────────────────┘ │ | │ │ file3.parquet │ │
43+
* │ │ │ | │ └─────────────────────────────┘ │
44+
* │ ▼ │ | │ │ │
45+
* │ ┌─────────────────────────────┐ │ | │ │ │
46+
* │ │ factoryCache │ │ | │ ▼ │
47+
* │ │ (many-to-one mapping) │ │ | │ ┌─────────────────────────────┐ │
48+
* │ │ │ │ | │ │ Parse file metadata & │ │
49+
* │ │ file1 ──┐ │ │ | │ │ extract keyMetadata │ │
50+
* │ │ file2 ──┼─► DecryptionProps │ │ | │ └─────────────────────────────┘ │
51+
* │ │ file3 ──┘ Factory │ │ | │ │ │
52+
* │ └─────────────────────────────┘ │ | │ │ │
53+
* │ │ │ | │ ▼ │
54+
* │ ▼ │ | │ ╔═════════════════════════════╗ │
55+
* │ ┌─────────────────────────────┐ │ | │ ║ JNI CALL: ║ │
56+
* │ │ retrieverCache │ │ | │ ║ getKey(filePath, ║ │
57+
* │ │ filePath -> KeyRetriever │◄───┼───┼───┼──║ keyMetadata) ║ │
58+
* │ └─────────────────────────────┘ │ | │ ╚═════════════════════════════╝ │
59+
* │ │ │ | │ │
60+
* │ ▼ │ | │ │
61+
* │ ┌─────────────────────────────┐ │ | │ │
62+
* │ │ DecryptionKeyRetriever │ │ | │ │
63+
* │ │ .getKey(keyMetadata) │ │ | │ │
64+
* │ └─────────────────────────────┘ │ | │ │
65+
* │ │ │ | │ │
66+
* │ ▼ │ | │ │
67+
* │ ┌─────────────────────────────┐ │ | │ ┌─────────────────────────────┐ │
68+
* │ │ return key bytes │────┼───┼───┼─►│ Use key for decryption │ │
69+
* │ └─────────────────────────────┘ │ | │ │ of parquet data │ │
70+
* └─────────────────────────────────────┘ | │ └─────────────────────────────┘ │
71+
* | └─────────────────────────────────────┘
72+
* |
73+
* JNI Boundary
74+
*
75+
* Setup Phase (storeDecryptionKeyRetriever):
76+
* 1. hadoopConf → DecryptionPropertiesFactory (cached in factoryCache)
77+
* 2. Factory + filePath → DecryptionKeyRetriever (cached in retrieverCache)
78+
*
79+
* Runtime Phase (getKey):
80+
* 3. Native code calls getKey(filePath, keyMetadata) ──► JVM
81+
* 4. Retrieve cached DecryptionKeyRetriever for filePath
82+
* 5. KeyRetriever.getKey(keyMetadata) → decrypted key bytes
83+
* 6. Return key bytes ──► Native code for parquet decryption
84+
*/
85+
// spotless:on
86+
87+
/**
88+
* Helper class to access DecryptionKeyRetriever.getKey from native code via JNI. This class handles
89+
* the complexity of creating and caching properly configured DecryptionKeyRetriever instances using
90+
* DecryptionPropertiesFactory. The life of this object is meant to map to a single Comet plan, so
91+
* associated with CometExecIterator.
92+
*/
93+
public class CometFileKeyUnwrapper {
94+
95+
// Each file path gets a unique DecryptionKeyRetriever
96+
private final ConcurrentHashMap<String, DecryptionKeyRetriever> retrieverCache =
97+
new ConcurrentHashMap<>();
98+
99+
// Cache the factory since we should be using the same hadoopConf for every file in this scan.
100+
private DecryptionPropertiesFactory factory = null;
101+
// Cache the hadoopConf just to assert the assumption above.
102+
private Configuration conf = null;
103+
104+
/**
105+
* Creates and stores a DecryptionKeyRetriever instance for the given file path.
106+
*
107+
* @param filePath The path to the Parquet file
108+
* @param hadoopConf The Hadoop Configuration to use for this file path
109+
*/
110+
public void storeDecryptionKeyRetriever(final String filePath, final Configuration hadoopConf) {
111+
// Use DecryptionPropertiesFactory.loadFactory to get the factory and then call
112+
// getFileDecryptionProperties
113+
if (factory == null) {
114+
factory = DecryptionPropertiesFactory.loadFactory(hadoopConf);
115+
conf = hadoopConf;
116+
} else {
117+
// Check the assumption that all files have the same hadoopConf and thus same Factory
118+
assert (conf == hadoopConf);
119+
}
120+
Path path = new Path(filePath);
121+
FileDecryptionProperties decryptionProperties =
122+
factory.getFileDecryptionProperties(hadoopConf, path);
123+
124+
DecryptionKeyRetriever keyRetriever = decryptionProperties.getKeyRetriever();
125+
retrieverCache.put(filePath, keyRetriever);
126+
}
127+
128+
/**
129+
* Gets the decryption key for the given key metadata using the cached DecryptionKeyRetriever for
130+
* the specified file path.
131+
*
132+
* @param filePath The path to the Parquet file
133+
* @param keyMetadata The key metadata bytes from the Parquet file
134+
* @return The decrypted key bytes
135+
* @throws ParquetCryptoRuntimeException if key unwrapping fails
136+
*/
137+
public byte[] getKey(final String filePath, final byte[] keyMetadata)
138+
throws ParquetCryptoRuntimeException {
139+
DecryptionKeyRetriever keyRetriever = retrieverCache.get(filePath);
140+
if (keyRetriever == null) {
141+
throw new ParquetCryptoRuntimeException(
142+
"Failed to find DecryptionKeyRetriever for path: " + filePath);
143+
}
144+
return keyRetriever.getKey(keyMetadata);
145+
}
146+
}

common/src/main/java/org/apache/comet/parquet/Native.java

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -267,9 +267,11 @@ public static native long initRecordBatchReader(
267267
String sessionTimezone,
268268
int batchSize,
269269
boolean caseSensitive,
270-
Map<String, String> objectStoreOptions);
270+
Map<String, String> objectStoreOptions,
271+
CometFileKeyUnwrapper keyUnwrapper);
271272

272273
// arrow native version of read batch
274+
273275
/**
274276
* Read the next batch of data into memory on native side
275277
*
@@ -280,6 +282,7 @@ public static native long initRecordBatchReader(
280282

281283
// arrow native equivalent of currentBatch. 'columnNum' is number of the column in the record
282284
// batch
285+
283286
/**
284287
* Load the column corresponding to columnNum in the currently loaded record batch into JVM
285288
*

common/src/main/java/org/apache/comet/parquet/NativeBatchReader.java

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -80,7 +80,7 @@
8080
import org.apache.comet.vector.CometVector;
8181
import org.apache.comet.vector.NativeUtil;
8282

83-
import static scala.jdk.javaapi.CollectionConverters.*;
83+
import static scala.jdk.javaapi.CollectionConverters.asJava;
8484

8585
/**
8686
* A vectorized Parquet reader that reads a Parquet file in a batched fashion.
@@ -410,6 +410,15 @@ public void init() throws Throwable {
410410
}
411411
}
412412

413+
boolean encryptionEnabled = CometParquetUtils.encryptionEnabled(conf);
414+
415+
// Create keyUnwrapper if encryption is enabled
416+
CometFileKeyUnwrapper keyUnwrapper = null;
417+
if (encryptionEnabled) {
418+
keyUnwrapper = new CometFileKeyUnwrapper();
419+
keyUnwrapper.storeDecryptionKeyRetriever(file.filePath().toString(), conf);
420+
}
421+
413422
int batchSize =
414423
conf.getInt(
415424
CometConf.COMET_BATCH_SIZE().key(),
@@ -426,7 +435,8 @@ public void init() throws Throwable {
426435
timeZoneId,
427436
batchSize,
428437
caseSensitive,
429-
objectStoreOptions);
438+
objectStoreOptions,
439+
keyUnwrapper);
430440
}
431441
isInitialized = true;
432442
}

common/src/main/scala/org/apache/comet/CometConf.scala

Lines changed: 21 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -63,9 +63,11 @@ object CometConf extends ShimCometConf {
6363

6464
def conf(key: String): ConfigBuilder = ConfigBuilder(key)
6565

66-
val COMET_EXEC_CONFIG_PREFIX = "spark.comet.exec";
66+
val COMET_PREFIX = "spark.comet";
6767

68-
val COMET_EXPR_CONFIG_PREFIX = "spark.comet.expression";
68+
val COMET_EXEC_CONFIG_PREFIX: String = s"$COMET_PREFIX.exec";
69+
70+
val COMET_EXPR_CONFIG_PREFIX: String = s"$COMET_PREFIX.expression";
6971

7072
val COMET_ENABLED: ConfigEntry[Boolean] = conf("spark.comet.enabled")
7173
.doc(
@@ -457,8 +459,10 @@ object CometConf extends ShimCometConf {
457459
val COMET_EXPLAIN_VERBOSE_ENABLED: ConfigEntry[Boolean] =
458460
conf("spark.comet.explain.verbose.enabled")
459461
.doc(
460-
"When this setting is enabled, Comet will provide a verbose tree representation of " +
461-
"the extended information.")
462+
"When this setting is enabled, Comet's extended explain output will provide the full " +
463+
"query plan annotated with fallback reasons as well as a summary of how much of " +
464+
"the plan was accelerated by Comet. When this setting is disabled, a list of fallback " +
465+
"reasons will be provided instead.")
462466
.booleanConf
463467
.createWithDefault(false)
464468

@@ -507,12 +511,13 @@ object CometConf extends ShimCometConf {
507511
.createWithDefault(false)
508512

509513
val COMET_EXEC_MEMORY_POOL_TYPE: ConfigEntry[String] = conf("spark.comet.exec.memoryPool")
510-
.doc("The type of memory pool to be used for Comet native execution. " +
511-
"When running Spark in on-heap mode, available pool types are 'greedy', 'fair_spill', " +
512-
"'greedy_task_shared', 'fair_spill_task_shared', 'greedy_global', 'fair_spill_global', " +
513-
"and `unbounded`. When running Spark in off-heap mode, available pool types are " +
514-
"'unified' and `fair_unified`. The default pool type is `greedy_task_shared` for on-heap " +
515-
s"mode and `unified` for off-heap mode. $TUNING_GUIDE.")
514+
.doc(
515+
"The type of memory pool to be used for Comet native execution. " +
516+
"When running Spark in on-heap mode, available pool types are 'greedy', 'fair_spill', " +
517+
"'greedy_task_shared', 'fair_spill_task_shared', 'greedy_global', 'fair_spill_global', " +
518+
"and `unbounded`. When running Spark in off-heap mode, available pool types are " +
519+
"'greedy_unified' and `fair_unified`. The default pool type is `greedy_task_shared` " +
520+
s"for on-heap mode and `unified` for off-heap mode. $TUNING_GUIDE.")
516521
.stringConf
517522
.createWithDefault("default")
518523

@@ -648,6 +653,12 @@ object CometConf extends ShimCometConf {
648653
.stringConf
649654
.createOptional
650655

656+
val COMET_MAX_TEMP_DIRECTORY_SIZE: ConfigEntry[Long] =
657+
conf("spark.comet.maxTempDirectorySize")
658+
.doc("The maximum amount of data (in bytes) stored inside the temporary directories.")
659+
.bytesConf(ByteUnit.BYTE)
660+
.createWithDefault(100L * 1024 * 1024 * 1024) // 100 GB
661+
651662
/** Create a config to enable a specific operator */
652663
private def createExecEnabledConfig(
653664
exec: String,

0 commit comments

Comments
 (0)