Skip to content

Conversation

@HyukjinKwon
Copy link
Member

What changes were proposed in this pull request?

This PR proposes to take the super classes into account when throwing an exception from the server to Python side by adding more metadata of classes, causes and traceback in JVM.

In addition, this PR matches the exceptions being thrown to the regular PySpark exceptions defined:

def convert_exception(e: Py4JJavaError) -> CapturedException:
assert e is not None
assert SparkContext._jvm is not None
assert SparkContext._gateway is not None
jvm = SparkContext._jvm
gw = SparkContext._gateway
if is_instance_of(gw, e, "org.apache.spark.sql.catalyst.parser.ParseException"):
return ParseException(origin=e)
# Order matters. ParseException inherits AnalysisException.
elif is_instance_of(gw, e, "org.apache.spark.sql.AnalysisException"):
return AnalysisException(origin=e)
elif is_instance_of(gw, e, "org.apache.spark.sql.streaming.StreamingQueryException"):
return StreamingQueryException(origin=e)
elif is_instance_of(gw, e, "org.apache.spark.sql.execution.QueryExecutionException"):
return QueryExecutionException(origin=e)
elif is_instance_of(gw, e, "java.lang.IllegalArgumentException"):
return IllegalArgumentException(origin=e)
elif is_instance_of(gw, e, "org.apache.spark.SparkUpgradeException"):
return SparkUpgradeException(origin=e)
c: Py4JJavaError = e.getCause()
stacktrace: str = jvm.org.apache.spark.util.Utils.exceptionString(e)
if c is not None and (
is_instance_of(gw, c, "org.apache.spark.api.python.PythonException")
# To make sure this only catches Python UDFs.
and any(
map(
lambda v: "org.apache.spark.sql.execution.python" in v.toString(), c.getStackTrace()
)
)
):
msg = (
"\n An exception was thrown from the Python worker. "
"Please see the stack trace below.\n%s" % c.getMessage()
)
return PythonException(msg, stacktrace)
return UnknownException(desc=e.toString(), stackTrace=stacktrace, cause=c)

Why are the changes needed?

Right now, many exceptions cannot be handled (e.g., NoSuchDatabaseException that inherits AnalysisException) in Python side.

Does this PR introduce any user-facing change?

No to end users.
Yes, it matches the exceptions to the regular PySpark exceptions.

How was this patch tested?

Unittests fixed.

@HyukjinKwon HyukjinKwon marked this pull request as draft February 9, 2023 03:20
@HyukjinKwon HyukjinKwon force-pushed the SPARK-41715 branch 6 times, most recently from d7ecbd5 to b47faf7 Compare February 9, 2023 05:16
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Otherwise, it complains the header length (8KiB limit). It can be configured below via NettyChannelBuilder.maxInboundMessageSize but I didn't change it here, see also https://stackoverflow.com/a/686243

Copy link
Contributor

@zhenlineo zhenlineo Mar 23, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@HyukjinKwon Am I understand correctly the header limit is for the HTTP header? Can we put it in the body? Is there still a ~4K limit?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, I think we could put it in the body.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why is this needed? We have the abbreviated message already in the body

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Eh, it was already there before in metadata to print out the stacktrace from the server to the client. The exception was thrown when the size of stacktrace was too big so I made the stractrace string truncated.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The original AnalysisException.getMessage contains the string representation of the underlying plan.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Example:

spark.range(1).select("a").show()
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "/.../spark/python/pyspark/sql/connect/dataframe.py", line 776, in show
    print(self._show_string(n, truncate, vertical))
  File "/.../spark/python/pyspark/sql/connect/dataframe.py", line 619, in _show_string
    pdf = DataFrame.withPlan(
  File "/.../spark/python/pyspark/sql/connect/dataframe.py", line 1325, in toPandas
    return self._session.client.to_pandas(query)
  File "/.../spark/python/pyspark/sql/connect/client.py", line 449, in to_pandas
    table, metrics = self._execute_and_fetch(req)
  File "/.../spark/python/pyspark/sql/connect/client.py", line 636, in _execute_and_fetch
    self._handle_error(rpc_error)
  File "/.../spark/python/pyspark/sql/connect/client.py", line 670, in _handle_error
    raise convert_exception(info, status.message) from None
pyspark.errors.exceptions.connect.AnalysisException: [UNRESOLVED_COLUMN.WITH_SUGGESTION] A column or function parameter with name `a` cannot be resolved. Did you mean one of the following? [`id`].;
'Project ['a]
+- Range (0, 1, step=1, splits=Some(16))

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What's the captured one's stack trace like?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Captured one:

Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "/.../spark/python/pyspark/sql/dataframe.py", line 2987, in select
    jdf = self._jdf.select(self._jcols(*cols))
  File "/.../spark/python/lib/py4j-0.10.9.7-src.zip/py4j/java_gateway.py", line 1322, in __call__
  File "/.../sparkk/python/pyspark/errors/exceptions/captured.py", line 159, in deco
    raise converted from None
