Skip to content

Commit 0cc1521

Browse files
authored
[GH-1918] Spark 4 support (#1919)
* Add Spark 4 support * Use staging repo * Try to run tests * Work on Spark 4 support * Use shims instead of reflection * Bump hadoop version * Add 4.0 copy, add nulll intolerant shim, and ignore dbscan tests * Fully shade jiffle and antlr * Add back specific exclusions to jiffle * Fix test * Bump to RC4 * Undo workflow change and add comment to shading * Run pre-commit * Comment in pom * Update snapshot version * Fix strategy import * Re-sync new spark-4.0 module and update arrow eval for 4.0 changes * Rework off of current arrow eval python * Install pyspark for udf test * Generate sources before scala doc * Use official 4.0 release * Use officialy python release and add to python test matrix * Set java version for python tests * Bump pandas to 2.x * Bump min python to 3.8 * Try to fix Python tests * Remove jiffle from spark common since it's not used and causes spark-shaded issues * Exclude jiffle in spark-shaded * Don't include connect for Spark 4+ * pre-check
1 parent ebf00cb commit 0cc1521

73 files changed

Lines changed: 8425 additions & 64 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/workflows/docs.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,7 @@ jobs:
4848
- name: Compile JavaDoc
4949
run: mvn -q clean install -DskipTests && mkdir -p docs/api/javadoc/spark && cp -r spark/common/target/apidocs/* docs/api/javadoc/spark/
5050
- name: Compile ScalaDoc
51-
run: mvn scala:doc -pl !common,!snowflake,!flink && mkdir -p docs/api/scaladoc/spark && cp -r spark/common/target/site/scaladocs/* docs/api/scaladoc/spark
51+
run: mvn generate-sources scala:doc -pl !common,!snowflake,!flink && mkdir -p docs/api/scaladoc/spark && cp -r spark/common/target/site/scaladocs/* docs/api/scaladoc/spark
5252
- uses: actions/setup-python@v5
5353
with:
5454
python-version: 3.x

.github/workflows/java.yml

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,9 @@ jobs:
6262
fail-fast: true
6363
matrix:
6464
include:
65+
- spark: 4.0.0
66+
scala: 2.13.8
67+
jdk: '17'
6568
- spark: 3.5.0
6669
scala: 2.13.8
6770
jdk: '11'
@@ -105,6 +108,11 @@ jobs:
105108
export SPARK_HOME=$(python -c "import pyspark; print(pyspark.__path__[0])")
106109
fi
107110
111+
if [ "${SPARK_VERSION}" == "4.0.0" ]; then
112+
pip install pyspark==4.0.0 pandas shapely apache-sedona pyarrow
113+
export SPARK_HOME=$(python -c "import pyspark; print(pyspark.__path__[0])")
114+
fi
115+
108116
mvn -q clean install -Dspark=${SPARK_COMPAT_VERSION} -Dscala=${SCALA_VERSION:0:4} -Dspark.version=${SPARK_VERSION} ${SKIP_TESTS}
109117
- run: mkdir staging
110118
- run: cp spark-shaded/target/sedona-*.jar staging

.github/workflows/python.yml

Lines changed: 19 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -60,33 +60,46 @@ jobs:
6060
strategy:
6161
matrix:
6262
include:
63+
- spark: '4.0.0'
64+
scala: '2.13.8'
65+
java: '17'
66+
python: '3.10'
6367
- spark: '3.5.0'
6468
scala: '2.12.8'
69+
java: '11'
6570
python: '3.10'
6671
shapely: '1'
6772
- spark: '3.5.0'
6873
scala: '2.12.8'
74+
java: '11'
6975
python: '3.10'
7076
- spark: '3.5.0'
7177
scala: '2.12.8'
78+
java: '11'
7279
python: '3.9'
7380
- spark: '3.5.0'
7481
scala: '2.12.8'
82+
java: '11'
7583
python: '3.8'
7684
- spark: '3.4.0'
7785
scala: '2.12.8'
86+
java: '11'
7887
python: '3.10'
7988
- spark: '3.4.0'
8089
scala: '2.12.8'
90+
java: '11'
8191
python: '3.9'
8292
- spark: '3.4.0'
8393
scala: '2.12.8'
94+
java: '11'
8495
python: '3.8'
8596
- spark: '3.4.0'
8697
scala: '2.12.8'
98+
java: '11'
8799
python: '3.8'
88100
- spark: '3.4.0'
89101
scala: '2.12.8'
102+
java: '11'
90103
python: '3.8'
91104
shapely: '1'
92105

@@ -97,7 +110,7 @@ jobs:
97110
- uses: actions/setup-java@v4
98111
with:
99112
distribution: 'zulu'
100-
java-version: '11'
113+
java-version: '${{ matrix.java }}'
101114
- uses: actions/setup-python@v5
102115
with:
103116
python-version: ${{ matrix.python }}
@@ -123,12 +136,17 @@ jobs:
123136
SPARK_VERSION: ${{ matrix.spark }}
124137
PYTHON_VERSION: ${{ matrix.python }}
125138
SHAPELY_VERSION: ${{ matrix.shapely }}
139+
PANDAS_VERSION: ${{ matrix.pandas }}
126140
run: |
127141
cd python
128142
if [ "${SHAPELY_VERSION}" == "1" ]; then
129143
echo "Patching Pipfile to use Shapely 1.x"
130144
sed -i 's/^shapely.*$/shapely="<2.0.0"/g' Pipfile
131145
fi
146+
if [ "${PANDAS_VERSION}" == "1" ]; then
147+
echo "Patching Pipfile to use Pandas 1.x"
148+
sed -i 's/^pandas.*$/pandas="<2.0.0"/g' Pipfile
149+
fi
132150
export PIPENV_CUSTOM_VENV_NAME=python-${PYTHON_VERSION}
133151
pipenv --python ${PYTHON_VERSION}
134152
pipenv install pyspark==${SPARK_VERSION}

pom.xml

Lines changed: 23 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -76,14 +76,14 @@
7676
<spatial4j.version>0.8</spatial4j.version>
7777

7878
<jt-jiffle.version>1.1.24</jt-jiffle.version>
79-
<antlr-runtime.version>4.9.3</antlr-runtime.version>
8079
<janino-version>3.1.9</janino-version>
8180

8281
<!-- Actual scala, spark and log4j version will be set by activated profiles.
8382
Setting a default value helps IDE:s that can't make sense of profiles. -->
8483
<scala.compat.version>2.12</scala.compat.version>
8584
<spark.version>3.4.0</spark.version>
8685
<spark.compat.version>3.4</spark.compat.version>
86+
<spark.major.version>3</spark.major.version>
8787
<log4j.version>2.19.0</log4j.version>
8888
<graphframe.version>0.8.3-spark3.4</graphframe.version>
8989

@@ -736,6 +736,28 @@
736736
<skip.deploy.common.modules>true</skip.deploy.common.modules>
737737
</properties>
738738
</profile>
739+
<profile>
740+
<id>sedona-spark-4.0</id>
741+
<activation>
742+
<property>
743+
<name>spark</name>
744+
<value>4.0</value>
745+
</property>
746+
</activation>
747+
<properties>
748+
<spark.version>4.0.0</spark.version>
749+
<spark.compat.version>4.0</spark.compat.version>
750+
<spark.major.version>4</spark.major.version>
751+
<hadoop.version>3.4.1</hadoop.version>
752+
<log4j.version>2.24.3</log4j.version>
753+
<slf4j.version>2.0.16</slf4j.version>
754+
<graphframe.version>0.8.3-spark3.5</graphframe.version>
755+
<scala.version>2.13.12</scala.version>
756+
<scala.compat.version>2.13</scala.compat.version>
757+
<!-- Skip deploying parent module. it will be deployed with sedona-spark-3.3 -->
758+
<skip.deploy.common.modules>true</skip.deploy.common.modules>
759+
</properties>
760+
</profile>
739761
<profile>
740762
<id>scala2.13</id>
741763
<activation>

python/Pipfile

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ matplotlib = "*" # implicit dependency of esda
1717
scipy = "<=1.10.0" # prevent incompatibility with pysal 4.7.0, which is what is resolved to when shapely >2 is specified
1818

1919
[packages]
20-
pandas="<=1.5.3"
20+
pandas=">=2.0.0"
2121
numpy="<2"
2222
geopandas="*"
2323
# https://stackoverflow.com/questions/78949093/how-to-resolve-attributeerror-module-fiona-has-no-attribute-path

python/sedona/spark/core/jvm/config.py

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,6 @@
2323
from re import findall
2424
from typing import Optional, Tuple
2525

26-
from py4j.protocol import Py4JJavaError
2726
from pyspark.sql import SparkSession
2827

2928
from sedona.spark.utils.decorators import classproperty
@@ -189,7 +188,7 @@ def get_spark_java_config(
189188

190189
try:
191190
used_jar_files = java_spark_conf.get(value)
192-
except Py4JJavaError:
191+
except Exception:
193192
error_message = f"Didn't find the value of {value} from SparkConf"
194193
logging.info(error_message)
195194

python/tests/sql/test_dataframe_api.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919
import concurrent.futures
2020
from typing import Callable, Tuple
2121

22+
import pyspark
2223
import pytest
2324
from pyspark.sql import Row
2425
from pyspark.sql import functions as f
@@ -1760,6 +1761,9 @@ def run_spatial_query():
17601761
os.getenv("SPARK_REMOTE") is not None,
17611762
reason="Checkpoint dir is not available in Spark Connect",
17621763
)
1764+
@pytest.mark.skipif(
1765+
pyspark.__version__ >= "4", reason="DBSCAN is not supported yet on Spark 4"
1766+
)
17631767
def test_dbscan(self):
17641768
df = self.spark.createDataFrame([{"id": 1, "x": 2, "y": 3}]).withColumn(
17651769
"geometry", f.expr("ST_Point(x, y)")

python/tests/stats/test_dbscan.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
# specific language governing permissions and limitations
1616
# under the License.
1717

18+
import pyspark
1819
import pyspark.sql.functions as f
1920
import pytest
2021
from sklearn.cluster import DBSCAN as sklearnDBSCAN
@@ -25,6 +26,9 @@
2526
from sedona.spark.stats import dbscan
2627

2728

29+
@pytest.mark.skipif(
30+
pyspark.__version__ >= "4", reason="DBSCAN is not supported yet on Spark 4"
31+
)
2832
class TestDBScan(TestBase):
2933

3034
@pytest.fixture

python/tests/test_base.py

Lines changed: 16 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -44,26 +44,29 @@ def spark(self):
4444

4545
builder = SedonaContext.builder().appName("SedonaSparkTest")
4646
if SPARK_REMOTE:
47-
builder = (
48-
builder.remote(SPARK_REMOTE)
49-
.config(
47+
builder = builder.remote(SPARK_REMOTE).config(
48+
"spark.sql.extensions",
49+
"org.apache.sedona.sql.SedonaSqlExtensions",
50+
)
51+
52+
# Connect is packaged with Spark 4+
53+
if pyspark.__version__ < "4":
54+
builder = builder.config(
5055
"spark.jars.packages",
5156
f"org.apache.spark:spark-connect_2.12:{pyspark.__version__}",
5257
)
53-
.config(
54-
"spark.sql.extensions",
55-
"org.apache.sedona.sql.SedonaSqlExtensions",
56-
)
57-
.config(
58-
"spark.sedona.stac.load.itemsLimitMax",
59-
"20",
60-
)
61-
)
6258
else:
63-
builder = builder.master("local[*]").config(
59+
builder = builder.master("local[*]")
60+
61+
builder = (
62+
builder.config(
6463
"spark.sedona.stac.load.itemsLimitMax",
6564
"20",
6665
)
66+
# Pandas on PySpark doesn't work with ANSI mode, which is enabled by default
67+
# in Spark 4
68+
.config("spark.sql.ansi.enabled", "false")
69+
)
6770

6871
# Allows the Sedona .jar to be explicitly set by the caller (e.g, to run
6972
# pytest against a freshly-built development version of Sedona)

spark/common/pom.xml

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -268,6 +268,25 @@
268268
<build>
269269
<sourceDirectory>src/main/java</sourceDirectory>
270270
<plugins>
271+
<plugin>
272+
<groupId>org.codehaus.mojo</groupId>
273+
<artifactId>build-helper-maven-plugin</artifactId>
274+
<version>3.2.0</version>
275+
<executions>
276+
<execution>
277+
<id>add-source</id>
278+
<phase>generate-sources</phase>
279+
<goals>
280+
<goal>add-source</goal>
281+
</goals>
282+
<configuration>
283+
<sources>
284+
<source>src/main/scala-spark-${spark.major.version}</source>
285+
</sources>
286+
</configuration>
287+
</execution>
288+
</executions>
289+
</plugin>
271290
<plugin>
272291
<groupId>net.alchim31.maven</groupId>
273292
<artifactId>scala-maven-plugin</artifactId>
@@ -290,4 +309,23 @@
290309
</plugin>
291310
</plugins>
292311
</build>
312+
<profiles>
313+
<profile>
314+
<id>sedona-spark-4.0</id>
315+
<activation>
316+
<property>
317+
<name>spark</name>
318+
<value>4.0</value>
319+
</property>
320+
</activation>
321+
<dependencies>
322+
<dependency>
323+
<groupId>org.apache.spark</groupId>
324+
<artifactId>spark-sql-api_${scala.compat.version}</artifactId>
325+
<version>${spark.version}</version>
326+
<scope>provided</scope>
327+
</dependency>
328+
</dependencies>
329+
</profile>
330+
</profiles>
293331
</project>

0 commit comments

Comments
 (0)