BigQuery execute_sql blocks the event loop for the whole query
メンテナーはふだん 5 日以内に返信
@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.8k
- フォーク
- 4.1k
- 平均マージ
- 1日 13時間
- マージ済み PR(30日)
- 6
環境構築
- Dockerfile・Docker Compose ファイルなし
- プルリクエストのテンプレートあり
- コントリビューションガイドを読む
はじめの一歩
- issue を最後まで読み、次にプロジェクトのコントリビューションガイドを読みます。
- 着手することを issue にコメントします — 二人が同じ作業をするのを防げます。
- リポジトリをフォークし、ブランチを切って変更します。
- issue 番号を参照したプルリクエストを送ります。
google/adk-python のほかの issue
-
Update opentelemetry-api and opentelemetry-sdk to 1.44.0対応中かも @llalitkumarrr が 2 日前に担当しました。 オープンrequest clarification tracing
難易度 2/5 1〜3時間 初心者へのやさしさ 70/100
google/adk-python#7433 · コメント 4 件 · 担当者 1 名 ·
メンテナーはふだん 5 日以内に返信
-
CI Mypy Check flags an existing streaming_utils.py error as new because the PR run reuses the baseline's mypy cache対応中かも @DeanChensj が 4 日前に担当しました。 オープンneeds review
難易度 2/5 1〜3時間 初心者へのやさしさ 72/100
google/adk-python#7409 · コメント 1 件 · 担当者 2 名 ·
メンテナーはふだん 5 日以内に返信
-
A2aAgentExecutor sends the raw exception text to the A2A caller when the run fails対応中かも @sanketpatil06 が 5 日前に担当しました。 オープンa2a request clarification
難易度 2/5 1〜3時間 初心者へのやさしさ 84/100
google/adk-python#7385 · コメント 2 件 · 担当者 1 名 ·
メンテナーはふだん 5 日以内に返信
-
Please support mermaid 12 (inbuild elk) in `adk web`対応中かも @sanketpatil06 が 5 日前に担当しました。 オープンneeds review web
難易度 2/5 1〜3時間 初心者へのやさしさ 62/100
google/adk-python#7381 · コメント 1 件 · 担当者 1 名 ·
メンテナーはふだん 5 日以内に返信
-
[A2A] RemoteA2aAgent(use_legacy=False): extension header written to state['http_kwargs'], ignored by a2a-sdk 1.x transports対応中かも @surajksharma07 が 11 日前に担当しました。 オープンa2a
難易度 2/5 1〜3時間 初心者へのやさしさ 84/100
google/adk-python#7334 · コメント 2 件 · 担当者 1 名 ·
メンテナーはふだん 5 日以内に返信
google/adk-python の issue をすべて見る
似ている issue
-
難易度 2/5 1〜3時間 初心者へのやさしさ 82/100
aicell-lab/bioengine#232 ·
メンテナーはふだん 1 日以内に返信
-
難易度 2/5 1〜3時間 初心者へのやさしさ 74/100
modelscope/evalscope#1836 ·
メンテナーはふだん 1 日以内に返信
-
難易度 2/5 1〜3時間 初心者へのやさしさ 72/100
-
bug
難易度 1/5 1時間未満 初心者へのやさしさ 78/100
メンテナーはふだん 1 日以内に返信
-
難易度 2/5 1〜3時間 初心者へのやさしさ 72/100
jbaruch/speaker-toolkit#480 ·
メンテナーはふだん 1 日以内に返信