Co-Study4Grid / docs /performance /history /shared-network.md
github-actions[bot]
Deploy f7b8bf6
1c730d1
|
Raw
History Blame Contribute Delete
4.8 kB

Mutualisation du pypowsybl Network entre network_service et recommender_service

Observation (v8 trace)

Le NAD prefetch introduit en v8 (docs/performance/history/nad-prefetch.md) a délivré /api/network-diagram en 516 ms au lieu de 6 385 ms, mais /api/config est passé de 14 790 ms à 19 171 ms (+4.4 s). Le gain net a été de seulement ~1.5 s sur la critical path.

En auditant le code, on s'aperçoit qu'on charge le même .xiidm jusqu'à trois fois pendant /api/config :

  1. network_service.load_network()pn.load(file_path), ~3-5 s
  2. recommender_service._get_base_network()pp.network.load(str(network_file)), ~3-5 s doublon complet
  3. setup_environment_configs_pypowsybl() dans grid2op — backend pypowsybl charge encore une fois, ~3-5 s

Les appels #1 et #2 chargent exactement le même fichier et produisent la même Network pypowsybl. La pré-chauffe _base_network que fait prefetch_base_nad_async() en main thread est responsable d'une grosse partie du +4.4 s sur /api/config.

Change

_get_base_network() préfère maintenant réutiliser la Network déjà chargée par network_service plutôt que de la re-parser :

def _get_base_network(self):
    if self._base_network is not None:
        return self._base_network

    # Préfère l'instance déjà chargée par network_service.
    from expert_backend.services.network_service import network_service
    if network_service.network is not None:
        n = network_service.network
        n.get_line_ids = lambda: n.get_lines().index.tolist()
        self._base_network = n
        self._capture_initial_pst_taps(n)
        return self._base_network

    # Fallback : pp.network.load(config.ENV_PATH) pour les tests /
    # appels qui bypass /api/config.

Sûreté de partage

Les deux services tiennent une référence vers le même objet pypowsybl Network. Les accès restent sûrs :

  • network_service ne fait que des lectures (get_lines, get_voltage_levels, …), jamais de variant switch.
  • recommender_service switche les variants dans _get_n_variant / _get_n1_variant mais restaure toujours le variant d'origine dans un try/finally, donc toute lecture depuis network_service voit un état cohérent.
  • Le monkey-patch get_line_ids est idempotent et purement additif.
  • _capture_initial_pst_taps est spécifique au recommender (state isolé) et doit toujours s'exécuter pour capturer les tap positions N-state.

Invariants (testés)

test_recommender_service.py::TestGetBaseNetworkMutualisation :

  • test_reuses_network_service_network_when_available — quand network_service.network est peuplé, _get_base_network() ne déclenche AUCUN nouveau pp.network.load() et retourne exactement la même instance.
  • test_falls_back_to_standalone_load_when_network_service_empty — quand network_service.network is None (tests, appelants qui bypass /api/config), on retombe sur le chargement standalone.

Le test existant test_direct_file_loading.py::test_get_base_network_with_file_path est mis à jour pour forcer explicitement le chemin fallback en mettant network_service.network = None.

Impact attendu

Baseline v8 (avec NAD prefetch seul) :

Segment v8 mesuré
/api/config 19 171 ms
/api/network-diagram 516 ms
Total critical path ~20.5 s

Attendu v9 (avec mutualisation) :

Segment v9 attendu
/api/config ~14-16 s (−3-5 s : plus de pp.network.load doublon)
/api/network-diagram ~500 ms (inchangé, cache toujours chaud)
Total critical path ~15 s (−5 s vs v8, −6 s vs v7)

On récupère ainsi une grosse partie du gain théorique du prefetch NAD qui avait été absorbé par le chargement dupliqué.

Ce qui n'est PAS changé

  • Le chargement pypowsybl par grid2op (#3 ci-dessus) reste en place — il est enfoui dans expert_op4grid_recommender et ne peut pas être court-circuité sans modifier la lib externe.
  • Le comportement des endpoints est identique : seul le plomberie interne change.
  • Les appelants qui construisent RecommenderService directement (tests, scripts d'intégration) continuent de fonctionner grâce au fallback.

Fichiers modifiés

Fichier Changement
expert_backend/services/recommender_service.py _get_base_network préfère network_service.network quand disponible ; fallback pp.network.load préservé.
expert_backend/tests/test_recommender_service.py Nouvelle classe TestGetBaseNetworkMutualisation (2 tests).
expert_backend/tests/test_direct_file_loading.py test_get_base_network_with_file_path force explicitement le chemin fallback.