BigQuery execute_sql blocks the event loop for the whole query
维护者通常 6 天内回复
@surajksharma07 已经在做这个了。
开始于 2026年9月17日。
评估
这个 Issue 还没有评估数据。
描述
🔴 Required Information
Describe the Bug:
execute_sql in the BigQuery toolset is a plain def, and the BigQuery client it calls blocks (src/google/adk/integrations/bigquery/query_tool.py, L341 and the _execute_sql helper it delegates to at L169). ADK awaits a sync tool inline on the running loop, so nothing else in the process runs until the query comes back. On a server handling more than one session, the others just wait.
Spanner hit the same thing and was fixed in 1dbceccf. Bigtable's execute_sql is async too. BigQuery still has no asyncio.to_thread anywhere.
Steps to Reproduce:
uv pip install "google-adk[gcp]==2.9.0"- Save the script below (also under Minimal Reproduction Code) as
repro.py. It replaces the BigQuery client with one that takes 2 seconds, so no credentials or real dataset are needed, and counts how many times a 100 ms heartbeat task gets to run during the query. python repro.py
Expected Behavior:
The heartbeat keeps ticking every 100 ms while the query is in flight.
Observed Behavior:
It doesn't tick at all until the query returns:
heartbeat ran 3 times
longest gap between heartbeats: 2.108s
The gap is the query duration.
Environment Details:
- ADK Library Version (pip show google-adk): 2.9.0. Also checked
mainat 7ae1c9b0, still sync there. - Desktop OS: macOS 25.6.0 (arm64)
- Python Version (python -V): 3.11.12, in a uv venv set up per the contribution guide (
uv venv --python "python3.11"+uv sync --all-extras)
Model Information:
- Are you using LiteLLM: No
- Which model is being used: N/A, no model involved
🟡 Optional Information
Regression:
No, it has always been sync.
Additional Context:
Same fix as Spanner: make execute_sql a coroutine and push _execute_sql into a thread. _execute_sql has to stay sync because forecast, analyze_contribution and detect_anomalies call it directly. Those three block the loop as well, but that's a separate change.
I have a patch and tests ready, so please assign this to me.
Minimal Reproduction Code:
import asyncio
import time
from unittest import mock
from google.adk.integrations.bigquery import query_tool
from google.adk.integrations.bigquery.config import BigQueryToolConfig
from google.adk.tools.tool_context import ToolContext
from google.auth.credentials import Credentials
from google.cloud import bigquery
QUERY_SECONDS = 2.0
async def heartbeat(stop):
ticks = []
start = time.monotonic()
while not stop.is_set():
await asyncio.sleep(0.1)
ticks.append(time.monotonic() - start)
return ticks
def slow_query_and_wait(*args, **kwargs):
time.sleep(QUERY_SECONDS)
return [{"num": 123}]
async def main():
credentials = mock.create_autospec(Credentials, instance=True)
tool_context = mock.create_autospec(ToolContext, instance=True)
settings = BigQueryToolConfig()
with mock.patch.object(bigquery, "Client", autospec=True) as client:
bq_client = client.return_value
query_job = mock.create_autospec(bigquery.QueryJob)
query_job.statement_type = "SELECT"
bq_client.query.return_value = query_job
bq_client.query_and_wait.side_effect = slow_query_and_wait
stop = asyncio.Event()
beat = asyncio.create_task(heartbeat(stop))
await asyncio.sleep(0.3)
result = query_tool.execute_sql(
"my_project", "SELECT 123 AS num", credentials, settings,
tool_context,
)
if asyncio.iscoroutine(result):
result = await result
stop.set()
ticks = await beat
gaps = [b - a for a, b in zip(ticks, ticks[1:])]
print(f"heartbeat ran {len(ticks)} times")
print(f"longest gap between heartbeats: {max(gaps):.3f}s")
print(f"result: {result}")
asyncio.run(main())
How often has this issue occurred?:
- Always (100%)
- 主要语言
- Python
- 星标
- 21.6k
- 派生
- 4k
- 平均合并
- 9 小时 59 分钟
- 30 天内合并 PR
- 5
环境准备
从这里开始
- 先读完整个 Issue,再读项目的贡献指南。
- 在 Issue 下留言说明你要接手 —— 这能避免两个人做同样的事。
- Fork 仓库,在一个分支上完成修改。
- 提交 Pull Request,并在描述里引用这个 Issue 编号。
google/adk-python 的其他 Issue
-
dependencies/vertexai.py eagerly imports the deprecated vertexai.preview.rag, so every evaluation import warns可能已有人在做 @a2105z 今天认领。 未关闭
难度 2/5 1-3 小时 新手友好度 74/100
google/adk-python#7415 · 2 条评论 ·
维护者通常 6 天内回复
-
CI Mypy Check flags an existing streaming_utils.py error as new because the PR run reuses the baseline's mypy cache可能已有人在做 @jennymeshaiah09 今天认领。 未关闭
难度 2/5 1-3 小时 新手友好度 72/100
google/adk-python#7409 ·
维护者通常 6 天内回复
-
A2aAgentExecutor sends the raw exception text to the A2A caller when the run fails可能已有人在做 关联的 PR 仍在进行中或已合并。 未关闭a2a
难度 2/5 1-3 小时 新手友好度 84/100
google/adk-python#7385 · 1 条评论 · 已指派 1 人 ·
维护者通常 6 天内回复
-
web
难度 2/5 1-3 小时 新手友好度 62/100
google/adk-python#7381 · 已指派 1 人 ·
维护者通常 6 天内回复
-
[A2A] RemoteA2aAgent(use_legacy=False): extension header written to state['http_kwargs'], ignored by a2a-sdk 1.x transports可能已有人在做 @surajksharma07 于 7 天前认领。 未关闭
难度 2/5 1-3 小时 新手友好度 84/100
google/adk-python#7334 · 2 条评论 · 已指派 1 人 ·
维护者通常 6 天内回复
查看 google/adk-python 的全部 Issue
相似的 Issue
-
needs-human needs-triage
难度 2/5 1-3 小时 新手友好度 76/100
gke-labs/kube-agents#2400 · 1 条评论 ·
维护者通常 1 天内回复
-
Device Details tables: FS/SF columns contradict each other (nfet_01v8 Vt row, pfet_01v8 Idsat row)未关闭
难度 2/5 1-3 小时 新手友好度 75/100
google/skywater-pdk#450 ·
-
Drained trajectory arrays are overwritten when the sequence buffer is reused可能已有人在做 @sylvesterkaczmarek 今天认领。 未关闭
难度 2/5 1-3 小时 新手友好度 78/100
google-deepmind/bsuite#56 ·
-
难度 2/5 1-3 小时 新手友好度 82/100
LearningCircuit/local-deep-research#7206 ·
维护者通常 1 天内回复
-
难度 2/5 1-3 小时 新手友好度 68/100
chingu-voyages/V62-tier3-team-33#285 ·
维护者通常 1 天内回复