aminghadersohi commented on code in PR #45348:
URL: https://github.com/apache/superset/pull/45348#discussion_r4289169223


##########
superset/sql/execution/celery_task.py:
##########
@@ -298,33 +307,31 @@ def _store_results_in_backend(
 
 def _serialize_result_set(
     result_set: SupersetResultSet,
-) -> tuple[bytes | list[Any], list[Any]]:
+) -> tuple[bytes | str, list[Any]]:
     """
     Serialize result set based on RESULTS_BACKEND_USE_MSGPACK config.
 
-    When msgpack is enabled, uses Apache Arrow IPC format for efficiency.
-    Otherwise, falls back to JSON-serializable records.
+    Both transports use Apache Arrow IPC to retain the result types. JSON
+    wraps the IPC bytes in Base64; MessagePack carries them directly.
 
     :param result_set: Query result set to serialize
     :returns: Tuple of (serialized_data, columns)
     """
     from superset import results_backend_use_msgpack
-    from superset.dataframe import df_to_records
 
-    if results_backend_use_msgpack:
-        if has_app_context():
-            stats_logger = app.config["STATS_LOGGER"]
-            with stats_timing(
-                "sqllab.query.results_backend_pa_serialization", stats_logger
-            ):
-                data: bytes | list[Any] = write_ipc_buffer(
-                    result_set.pa_table
-                ).to_pybytes()
-        else:
-            data = write_ipc_buffer(result_set.pa_table).to_pybytes()
+    if has_app_context():
+        stats_logger = app.config["STATS_LOGGER"]
+        with stats_timing(
+            "sqllab.query.results_backend_pa_serialization", stats_logger
+        ):
+            arrow_data = write_ipc_buffer(result_set.pa_table).to_pybytes()
     else:
