Skip to content

fix: add retry and neighbor fallback for PLATEAU CityGML catalog API#208

Open
Soynyuu wants to merge 1 commit intomainfrom
fix/207-plateau-citygml-retry-fallback
Open

fix: add retry and neighbor fallback for PLATEAU CityGML catalog API#208
Soynyuu wants to merge 1 commit intomainfrom
fix/207-plateau-citygml-retry-fallback

Conversation

@Soynyuu
Copy link
Owner

@Soynyuu Soynyuu commented Mar 6, 2026

Summary

  • PLATEAU CityGML カタログ API の断続的 404 エラーに対するリトライロジックと近隣メッシュフォールバックを追加
  • 検索成功後のインポート/展開が同じメッシュコードで 404 になる問題を解消

背景

search-by-address で建物検索が成功した直後に unfold-textured-by-id-and-mesh を呼ぶと、同じメッシュコード (53394611) で PLATEAU API が 404 を返す現象が発生。外部 API の断続的な不安定性が原因。

変更内容

リトライロジック (_query_plateau_catalog_api)

  • 新規ヘルパー関数: エクスポネンシャルバックオフ付きリトライ(最大2回、0.5s/1.0s 間隔)
  • 検索パス (fetch_citygml_from_plateau) と展開パス (_fetch_citygml_by_mesh_code_with_sources) の両方で使用(DRY)

近隣メッシュフォールバック (Step 3)

  • search_building_by_id_and_mesh で初回メッシュフェッチが全リトライ後も失敗した場合、8つの隣接メッシュを試行
  • 既存の Step 5 フォールバック(ビルIDが見つからない場合)とは別の、フェッチ自体の失敗を補完するレイヤー

テスト

  • 4 件の新規テストを追加(合計 68 件全パス)
    • test_fetch_failure_triggers_neighbor_fallback: Step 3 フォールバックの回帰テスト
    • test_all_fetches_fail_returns_error_with_neighbor_details: 全フェッチ失敗時のエラーメッセージ
    • test_query_plateau_catalog_api_retries_on_failure: リトライ成功のテスト
    • test_query_plateau_catalog_api_returns_none_after_exhausting_retries: リトライ枯渇テスト

テスト結果

68 passed in 0.90s

Closes #207

The PLATEAU Data Catalog API intermittently returns 404 for valid mesh
codes, causing unfold-textured-by-id-and-mesh to fail even though
search-by-address succeeded seconds earlier for the same mesh.

Changes:
- Add _query_plateau_catalog_api() helper with exponential backoff retry
  (max 2 retries, 0.5s/1.0s delays) for the catalog API call
- Apply retry to both search and unfold code paths (DRY)
- Add neighbor mesh fallback in search_building_by_id_and_mesh Step 3:
  when the primary mesh fetch fails after retries, try 8 neighboring
  meshes before returning error
- Add 4 new regression tests covering retry logic and fetch-failure
  fallback (68 total tests pass)

Closes #207
Copilot AI review requested due to automatic review settings March 6, 2026 06:43
@cloudflare-workers-and-pages
Copy link

Deploying newlppaper-cad with  Cloudflare Pages  Cloudflare Pages

Latest commit: 4717899
Status: ✅  Deploy successful!
Preview URL: https://f76e7b29.newlppaper-cad.pages.dev
Branch Preview URL: https://fix-207-plateau-citygml-retr.newlppaper-cad.pages.dev

View logs

@cloudflare-workers-and-pages
Copy link

Deploying paper-cad with  Cloudflare Pages  Cloudflare Pages

Latest commit: 4717899
Status: ✅  Deploy successful!
Preview URL: https://60789c52.paper-cad.pages.dev
Branch Preview URL: https://fix-207-plateau-citygml-retr.paper-cad.pages.dev

View logs

Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Adds resiliency to PLATEAU CityGML catalog lookups to mitigate intermittent 404s that can break the “search → import/unfold” flow.

Changes:

  • Introduces _query_plateau_catalog_api() with exponential-backoff retries and reuses it from both search and fetch paths.
  • Adds Step 3 “neighbor mesh” fallback when the fetch itself fails for the primary mesh.
  • Adds regression tests covering neighbor fallback behavior and retry exhaustion/success cases.

Reviewed changes

Copilot reviewed 2 out of 2 changed files in this pull request and generated 3 comments.

File Description
backend/services/plateau_fetcher.py Adds retry helper, wires it into catalog calls, and adds neighbor fallback when primary mesh fetch fails.
backend/tests/test_plateau_mesh_neighbor_fallback.py Adds tests for Step 3 neighbor fallback and _query_plateau_catalog_api retry behavior.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.


# The primary mesh should have been tried first
called_meshes = [call.args[0] for call in mock_fetch.call_args_list]
assert called_meshes[0] == primary_mesh
Copy link

Copilot AI Mar 6, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This new regression test verifies Step 3 neighbor fallback success, but it doesn’t assert that the returned result["mesh_code"] reflects the neighbor mesh that actually supplied the CityGML (vs. the originally requested mesh). Adding that assertion would catch cases where the implementation fetches from a neighbor but still reports the primary mesh.

Suggested change
assert called_meshes[0] == primary_mesh
assert called_meshes[0] == primary_mesh
# The mesh_code in the result should correspond to a neighbor that supplied CityGML
neighbor_meshes = [m for m in called_meshes if m != primary_mesh]
assert result["mesh_code"] in neighbor_meshes

Copilot uses AI. Check for mistakes.
Comment on lines +2427 to +2434
for neighbor_mesh in neighboring_meshes:
fetched = _fetch_citygml_by_mesh_code_with_sources(neighbor_mesh)
if fetched:
logger.info(
f"[BUILDING SEARCH] ✓ CityGML found via neighboring mesh "
f"{neighbor_mesh} (primary {mesh_code} had no data)"
)
break
Copy link

Copilot AI Mar 6, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Step 3 neighbor fallback can fetch CityGML from a neighboring mesh, but the function never records which mesh actually produced fetched. As a result, later resolved_mesh_code is initialized to the originally requested mesh_code, and the success response can report the wrong mesh_code even though the XML came from a neighbor. Track the mesh code used for the successful fetch (e.g., fetched_mesh_code) and use it to initialize resolved_mesh_code / searched mesh list.

Copilot uses AI. Check for mistakes.
Comment on lines +2447 to +2450
"error_details": (
f"No CityGML data found for mesh code: {mesh_code} "
f"(also tried neighboring meshes)"
),
Copy link

Copilot AI Mar 6, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

error_details always says “(also tried neighboring meshes)”, but if get_neighboring_meshes_3rd() throws (or returns an empty list) no neighbors were actually tried. Consider building this message conditionally (or include how many/which meshes were attempted) so the error is accurate and more actionable.

Copilot uses AI. Check for mistakes.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

fix: PLATEAU CityGML catalog API の断続的 404 により unfold-textured-by-id-and-mesh が失敗する

2 participants