Default collectors inline with the API again.

Frontend+backend start is enough; external worker remains an optional production split.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
oliver 2026-08-02 16:55:46 +08:00
parent 136f40cdae
commit b03c92bdef
8 changed files with 14 additions and 13 deletions

View file

@ -54,8 +54,8 @@ NETX_UME_NOTIFICATION_TOPIC=ALARM
# NETX_ALEMBIC_UPGRADE_ON_START=false # NETX_ALEMBIC_UPGRADE_ON_START=false
# NETX_SKIP_LEGACY_STARTUP_DDL=false # NETX_SKIP_LEGACY_STARTUP_DDL=false
# NETX_SQL_READONLY_DATABASE_URL=postgresql+psycopg://netx_ro:xxx@127.0.0.1:5432/netx # NETX_SQL_READONLY_DATABASE_URL=postgresql+psycopg://netx_ro:xxx@127.0.0.1:5432/netx
# Device collectors: default off in API — run `python -m netx_api.worker`. # Device collectors run inline with the API by default (frontend+backend start is enough).
# Lab single-process: NETX_RUN_INLINE_SCHEDULERS=true # Production split only: NETX_RUN_INLINE_SCHEDULERS=false and run `python -m netx_api.worker`
# NETX_RUN_INLINE_SCHEDULERS=true # NETX_RUN_INLINE_SCHEDULERS=false
# NETX_AUDIT_ASYNC=true # NETX_AUDIT_ASYNC=true
# NETX_AUDIT_SAMPLE_N=1 # NETX_AUDIT_SAMPLE_N=1

View file

@ -12,7 +12,7 @@
## Runtime ## Runtime
- Ensure PostgreSQL backup policy exists (daily logical backup + retention). - Ensure PostgreSQL backup policy exists (daily logical backup + retention).
- Schema: API auto-runs `alembic upgrade head` on start (see [docs/ALEMBIC.md](docs/ALEMBIC.md)). No manual migrate flag required for normal deploys. - Schema: API auto-runs `alembic upgrade head` on start (see [docs/ALEMBIC.md](docs/ALEMBIC.md)). No manual migrate flag required for normal deploys.
- Collectors: default is external worker (`python -m netx_api.worker`). Only set `NETX_RUN_INLINE_SCHEDULERS=true` for single-process lab. Check `/health/ready` → `schedulers.mode`. - Collectors: default inline with the API (normal frontend+backend start is enough). Optional split: `NETX_RUN_INLINE_SCHEDULERS=false` + `python -m netx_api.worker`. Check `/health/ready` → `schedulers.mode`.
- Run `oclaw` and `netx` under process managers (systemd/Windows service/pm2 equivalent). - Run `oclaw` and `netx` under process managers (systemd/Windows service/pm2 equivalent).
- Enable auto-restart and startup-at-boot for both services. - Enable auto-restart and startup-at-boot for both services.

View file

@ -130,7 +130,7 @@ def run_api_startup() -> None:
finally: finally:
db.close() db.close()
if bool(getattr(settings, "run_inline_schedulers", False)): if bool(getattr(settings, "run_inline_schedulers", True)):
try: try:
start_device_schedulers() start_device_schedulers()
except Exception: except Exception:

View file

@ -140,9 +140,9 @@ class Settings(BaseSettings):
alembic_upgrade_on_start: bool = True alembic_upgrade_on_start: bool = True
# Optional dedicated SQLAlchemy URL for /v1/sql/* (read-only DB role recommended). # Optional dedicated SQLAlchemy URL for /v1/sql/* (read-only DB role recommended).
sql_readonly_database_url: str = "" sql_readonly_database_url: str = ""
# When false (default), API skips config_sync / lldp / port_traffic schedulers — # When true (default), API also runs config_sync / lldp / port_traffic schedulers.
# run `python -m netx_api.worker` alongside the API. Set true only for single-process lab. # Production split: set false and run `python -m netx_api.worker` beside the API.
run_inline_schedulers: bool = False run_inline_schedulers: bool = True
settings = Settings() settings = Settings()

View file

@ -36,7 +36,7 @@ def health_ready(db: Session = Depends(get_db)) -> dict[str, Any]:
"db": "down", "db": "down",
"error": str(exc)[:240], "error": str(exc)[:240],
} }
inline = bool(getattr(settings, "run_inline_schedulers", False)) inline = bool(getattr(settings, "run_inline_schedulers", True))
out["schedulers"] = { out["schedulers"] = {
"inline": inline, "inline": inline,
"mode": "inline" if inline else "external_worker", "mode": "inline" if inline else "external_worker",

View file

@ -1,7 +1,8 @@
"""UME / long-task runtime helpers shared by API and optional worker process. """UME / long-task runtime helpers shared by API and optional worker process.
Device collectors (config_sync / LLDP / port_traffic) run via ``start_device_schedulers`` Device collectors (config_sync / LLDP / port_traffic) run via ``start_device_schedulers``
(API inline when ``NETX_RUN_INLINE_SCHEDULERS=true``, otherwise ``python -m netx_api.worker``). (API inline by default; set ``NETX_RUN_INLINE_SCHEDULERS=false`` and run
``python -m netx_api.worker`` for a split process).
API process also owns UME keepalive, alarm WSS, current-alarm/inventory sync loops, API process also owns UME keepalive, alarm WSS, current-alarm/inventory sync loops,
and oclaw forwarder via ``start_api_sideband_threads``. and oclaw forwarder via ``start_api_sideband_threads``.
""" """

View file

@ -1,12 +1,12 @@
"""Background worker process for long-running schedulers. """Background worker process for long-running schedulers.
Default deployment: API has ``NETX_RUN_INLINE_SCHEDULERS=false``; run this Optional when ``NETX_RUN_INLINE_SCHEDULERS=false`` (API does not start collectors):
alongside the API:
python -m netx_api.worker python -m netx_api.worker
Starts: config_sync, lldp_collect, port_traffic tick loops. Starts: config_sync, lldp_collect, port_traffic tick loops.
UME WS / keepalive remain in the API process (token + alarm coordination). UME WS / keepalive remain in the API process (token + alarm coordination).
By default the API runs collectors inline — no separate worker needed.
""" """
from __future__ import annotations from __future__ import annotations

View file

@ -42,7 +42,7 @@ class SchemaPatchesTests(unittest.TestCase):
from netx_api.config import Settings from netx_api.config import Settings
s = Settings(_env_file=None) s = Settings(_env_file=None)
self.assertFalse(s.run_inline_schedulers) self.assertTrue(s.run_inline_schedulers)
self.assertTrue(s.alembic_upgrade_on_start) self.assertTrue(s.alembic_upgrade_on_start)
versions = Path(__file__).resolve().parents[1] / "alembic" / "versions" versions = Path(__file__).resolve().parents[1] / "alembic" / "versions"