-        df = result_set.to_pandas_df()
-        data = df_to_records(df, convert_decimals=True) or []
+        arrow_data = write_ipc_buffer(result_set.pa_table).to_pybytes()
+    data = (
+        arrow_data
+        if results_backend_use_msgpack
+        else base64.b64encode(arrow_data).decode("ascii")

Review Comment:
   You're right: the Base64 storage format is intentional, but charging its 
encoding overhead against the result-size limit was not. I reproduced the 10 
MiB-limit regression and corrected the accounting in 
e63443ac54bc300f61027b7cb50784b528473c8d, retained after merging master in 
d205c6fd3ac016d58b688b3eec250f974491eb0c.
   
   Measured bytes using the real `_finalize_successful_query` / 
`_serialize_payload` functions from master 
13bd0cd932632522ddc28d193a1057f8f487691c and the reviewed head 
f873c74a4a9fc2f3340b81d88bf190bda4e46b87 (PyArrow 25.0.1; identical result sets 
and fixed query metadata):
   
   | Result / transport | Master, before compression | Reviewed head, before 
compression | Master / reviewed head, after zlib |
   | --- | ---: | ---: | ---: |
   | 100,000 mixed rows, JSON | 20,473,282 | 15,635,314 | 1,449,670 / 1,613,220 
|
   | 100,000 mixed rows, MessagePack | 11,726,451 | 11,726,451 | 1,248,333 / 
1,248,333 |
   | One 8 MiB text value, JSON | 8,389,027 | 11,185,615 | 8,436 / 11,317 |
   | One 8 MiB text value, MessagePack | 8,389,217 | 8,389,217 | 8,552 / 8,552 |
   
   The mixed rows contain an integer ID, an 81-character string, float, 
boolean, timestamp, and nullable integer. The text case is one string of `8 * 
1024 * 1024` ASCII characters. Its raw IPC is 8,388,904 bytes; Base64 is 
11,185,208 bytes. The mixed case is smaller than master, but the text case 
confirms that compression does not prevent the rejection: the check runs before 
compression.
   
   At the reviewed head, `superset/sql/execution/celery_task.py:250-265` 
compares `len(serialized_payload)` with `SQLLAB_PAYLOAD_MAX_MB * 1024 * 1024`. 
The new `_get_payload_size` calculation substitutes raw IPC length for each 
Base64 statement value, retaining the envelope, metadata, and nested sidecars. 
It does not change the stored bytes or use compressed size. The text case is 
charged 8,389,311 bytes in JSON mode, below 10 MiB. Arrow/schema/envelope sizes 
can still differ from the former record format; this removes the Base64 penalty 
rather than promising byte-identical serialization.
   
   Regression tests added by this PR:
   - 
`tests/unit_tests/sql/execution/test_async_results.py::test_async_text_payload_limit_excludes_base64_overhead`:
 actual 8 MiB writer/reader round-trip under a 10 MiB limit, one/two SELECT 
statements and both transports. Both JSON cases fail at f873c74a and pass at 
e63443ac.
   - 
`tests/unit_tests/sql/execution/test_celery_task.py::test_store_results_in_backend_raw_arrow_size_boundary`:
 exact-limit acceptance, one-byte-over rejection without writing, all three 
Base64 padding lengths, DML, multiple SELECTs, metadata/sidecar accounting, and 
unchanged stored bytes. Three JSON acceptance cases fail before the change and 
pass afterward; MessagePack and rejection cases are controls.
   
   The focused command (`pytest 
tests/unit_tests/sql/execution/test_async_results.py 
tests/unit_tests/sql/execution/test_celery_task.py -k 'text_payload_limit or 
raw_arrow_size_boundary'`) gives 5 failed / 11 passed before and 16 passed 
after. `pytest tests/unit_tests/sql/ -m 'not manual and not llm_compliance'` at 
the merged head gives 2,634 passed / 2 skipped (the optional Impala driver is 
not installed); pre-commit on the branch's changed files, including mypy, 
passes. The legacy payload-limit tests also pass (4 selected tests, including 
the JSON normalization control).
   
   Siblings checked:
   - superset/sql/execution/celery_task.py:252 — fixed in 
d205c6fd3ac016d58b688b3eec250f974491eb0c
   - superset/sql/execution/celery_task.py:294 — fixed in 
e63443ac54bc300f61027b7cb50784b528473c8d
   - superset/sql/execution/celery_task.py:200 — not affected: every SELECT 
generated here reaches the shared storage check; the regression covers one and 
multiple statements, including DML at the boundary.
   - superset/sql/execution/celery_task.py:665 — fixed in 
d205c6fd3ac016d58b688b3eec250f974491eb0c
   - superset/sql_lab.py:714 — not affected: legacy backend storage uses JSON 
records or raw MessagePack Arrow, not Base64 Arrow; its existing 
pre-compression `sys.getsizeof` comparison is unchanged.
   - superset/sql_lab.py:803 — not affected: the return-results branch converts 
Arrow back to records before its pre-compression `sys.getsizeof` check.
   - superset/sql/execution/celery_task.py:320 — not affected: 
serialized/compressed byte sizes here are logging only and continue to report 
actual storage sizes.
   - superset/sql_lab.py:734 — not affected: serialized/compressed byte sizes 
here are logging only, not additional limit checks.
   - superset/config.py:2007 — not affected: the opt-in limit/default is 
unchanged.
   - superset/errors.py:90 — not affected: error enum and its code mapping at 
line 196 do not measure payloads.
   - superset/utils/error_sanitization.py:63 — not affected: error 
classification does not measure payloads.
   - docs/static/resources/openapi.json:121 — not affected: error-enum 
documentation only.
   - 
docs/developer_docs_versioned_docs/version-6.1.0/api/get-the-result-of-a-sql-query-execution.StatusCodes.json:179
 — not affected: error-enum documentation only.
   - tests/unit_tests/sql_lab_test.py:183 — not affected: legacy within-limit 
and over-limit controls pass; no Base64 producer in that path.
   - tests/unit_tests/sql/execution/test_celery_task.py:779 — fixed in 
e63443ac54bc300f61027b7cb50784b528473c8d
   - tests/unit_tests/sql/execution/test_async_results.py:137 — fixed in 
e63443ac54bc300f61027b7cb50784b528473c8d
   - docs/developer_docs/extensions/mcp.md:47 — fixed in 
e63443ac54bc300f61027b7cb50784b528473c8d
   
   Search: `rg -n 
'SQLLAB_PAYLOAD_MAX_MB|RESULT_TOO_LARGE_ERROR|serialized_payload_size|(?:len|(?:sys\.)?getsizeof)\((?:serialized_payload|compressed)\)'
 superset tests docs`, plus searches for `_store_results_in_backend`, 
`_serialize_result_set`, and `_serialize_and_expand_data` callers.
   



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