mirror of
https://github.com/zvx-echo6/navi.git
synced 2026-08-26 17:31:37 +00:00
perf(offroute): EXISTS guard replaces COUNT(*) on entry_points hot path
The three wilderness-scenario guards used table_exists() OR get_entry_point_count()==0 to check the index is non-empty — a full SELECT COUNT(*) that scanned the entire 2.94M-row table (~9-73s depending on load/cache). Add EntryPointIndex.has_entry_points() using SELECT EXISTS (SELECT 1 ... LIMIT 1), which short-circuits at the first row, and swap it into _route_A/_route_B/_route_C. get_entry_point_count() kept for admin-info/tests. 3 new tests: table missing -> False, empty -> False, rows -> True. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
24d69716d8
commit
f28ababbbb
2 changed files with 61 additions and 3 deletions
|
|
@ -197,6 +197,17 @@ class EntryPointIndex:
|
||||||
cur.execute("SELECT COUNT(*) FROM entry_points")
|
cur.execute("SELECT COUNT(*) FROM entry_points")
|
||||||
return cur.fetchone()[0]
|
return cur.fetchone()[0]
|
||||||
|
|
||||||
|
def has_entry_points(self) -> bool:
|
||||||
|
"""Fast non-emptiness check. SELECT EXISTS short-circuits at the first row,
|
||||||
|
unlike SELECT COUNT(*) which scans the entire table (~73s on 2.94M rows).
|
||||||
|
Returns False if the table is absent."""
|
||||||
|
if not self.table_exists():
|
||||||
|
return False
|
||||||
|
conn = self._get_conn()
|
||||||
|
with conn.cursor() as cur:
|
||||||
|
cur.execute("SELECT EXISTS (SELECT 1 FROM entry_points LIMIT 1)")
|
||||||
|
return cur.fetchone()[0]
|
||||||
|
|
||||||
def query_bbox(
|
def query_bbox(
|
||||||
self,
|
self,
|
||||||
south: float,
|
south: float,
|
||||||
|
|
@ -905,7 +916,7 @@ class OffrouteRouter:
|
||||||
t0 = time.time()
|
t0 = time.time()
|
||||||
|
|
||||||
# Ensure entry point index exists
|
# Ensure entry point index exists
|
||||||
if not self.entry_index.table_exists() or self.entry_index.get_entry_point_count() == 0:
|
if not self.entry_index.has_entry_points():
|
||||||
return {
|
return {
|
||||||
"status": "error",
|
"status": "error",
|
||||||
"message": "Trail entry point index not built. Run build_entry_index() first."
|
"message": "Trail entry point index not built. Run build_entry_index() first."
|
||||||
|
|
@ -986,7 +997,7 @@ class OffrouteRouter:
|
||||||
"""
|
"""
|
||||||
t0 = time.time()
|
t0 = time.time()
|
||||||
|
|
||||||
if not self.entry_index.table_exists() or self.entry_index.get_entry_point_count() == 0:
|
if not self.entry_index.has_entry_points():
|
||||||
return {
|
return {
|
||||||
"status": "error",
|
"status": "error",
|
||||||
"message": "Trail entry point index not built. Run build_entry_index() first."
|
"message": "Trail entry point index not built. Run build_entry_index() first."
|
||||||
|
|
@ -1066,7 +1077,7 @@ class OffrouteRouter:
|
||||||
"""
|
"""
|
||||||
t0 = time.time()
|
t0 = time.time()
|
||||||
|
|
||||||
if not self.entry_index.table_exists() or self.entry_index.get_entry_point_count() == 0:
|
if not self.entry_index.has_entry_points():
|
||||||
return {
|
return {
|
||||||
"status": "error",
|
"status": "error",
|
||||||
"message": "Trail entry point index not built. Run build_entry_index() first."
|
"message": "Trail entry point index not built. Run build_entry_index() first."
|
||||||
|
|
|
||||||
|
|
@ -465,3 +465,50 @@ def test_spatial_no_class_no_use_picks_foot(monkeypatch):
|
||||||
r = object.__new__(OffrouteRouter)
|
r = object.__new__(OffrouteRouter)
|
||||||
modes = r._spatial_eligible_modes(43.6, -116.2, {})
|
modes = r._spatial_eligible_modes(43.6, -116.2, {})
|
||||||
assert modes == frozenset({"foot"})
|
assert modes == frozenset({"foot"})
|
||||||
|
|
||||||
|
|
||||||
|
# ── EntryPointIndex.has_entry_points — EXISTS guard (replaces COUNT(*)) ────
|
||||||
|
# Bare index (no __init__/DB); table_exists + _get_conn monkeypatched.
|
||||||
|
|
||||||
|
from services.navi_offroute.router import EntryPointIndex
|
||||||
|
|
||||||
|
|
||||||
|
class _FakeCur:
|
||||||
|
def __init__(self, row):
|
||||||
|
self._row = row
|
||||||
|
def __enter__(self):
|
||||||
|
return self
|
||||||
|
def __exit__(self, *a):
|
||||||
|
return False
|
||||||
|
def execute(self, q, *a):
|
||||||
|
self.q = q
|
||||||
|
def fetchone(self):
|
||||||
|
return self._row
|
||||||
|
|
||||||
|
|
||||||
|
class _FakeConn:
|
||||||
|
def __init__(self, row):
|
||||||
|
self._row = row
|
||||||
|
def cursor(self):
|
||||||
|
return _FakeCur(self._row)
|
||||||
|
|
||||||
|
|
||||||
|
def _bare_index(monkeypatch, table_exists, row=None):
|
||||||
|
monkeypatch.setattr(EntryPointIndex, "table_exists", lambda self: table_exists)
|
||||||
|
monkeypatch.setattr(EntryPointIndex, "_get_conn", lambda self: _FakeConn(row))
|
||||||
|
return object.__new__(EntryPointIndex)
|
||||||
|
|
||||||
|
|
||||||
|
def test_has_entry_points_table_missing(monkeypatch):
|
||||||
|
idx = _bare_index(monkeypatch, table_exists=False)
|
||||||
|
assert idx.has_entry_points() is False
|
||||||
|
|
||||||
|
|
||||||
|
def test_has_entry_points_empty(monkeypatch):
|
||||||
|
idx = _bare_index(monkeypatch, table_exists=True, row=(False,))
|
||||||
|
assert idx.has_entry_points() is False
|
||||||
|
|
||||||
|
|
||||||
|
def test_has_entry_points_rows(monkeypatch):
|
||||||
|
idx = _bare_index(monkeypatch, table_exists=True, row=(True,))
|
||||||
|
assert idx.has_entry_points() is True
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue