firebase / firebase/firebase-functions-python
`firestore_fn` builds a new `firestore_v1.Client` (and calls `google.auth.default()`) on every event delivery
- 主要語言
- Python
- 星號
- 167
- 分支
- 34
- PR 合併指標
- 30 天內沒有已合併 PR
描述
**Version:** firebase-functions 0.5.0 (same code on `main` / 0.6.0), Python 3.14, Cloud Run functions gen2, google-auth 2.53.0
**What happens**
`firebase_functions/firestore_fn.py::_firestore_endpoint_handler` does, per delivery:
```python
if _DEFAULT_APP_NAME not in _apps:
initialize_app()
app = get_app()
firestore_client = _firestore_v1.Client(project=app.project_id, database=event_database)
```
`google.cloud.firestore_v1.Client(...)` with no `credentials=` calls `google.auth.default()`,
which on Cloud Run pings the metadata server (`google.auth.compute_engine._metadata.ping`).
That ping only retries on `TransportError`; any non-`200 + Metadata-Flavor: Google` answer
returns `False` immediately, and gen2's sandbox has no DMI fallback, so the call raises
`DefaultCredentialsError` — before the user's handler runs and before any user-level error
handling can see it.
We observed this on a **warm** instance that had been serving for three hours and had served
nine events in the previous thirty seconds: four concurrent deliveries at
`2026-08-28T21:40:13Z` all died in the framework with
```
File "firebase_functions/firestore_fn.py", line 145, in _firestore_endpoint_handler
firestore_client = _firestore_v1.Client(project=app.project_id, database=event_database)
...
File "google/auth/_default.py", line 748, in default
raise exceptions.DefaultCredentialsError(_CLOUD_SDK_MISSING_CREDENTIALS)
```
while `firebase_admin` in the same process already held perfectly good cached credentials.
Thirty seconds later the same instance logged a TLS EOF talking to an unrelated host, so the
trigger was a brief network hiccup on that instance — but the per-event credential lookup is
what turned it into four dropped events (the trigger had no retry policy).
**Why it matters**
- The client is only used to decode the snapshot (`_firestore_helpers.decode_dict(...,
firestore_client)` and `DocumentReference`); it never talks to Firestore for that. A
credential lookup per event is pure overhead and a per-event failure point.
- The failure happens before user code, so `retry` on the trigger is the only mitigation
available to users.
**Suggested fix**
Reuse the admin app's client (`firebase_admin.firestore.client(app, database_id=...)`) or
cache one `firestore_v1.Client` per `(project, database)` at module level, so
`google.auth.default()` runs once per process instead of once per event. Passing
`credentials=app.credential.get_credential()` into the `Client` constructor would also avoid
the per-event lookup.
**Related gap (worth a second issue or the same one)**
`FirestoreOptions._endpoint` hardcodes `retry=False` and `FirestoreOptions` does not accept a
`retry` keyword (`TypeError: FirestoreOptions.__init__() got an unexpected keyword argument
'retry'`), even though `EventHandlerOptions.retry` exists and the Node SDK exposes `retry` on
Firestore triggers. The only way to get an Eventarc retry policy from Python today is to
mutate `fn.__firebase_endpoint__.eventTrigger["retry"]` after decoration.
**Repro**
Any Firestore trigger; make the metadata server return a non-200 for one request (or block
`169.254.169.254` briefly) on a warm instance and deliver an event. The handler never runs.
貢獻指南
研究方向
從 firebase_functions/firestore_fn.py::_firestore_endpoint_handler 開始,追蹤 firestore_client 如何傳遞給 firestore_helpers.decode_dict。比較建議的 admin-app 用戶端、按資料庫劃分的快取和明確憑證方案,然後確認快照解碼在每次傳遞時不必查找憑證仍能正常運作。如果包含重試策略缺口,請另外檢查 FirestoreOptions._endpoint。
由索引模型根據 Issue 內容生成。
評估
- 技術堆疊
- google-cloud, python
- 領域
- backend, databases
- Issue 類型
- 缺陷
- 難度
- 4/5
- 預估耗時
- 3-5 天
- 活躍度
- 活躍
- 描述清晰度
- 基本清楚
- 新手友好度
- 48/100