verdier opened a new issue, #42567:
URL: https://github.com/apache/superset/issues/42567

   ### Bug description
   
   The built-in MCP service's `generate_chart` is not safe under modest 
concurrency. From 5 concurrent calls, most calls raise `DetachedInstanceError` 
on the freshly created `Slice` — **after the chart has already been 
committed**. The client receives an error for a chart that exists.
   
   For an LLM agent this is the worst possible shape: the tool reports failure, 
the agent retries, and each retry creates another chart. Duplicate charts 
accumulate silently.
   
   ```
   superset.mcp_service.chart.tool.generate_chart:Chart generation failed:
   Instance <Slice at 0x7ac59a7dc310> is not bound to a Session;
   attribute refresh operation cannot proceed
   
     File "/app/superset/mcp_service/chart/tool/generate_chart.py", line 828, 
in generate_chart
     File ".../sqlalchemy/orm/attributes.py", line 487, in __get__
     File ".../sqlalchemy/orm/state.py", line 712, in _load_expired
     File ".../sqlalchemy/orm/loading.py", line 1369, in load_scalar_attributes
   sqlalchemy.orm.exc.DetachedInstanceError
   ```
   
   `generate_chart.py:828` is the completion log, which reads `chart.id` after 
the transaction has committed and the instance has been expired/detached:
   
   ```python
   await ctx.info(
       "Chart generation completed successfully: chart_id=%s, 
execution_time_ms=%s"
       % (chart.id if chart else None, ...)
   )
   ```
   
   ### How to reproduce
   
   `apache/superset:6.1.0-py311`, `fastmcp==3.4.5`, `superset mcp run`, 
`stateless_http=True`, stock SQLAlchemy pool. N concurrent `generate_chart` 
calls against one physical dataset via `asyncio.gather`, then read back the 
`slices` table.
   
   | concurrency | successful responses | rows actually created |
   |---|---|---|
   | 1  | 1/1   | 1 |
   | 2  | 2/2   | 2 |
   | 5  | 2/5   | 5 |
   | 10 | 4/10  | 10 |
   | 20 | 1/20  | **3** |
   
   Two things worth noting:
   
   - **The highest fully clean concurrency is 2.** Two users asking an agent 
for a chart at the same time is enough to reach it.
   - At concurrency 20 the writes themselves start disappearing (3 rows for 20 
calls), so this is not purely a response-serialisation problem.
   
   Assertions are read back from the metadata DB rather than from the MCP 
client result, because the error middleware returns the failure as `Error: ...` 
text while the SDK's `result.is_error` stays false — a green client flag can 
hide a total failure here, which may be worth a separate look.
   
   ### Not caused by local customisation
   
   We ran this while evaluating the built-in MCP for a deployment that needs 
per-user identity. To rule ourselves out, we ran the identical workload on 
**stock 6.1 with no customisation at all**, in single-user mode 
(`MCP_DEV_USERNAME`):
   
   - stock, unmodified: **5/40** successful responses
   - with our own identity bridge installed: **19/40**
   
   So the unmodified configuration performs *worse*. The defect is in the 
built-in MCP itself.
   
   ### `generate_dashboard` is unaffected, and that looks deliberate
   
   `generate_dashboard` passes 40/40 under the same load. It re-queries the 
user by id inside the tool's own session before use 
(`superset/mcp_service/dashboard/tool/generate_dashboard.py:301-317`), with a 
comment describing exactly this class of problem. So the pattern appears to be 
understood — it just has not been applied to the chart path.
   
   This also makes `generate_dashboard` a misleading tool to benchmark 
concurrency with: it produces a false green.
   
   ### Question about `master`
   
   `master` has since restructured this path — `chart_id` is captured 
immediately after `command.run()`, and `db.session.refresh(chart)` is wrapped 
in a guarded `try/except SQLAlchemyError`. But `chart.id` and 
`chart.slice_name` are still read afterwards, outside that guard.
   
   We have not been able to test `master`, so we cannot say whether the 
concurrency case is resolved there. Two questions:
   
   1. Is this considered fixed on `master`?
   2. If so, is the fix expected in 6.2 — and would a 6.1.x backport be 
considered? The identity chain in `superset/mcp_service/auth.py` 
(`_resolve_user_from_jwt_context` and the `claims`-first 
`default_user_resolver`) landed just after the 6.1.0 tag as well, so 6.1.0 
users currently have neither.
   
   Happy to run any diagnostic against 6.1.0, or to test a candidate patch — we 
have a scripted reproduction and can turn it around quickly.
   
   ### Superset version
   
   6.1.0
   
   ### Python version
   
   3.11
   
   ### Node version
   
   Not applicable
   
   ### Browser
   
   Not applicable
   
   ### Additional context
   
   Reported by [Veremes](https://www.veremes.com), evaluating the built-in MCP 
service for a production Superset deployment.
   
   ### Checklist
   
   - [x] I have searched Superset docs and Slack and didn't find a solution to 
my problem.
   - [x] I have searched the GitHub issue tracker and didn't find a similar bug 
report.
   - [x] I have checked Superset's logs for errors and if I found a relevant 
Python stacktrace, I included it here as text or in a screenshot.
   


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