- Accueil
- Documentation
- Configuration
- Contrat d'architecture du cache de scan du système de fichiers
Contrat d'architecture du cache de scan du système de fichiers
Ce document définit le contrat actuel du cache partagé de scan du système de fichiers implémenté en Rust (crates/pi-natives/src/fs_cache.rs) et consommé par les API natives de découverte/recherche exposées à packages/coding-agent.
Ce qu’est ce cache
Section intitulée « Ce qu’est ce cache »Le cache stocke des listes complètes d’entrées de scan de répertoires (GlobMatch[]) indexées par portée de scan et politique de parcours, puis permet aux opérations de niveau supérieur (filtrage glob, scoring flou, sélection de fichiers grep) de s’exécuter sur ces entrées mises en cache.
Objectifs principaux :
- éviter les parcours répétés du système de fichiers pour des appels répétés de découverte/recherche
- maintenir la cohérence entre
glob,fuzzyFindetgreplorsqu’ils partagent la même politique de scan - permettre la récupération explicite en cas d’obsolescence pour les résultats vides et l’invalidation explicite après des mutations de fichiers
Propriété et surface publique
Section intitulée « Propriété et surface publique »- Implémentation du cache et politique :
crates/pi-natives/src/fs_cache.rs - Consommateurs natifs :
crates/pi-natives/src/glob.rscrates/pi-natives/src/fd.rs(fuzzyFind)crates/pi-natives/src/grep.rs
- Binding/export JS :
packages/natives/src/glob/index.ts(invalidateFsScanCache)packages/natives/src/glob/types.tspackages/natives/src/grep/types.ts
- Helpers d’invalidation de mutation du coding-agent :
packages/coding-agent/src/tools/fs-cache-invalidation.ts
Partitionnement de la clé de cache (contrat strict)
Section intitulée « Partitionnement de la clé de cache (contrat strict) »Chaque entrée est indexée par :
- chemin canonicalisé du répertoire
root - booléen
include_hidden - booléen
use_gitignore
Implications :
- Les scans avec et sans fichiers cachés ne partagent pas d’entrées.
- Les scans respectant le gitignore et ceux désactivant l’ignorance ne partagent pas d’entrées.
- Les consommateurs doivent passer une sémantique stable pour le comportement hidden/gitignore ; changer l’un ou l’autre flag crée une partition de cache différente.
L’inclusion de node_modules n’est pas dans la clé de cache. Le cache stocke les entrées avec node_modules inclus ; le filtrage par consommateur est appliqué après la récupération.
Comportement de collecte du scan
Section intitulée « Comportement de collecte du scan »Le peuplement du cache utilise un walker déterministe (ignore::WalkBuilder) configuré par include_hidden et use_gitignore :
follow_links(false)- trié par chemin de fichier
.gitest toujours ignorénode_modulesest toujours collecté au moment du scan du cache (et optionnellement filtré ensuite)- le type de fichier d’entrée +
mtimesont capturés viasymlink_metadata
Les racines de recherche sont résolues par resolve_search_path :
- les chemins relatifs sont résolus par rapport au cwd courant
- la cible doit être un répertoire existant
- la racine est canonicalisée quand c’est possible
Politique de fraîcheur et d’éviction
Section intitulée « Politique de fraîcheur et d’éviction »Politique globale (configurable par variable d’environnement) :
FS_SCAN_CACHE_TTL_MS(défaut1000)FS_SCAN_EMPTY_RECHECK_MS(défaut200)FS_SCAN_CACHE_MAX_ENTRIES(défaut16)
Comportement :
get_or_scan(...)- si le TTL est
0: contournement complet du cache, toujours un scan frais (cache_age_ms = 0) - sur un hit de cache dans le TTL : retourne les entrées en cache +
cache_age_msnon nul - sur un hit expiré : éviction de la clé, rescan, stockage d’une entrée fraîche
- si le TTL est
- l’application du nombre maximum d’entrées se fait par éviction du plus ancien selon
created_at
Revérification rapide des résultats vides (distincte des hits normaux)
Section intitulée « Revérification rapide des résultats vides (distincte des hits normaux) »Hit de cache normal :
- un hit de cache dans le TTL retourne les entrées en cache et ne fait rien d’autre.
Revérification rapide des résultats vides :
- c’est une politique côté appelant utilisant
ScanResult.cache_age_ms - si le résultat filtré/requêté est vide et que l’âge du scan en cache est d’au moins
empty_recheck_ms(), l’appelant effectue unforce_rescan(...)et réessaie - destiné à réduire les résultats faux négatifs obsolètes lorsque des fichiers ont été récemment ajoutés mais que le cache est encore dans le TTL
Consommateurs actuels :
glob: revérifie quand les correspondances filtrées sont vides et que l’âge du scan dépasse le seuilfuzzyFind(fd.rs) : revérifie uniquement quand la requête est non vide et que les correspondances scorées sont videsgrep: revérifie quand la liste de fichiers candidats sélectionnés est vide
Valeurs par défaut des consommateurs et utilisation du cache
Section intitulée « Valeurs par défaut des consommateurs et utilisation du cache »Le cache est optionnel sur toutes les API exposées (cache?: boolean, défaut false).
Valeurs par défaut actuelles dans les API natives :
glob:hidden=false,gitignore=true,cache=falsefuzzyFind:hidden=false,gitignore=true,cache=falsegrep:hidden=true,cache=false, et le scan du cache utilise toujoursuse_gitignore=true
Appelants du coding-agent aujourd’hui :
- La découverte de candidats de mention à haut volume active le cache :
packages/coding-agent/src/utils/file-mentions.ts- profil :
hidden=true,gitignore=true,includeNodeModules=true,cache=true
- L’intégration
grepau niveau outil désactive actuellement le cache de scan (cache: false) :packages/coding-agent/src/tools/grep.ts
Contrat d’invalidation
Section intitulée « Contrat d’invalidation »Point d’entrée d’invalidation natif :
invalidateFsScanCache(path?: string)- avec
path: supprime les entrées du cache dont la racine est un préfixe du chemin cible - sans path : efface toutes les entrées du cache de scan
- avec
Détails de gestion des chemins :
- les chemins d’invalidation relatifs sont résolus par rapport au cwd
- l’invalidation tente la canonicalisation
- si la cible n’existe pas (par ex., suppression), le fallback canonicalise le parent et rattache le nom de fichier quand c’est possible
- cela préserve le comportement d’invalidation pour les créations/suppressions/renommages où un côté peut ne pas exister
Responsabilités du flux de mutation du coding-agent
Section intitulée « Responsabilités du flux de mutation du coding-agent »Le code du coding-agent doit invalider après des mutations réussies du système de fichiers.
Helpers centraux :
invalidateFsScanAfterWrite(path)invalidateFsScanAfterDelete(path)invalidateFsScanAfterRename(oldPath, newPath)(invalide les deux côtés quand les chemins diffèrent)
Points d’appel actuels des outils de mutation :
packages/coding-agent/src/tools/write.tspackages/coding-agent/src/patch/index.ts(flux hashline/patch/replace)
Règle : si un flux mute le contenu ou l’emplacement du système de fichiers et contourne ces helpers, des bugs d’obsolescence du cache sont à prévoir.
Ajouter un nouveau consommateur de cache en toute sécurité
Section intitulée « Ajouter un nouveau consommateur de cache en toute sécurité »Lors de l’introduction de l’utilisation du cache dans un nouveau chemin de scanner/recherche :
-
Utilisez des entrées de politique de scan stables
- décidez d’abord la sémantique hidden/gitignore
- passez-les de manière cohérente à
get_or_scan/force_rescanpour que les partitions de cache soient intentionnelles
-
Traitez les données du cache comme pré-filtrées uniquement par la politique de parcours
- appliquez le filtrage spécifique à l’outil (patterns glob, filtres de type, règles node_modules) après la récupération
- ne supposez jamais que les entrées en cache reflètent déjà vos filtres de niveau supérieur
-
Implémentez la revérification rapide des résultats vides uniquement pour le risque de faux négatifs obsolètes
- utilisez
scan.cache_age_ms >= empty_recheck_ms() - réessayez une fois avec
force_rescan(..., store=true, ...) - gardez ce chemin séparé de la logique normale de hit de cache
- utilisez
-
Respectez explicitement le mode sans cache
- quand l’appelant désactive le cache, appelez
force_rescan(..., store=false, ...) - ne peuplez pas le cache partagé dans un chemin de requête sans cache
- quand l’appelant désactive le cache, appelez
-
Connectez l’invalidation de mutation pour tout nouveau chemin d’écriture
- après une écriture/édition/suppression/renommage réussi, appelez le helper d’invalidation du coding-agent
- pour un renommage/déplacement, invalidez à la fois l’ancien et le nouveau chemin
-
N’ajoutez pas de contrôles TTL par appel
- le contrat actuel est une politique globale uniquement (configurée par env), pas de surcharge TTL par requête
Limites connues
Section intitulée « Limites connues »- La portée du cache est en mémoire au niveau du processus (
DashMap), non persistée entre les redémarrages de processus. - Le cache stocke les entrées de scan, pas les résultats finaux des outils.
glob/fuzzyFind/greppartagent les entrées de scan uniquement quand les dimensions clés (root,hidden,gitignore) correspondent..gitest toujours exclu au moment de la collecte du scan, indépendamment des options de l’appelant.