EnxDev opened a new pull request, #44425:
URL: https://github.com/apache/superset/pull/44425

   ### SUMMARY
   
   Exporting a chart or dashboard to CSV/Excel fails when the row limit is 
100,000 or higher and the dataset sits on a BigQuery connection that 
authenticates with OAuth2. The file downloads, but it contains an error marker 
instead of any rows.
   
   At or above `CSV_STREAMING_ROW_THRESHOLD` (100k by default) the export 
switches from the buffered path to the streaming one. The streaming path hands 
Werkzeug a generator and returns, so the query actually runs *after* Flask has 
torn down the request context. To cope with that, the command snapshots 
`flask.g` and replays it inside a fresh app context.
   
   The snapshot copied `g.user` as-is. Flask-AppBuilder sets that to 
Flask-Login's `current_user`, which is a `LocalProxy` that checks 
`has_request_context()` and resolves to `None` once the request is over. So the 
generator ended up running as nobody at all. The guard in 
`Database._get_sqla_engine` (`hasattr(g.user, "id")`) is then `False`, the 
engine is built with `access_token=None`, and the user's OAuth2 token never 
reaches BigQuery. The connection fails, the generator's catch-all writes a 
`__STREAM_ERROR__` marker into the response body, and that marker is what ends 
up in the downloaded file.
   
   That also explains the two things that made this look odd:
   
   - it only reproduces at exactly 100k rows, because below the threshold the 
buffered path never leaves the request context;
   - it only reproduces with OAuth2, because service account credentials live 
on the connection itself and don't depend on who is asking.
   
   The fix resolves request-bound proxies to the concrete object they point at 
while the request context is still alive, so the generator sees the same acting 
user as every other place that acquires an engine. Impersonation and 
`start_oauth2_dance` were losing the user for the same reason and are fixed 
along with it.
   
   Worth flagging: there was already a test claiming to pin this behaviour, but 
it set `g.user` to a plain `Mock`, which survives being copied, so it passed 
while production broke. The new test uses a real `LocalProxy` across a genuine 
request boundary and fails without the fix.
   
   One thing this does not solve: if a user has no token at all, or no refresh 
token, the streaming path still cannot prompt them to re-authenticate, because 
the response headers have already gone out by the time we find out. Handling 
that means checking auth before streaming starts, which felt like a separate 
change rather than something to bundle in here.
   
   ### BEFORE/AFTER SCREENSHOTS OR ANIMATED GIF
   
   Not applicable, backend only.
   
   ### TESTING INSTRUCTIONS
   
   Manually:
   
   1. Create a BigQuery database connection using OAuth2, and a dataset on top 
of it.
   2. Build a simple table chart on that dataset and set the row limit to 
100000.
   3. Download the chart as CSV or Excel.
   
   Before this change the file contains a `__STREAM_ERROR__` marker and no 
rows. After it the export completes normally. Anything below 100000 rows was 
already working and still does.
   
   Automated:
   
   ```bash
   pytest tests/unit_tests/commands/chart/streaming_export_command_test.py \
          tests/unit_tests/commands/sql_lab/streaming_export_command_test.py
   ```
   
   ### ADDITIONAL INFORMATION
   
   - [ ] Has associated issue:
   - [ ] Required feature flags:
   - [ ] Changes UI
   - [ ] Includes DB Migration (follow approval process in 
[SIP-59](https://github.com/apache/superset/issues/13351))
     - [ ] Migration is atomic, supports rollback & is backwards-compatible
     - [ ] Confirm DB migration upgrade and downgrade tested
     - [ ] Runtime estimates and downtime expectations provided
   - [ ] Introduces new feature or API
   - [ ] Removes existing feature or API
   


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