pyspark.errors.exceptions.captured.AnalysisException: [UNRESOLVED_COLUMN.WITH_SUGGESTION] A column or function parameter with name `a` cannot be resolved. Did you mean one of the following? [`id`].;
'Project ['a]
+- Range (0, 1, step=1, splits=Some(16))

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In that case, should we still show the plan to be consistent?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

eh, yeah. It still shows the plan. This is the part of getMessage.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah, got it. 👍

@HyukjinKwon HyukjinKwon force-pushed the SPARK-41715 branch 4 times, most recently from 21fa863 to eb13838 Compare February 9, 2023 07:34
@HyukjinKwon HyukjinKwon marked this pull request as ready for review February 9, 2023 07:37
Copy link
Member Author

@HyukjinKwon HyukjinKwon Feb 9, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Example:

from pyspark.sql.functions import udf
@udf
def aa(a):
    1/0

spark.range(1).select(aa("id")).show()
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "/.../spark/python/pyspark/sql/connect/dataframe.py", line 776, in show
    print(self._show_string(n, truncate, vertical))
  File "/.../spark/python/pyspark/sql/connect/dataframe.py", line 619, in _show_string
    pdf = DataFrame.withPlan(
  File "/.../spark/python/pyspark/sql/connect/dataframe.py", line 1325, in toPandas
    return self._session.client.to_pandas(query)
  File "/.../spark/python/pyspark/sql/connect/client.py", line 449, in to_pandas
    table, metrics = self._execute_and_fetch(req)
  File "/.../spark/python/pyspark/sql/connect/client.py", line 636, in _execute_and_fetch
    self._handle_error(rpc_error)
  File "/.../spark/python/pyspark/sql/connect/client.py", line 670, in _handle_error
    raise convert_exception(info, status.message) from None
pyspark.errors.exceptions.connect.PythonException:
  An exception was thrown from the Python worker. Please see the stack trace below.
Traceback (most recent call last):
  File "<stdin>", line 3, in aa
ZeroDivisionError: division by zero

@HyukjinKwon HyukjinKwon force-pushed the SPARK-41715 branch 2 times, most recently from 2435bb9 to fed524b Compare February 9, 2023 12:11
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

getLocalizedMessage is not used in our codebase.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

and the doc of setMessage mentions that it's fine to send non-localized errors (and expect the client to localize it).

@HyukjinKwon HyukjinKwon force-pushed the SPARK-41715 branch 7 times, most recently from f4d4cf6 to 041458c Compare February 9, 2023 13:06
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The error message is too long, and it gets abbreviated in Connect case.

@HyukjinKwon
Copy link
Member Author

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What's the captured one's stack trace like?

Copy link
Member

@ueshin ueshin left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM, pending tests.

@HyukjinKwon
Copy link
Member Author

Merged to master and branch-3.4.

HyukjinKwon added a commit that referenced this pull request Feb 10, 2023
…n throwing an exception

### What changes were proposed in this pull request?

This PR proposes to take the super classes into account when throwing an exception from the server to Python side by adding more metadata of classes, causes and traceback in JVM.

In addition, this PR matches the exceptions being thrown to the regular PySpark exceptions defined:

https://github.com/apache/spark/blob/04550edd49ee587656d215e59d6a072772d7d5ec/python/pyspark/errors/exceptions/captured.py#L108-L147

### Why are the changes needed?

Right now, many exceptions cannot be handled (e.g., `NoSuchDatabaseException` that inherits `AnalysisException`) in Python side.

### Does this PR introduce _any_ user-facing change?

No to end users.
Yes, it matches the exceptions to the regular PySpark exceptions.

### How was this patch tested?

Unittests fixed.

Closes #39947 from HyukjinKwon/SPARK-41715.

Authored-by: Hyukjin Kwon <gurwls223@apache.org>
Signed-off-by: Hyukjin Kwon <gurwls223@apache.org>
(cherry picked from commit c5230e4)
Signed-off-by: Hyukjin Kwon <gurwls223@apache.org>
--------
>>> df = spark.createDataFrame([(1, 11), (1, 11), (3, 10), (4, 8), (4, 8)], ["c1", "c2"])
>>> df.freqItems(["c1", "c2"]).show() # doctest: +SKIP
>>> df.freqItems(["c1", "c2"]).show()
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

HyukjinKwon pushed a commit that referenced this pull request Feb 13, 2023
…octest due to Scala 2.13 failure

### What changes were proposed in this pull request?

This is a follow-up of #39947 to ignore `freqItems` doctest back.

### Why are the changes needed?

### Does this PR introduce _any_ user-facing change?

### How was this patch tested?

Closes #39983 from dongjoon-hyun/SPARK-40453.

Authored-by: Dongjoon Hyun <dongjoon@apache.org>
Signed-off-by: Hyukjin Kwon <gurwls223@apache.org>
HyukjinKwon pushed a commit that referenced this pull request Feb 13, 2023
…octest due to Scala 2.13 failure

### What changes were proposed in this pull request?

This is a follow-up of #39947 to ignore `freqItems` doctest back.

### Why are the changes needed?

### Does this PR introduce _any_ user-facing change?

### How was this patch tested?

Closes #39983 from dongjoon-hyun/SPARK-40453.

Authored-by: Dongjoon Hyun <dongjoon@apache.org>
Signed-off-by: Hyukjin Kwon <gurwls223@apache.org>
(cherry picked from commit d703808)
Signed-off-by: Hyukjin Kwon <gurwls223@apache.org>
snmvaughan pushed a commit to snmvaughan/spark that referenced this pull request Jun 20, 2023
…n throwing an exception

### What changes were proposed in this pull request?

This PR proposes to take the super classes into account when throwing an exception from the server to Python side by adding more metadata of classes, causes and traceback in JVM.

In addition, this PR matches the exceptions being thrown to the regular PySpark exceptions defined:

https://github.com/apache/spark/blob/04550edd49ee587656d215e59d6a072772d7d5ec/python/pyspark/errors/exceptions/captured.py#L108-L147

### Why are the changes needed?

Right now, many exceptions cannot be handled (e.g., `NoSuchDatabaseException` that inherits `AnalysisException`) in Python side.

### Does this PR introduce _any_ user-facing change?

No to end users.
Yes, it matches the exceptions to the regular PySpark exceptions.

### How was this patch tested?

Unittests fixed.

Closes apache#39947 from HyukjinKwon/SPARK-41715.

Authored-by: Hyukjin Kwon <gurwls223@apache.org>
Signed-off-by: Hyukjin Kwon <gurwls223@apache.org>
(cherry picked from commit c5230e4)
Signed-off-by: Hyukjin Kwon <gurwls223@apache.org>
snmvaughan pushed a commit to snmvaughan/spark that referenced this pull request Jun 20, 2023
…octest due to Scala 2.13 failure

### What changes were proposed in this pull request?

This is a follow-up of apache#39947 to ignore `freqItems` doctest back.

### Why are the changes needed?

### Does this PR introduce _any_ user-facing change?

### How was this patch tested?

Closes apache#39983 from dongjoon-hyun/SPARK-40453.

Authored-by: Dongjoon Hyun <dongjoon@apache.org>
Signed-off-by: Hyukjin Kwon <gurwls223@apache.org>
(cherry picked from commit d703808)
Signed-off-by: Hyukjin Kwon <gurwls223@apache.org>
@HyukjinKwon HyukjinKwon deleted the SPARK-41715 branch January 15, 2024 00:48
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants