[Bug] lambda_worker otel.apply_defaults leaks a span exporter thread and gRPC channel on every warm invocation
メンテナーはふだん 1 日以内に返信
評価
- 難易度
- 4/5
- 見積もり時間
- 1〜2日
- 初心者へのやさしさ
- 30/100
- issue の種類
- バグ
- 明瞭さ
- 明確に書かれている
- 活発さ
- 停滞
- 技術スタック
- aws, grpc, python
- 領域
- backend, observability
調査の方向性
Start in temporalio/contrib/aws/lambda_worker/otel.py at apply_defaults, then read the run_worker configuration path and its existing tests. The issue provides a minimal reproduction using _run_worker_internal; run it across repeated invocations and check whether exporter threads and gRPC channels keep accumulating. Done means repeated calls do not grow those resources, including when another provider already owns the global slot.
索引モデルが issue の本文から書いたものです。
説明
What are you really trying to do?
Run a Temporal worker on AWS Lambda with tracing, following the README "Observability" example: call otel.apply_defaults(config, OtelOptions()) inside the configure callback passed to run_worker.
Describe the bug
run_worker calls the user's configure callback on every Lambda invocation (its docstring says so), and a warm container keeps the same process alive. apply_defaults builds a new replay-safe TracerProvider with a new BatchSpanProcessor(OTLPSpanExporter) each time (one daemon export thread and one gRPC channel) and then calls set_tracer_provider(). OpenTelemetry only lets the global provider be set once, so from the second invocation on:
- OpenTelemetry logs
Overriding of current TracerProvider is not allowed, - the new provider is never installed and never used (
OpenTelemetryPlugintakes the global one), and - it is never shut down, so its export thread and channel stay alive for the life of the container.
Result: one extra OtelBatchSpanRecordProcessor thread and gRPC channel per warm invocation, forever.
Expected: the documented per-invocation configure + apply_defaults pattern should be safe to run repeatedly, i.e. no growth in threads or channels across warm invocations.
Minimal Reproduction
Drives the real _run_worker_internal handler with a fake connect and a fake worker, so it needs no Lambda and no collector:
import gc, logging, threading
from opentelemetry import trace
from temporalio.common import WorkerDeploymentVersion
from temporalio.contrib.aws.lambda_worker import LambdaWorkerConfig, otel
from temporalio.contrib.aws.lambda_worker._run_worker import _run_worker_internal, _WorkerDeps
from temporalio.envconfig import ClientConfigProfile
logging.basicConfig(level=logging.WARNING, format="LOG %(levelname)s %(name)s: %(message)s")
class FakeWorker:
async def run(self):
return
async def fake_connect(**kwargs):
return object()
def configure(config: LambdaWorkerConfig) -> None: # the README example
config.worker_config["task_queue"] = "tq"
otel.apply_defaults(config, otel.OtelOptions())
deps = _WorkerDeps(
connect=fake_connect,
create_worker=lambda client, **kw: FakeWorker(),
load_config=lambda: ClientConfigProfile(address="localhost:7233"),
getenv=lambda k: None,
)
handler = _run_worker_internal(WorkerDeploymentVersion("svc", "v1"), configure, deps)
providers = []
for invocation in range(1, 6):
handler({}, None) # one Lambda invocation in a warm container
gc.collect()
providers.append(trace.get_tracer_provider())
n = sum(1 for t in threading.enumerate() if t.name == "OtelBatchSpanRecordProcessor")
print(f"after invocation {invocation}: OTLP span-export threads alive={n}, total threads={threading.active_count()}")
print("global tracer provider is the same object every time:", len({id(p) for p in providers}) == 1)
Output on main:
LOG WARNING opentelemetry.trace: Overriding of current TracerProvider is not allowed (x4)
after invocation 1: OTLP span-export threads alive=1, total threads=2
after invocation 2: OTLP span-export threads alive=2, total threads=3
after invocation 3: OTLP span-export threads alive=3, total threads=4
after invocation 4: OTLP span-export threads alive=4, total threads=5
after invocation 5: OTLP span-export threads alive=5, total threads=6
global tracer provider is the same object every time: True
Environment/Versions
- OS and processor: macOS (Apple Silicon), Python 3.12.14
- Temporal Version:
temporaliomainat2a7d321c(version 1.35.0), with the native bridge from the 1.34.0 wheel; only pure-Python code frommaindiffers. Extrasopentelemetry-exporter-otlp-proto-grpcandopentelemetry-sdk-extension-awsinstalled. - Docker or Kubernetes or building Temporal from source? No. This is a simulated invocation loop; I have not run it on a real Lambda or against a real collector.
Additional context
The relevant code is temporalio/contrib/aws/lambda_worker/otel.py (apply_defaults). I have a fix ready that installs the provider once per process and reuses it on later calls (and shuts an unused provider down when another provider already owns the global slot, e.g. ADOT auto-instrumentation), with tests. Design notes I would like your view on: the first call wins (endpoint and service name are fixed for the process, which matches how they are configured today), and a foreign global provider still triggers OpenTelemetry's own "Overriding" warning on each call.
- 主要言語
- Python
- スター
- 1.2k
- フォーク
- 285
- 平均マージ
- 2日 3時間
- マージ済み PR(30日)
- 79
環境構築
- Dockerfile・Docker Compose ファイルなし
- プルリクエストのテンプレートなし
- コントリビューションガイドを読む
はじめの一歩
- issue を最後まで読み、次にプロジェクトのコントリビューションガイドを読みます。
- 着手することを issue にコメントします — 二人が同じ作業をするのを防げます。
- リポジトリをフォークし、ブランチを切って変更します。
- issue 番号を参照したプルリクエストを送ります。
temporalio/sdk-python のほかの issue
-
[Bug] Pydantic converter writes computed fields that models with extra="forbid" then reject対応中かも @sohom-cs が 1 日前に担当しました。 オープン
難易度 3/5 半日 初心者へのやさしさ 40/100
temporalio/sdk-python#2052 ·
メンテナーはふだん 1 日以内に返信
-
Cloud CI Skips Nexus Tests再び着手できるかも @tconley1428 が 81 日前に担当しましたが、オープン中のプルリクエストはありません。 オープン
temporalio/sdk-python#1704 · 担当者 1 名 ·
メンテナーはふだん 1 日以内に返信
-
難易度 4/5 3〜5日 初心者へのやさしさ 45/100
temporalio/sdk-python#1592 · コメント 8 件 ·
メンテナーはふだん 1 日以内に返信
-
bug
難易度 4/5 3〜5日 初心者へのやさしさ 48/100
temporalio/sdk-python#1578 · コメント 2 件 · リアクション 2 件 ·
メンテナーはふだん 1 日以内に返信
-
[Bug] Headers should always run through external storage再び着手できるかも @jmaeagle99 が 117 日前に担当しましたが、オープン中のプルリクエストはありません。 オープンbug
temporalio/sdk-python#1395 · 担当者 1 名 ·
メンテナーはふだん 1 日以内に返信
temporalio/sdk-python の issue をすべて見る
似ている issue
-
[Bug]: Responses API 500s on assistant message with empty content list対応中かも @shadowmodder が今日担当しました。 オープンbug
難易度 2/5 1〜3時間 初心者へのやさしさ 66/100
vllm-project/vllm#64940 ·
メンテナーはふだん 1 日以内に返信
-
limits
難易度 2/5 1〜3時間 初心者へのやさしさ 82/100
メンテナーはふだん 1 日以内に返信
-
Status: Triage Type: Quest
難易度 2/5 1〜3時間 初心者へのやさしさ 66/100
GTNewHorizons/GT-New-Horizons-Modpack#27490 ·
メンテナーはふだん 1 日以内に返信
-
LLM.save() discards the model's active prompt style対応中かも @remote-controlled-man が今日担当しました。 オープン
難易度 2/5 1〜3時間 初心者へのやさしさ 78/100
Lightning-AI/litgpt#2378 ·
メンテナーはふだん 23 日以内に返信
-
難易度 2/5 1〜3時間 初心者へのやさしさ 62/100
メンテナーはふだん 1 日以内に返信