StyloBot Release Series: Trouver et corriger la croissance sans limite dans les services de réseau longs (Français (French))

StyloBot Release Series: Trouver et corriger la croissance sans limite dans les services de réseau longs

Monday, 01 June 2026

//

23 minute read

Long-les services qui fonctionnent ont tendance à se déplacer lentement vers la mémoire non liée .Cette est la façon dont je trouve cette dérive,la manière dont j’envisage de le corriger sans en papierer sur elle–, et un exemple travaillé du StyloBotMSC5, couche de similitude vectorielle qui l’a ramenée de M SK6 GB sur le Large Object Heap à sous 6 MBMST8

StyloBot

StyloBot Release Series

  1. Comportement, Non identité: pourquoi StyloBot modele les clients de façon comportementale
  2. Behaviour-Aware ASP.NET UI: le serveur - remet la surface sur ce résultat de détection
  3. Trouver et rétablir une croissance sans limites dans les services longs-Running .NET: la discipline de fiabilité
  4. Behaviour-Aware TypeScript UI: ExpressM SK1 Fastify, et composants de navigateur
  5. L'architecture de la Sidecar: comment le moteur de détection se connecte aux piles NET non -.
  6. Apprentissage pour devenir plus rapide: le système d'apprentissage adaptatif , quatre- mémoire de niveauM SK3 et cache des verdicts
  7. Étudier ce qui ne va pas se tenir: la discipline de vérification : un fichier BDF conduit à la régressionM SK2 charge, et à l'étalonnage
  8. StyloExtract - un convertisseur HTML d'apprentissage local à Markdown: la couche de marquage HTML→ qui se joigne au détecteur , le bug lucidVIEW du fauteuil roulant qui a capté l’image

L'exemple travaillé est la couche de vector StyloBot, mais la discipline n'est pas't Stylo BotM SK2specificMSC3 Tout service longMST4running MSST5NET qui accumule l'état du trafic augmente finalement une classe d'erreurs que vous pouvezMst6t catch dans les essais ou les courts essais de chargementM st7 Il apparaît seulement après des jours de trafic réelMstr8 Ce post est le guide que j'utilise pour le trouverM str9

Le modèle comportemental se trouve dans Comportement, Non identité; la surface ASP.NET dans Behaviour-Aware ASP.NET UI; source à github.comM SK1scottgal/stylobot.


La classe du problème

Long-services opérationnels s’accumulent. Chaque cache, , chaque magasin d’apprentissage, , chaque M SK4nous, MSC5 nous garderons seulement les dernières demandes de N." le buffer est un petit accumulateur,. chacun semble bien sur son propre compte,. Ensemble,M SK9 dans un processus qui, depuis une semaine, a été mis à jour par ' , ils forment une forme de mémoire qu’aucun essai ne reproduit jamais

  • fonctionne bien dans dev, CI, et le premier jour de production
  • se déplace graduellement vers OOM, puis soit s'écrase, soit commence à naviguer

Vous l'apercevez en cherchant délibérément, sur un processus qui ' a duré assez longtemps pour que la forme apparaisse

Étape 1: Examens périodiques de la fiabilité (l'acte d'examiner les données

Le premier élément de la discipline est :

Chaque quelques sorties, Je cesse d’ajouter des fonctionnalités et je regarde simplement le système en cours sous un trafic réaliste, demandant Tout cela semble-t-il mauvais? Aucune erreur spécifique, aucun objectif . Je regarde simplement. Les systèmes qui fonctionnent se déplacent silencieusementM SK3 ils ne faillent pasMSC4ils n’échouent pas fortement jusqu’à ce qu’ils échouent de façon catastrophiqueMST5 Si vous regardez seulement lorsque quelque chose se romptMst6 vousMSt7vous avez déjà perduM st8

La révision la plus récente a capté le bug ce post est sur: Mostlylucid.BotDetection.Demo Processus de sitting at a 20 GB resident set under synthetic test traffic . That' is the kind of number that should not survive thirty seconds of attention

Règle: Mettez un espace récurrent sur le calendrier pour lire vos propres mesures.

Étape 2: Laissez les compteurs vous surprendre

L’outil approprié pour " est quelque chose de mal ?" sur un processus .NET est dotnet-counters. Celle-ci, ', est gratuite, c'est déjà installée ' ce qui vous dit la vérité sur ce que votre processus fait actuellement

Quelques commandes plus tard sur le StyloBot demo:

dotnet.gc.last_collection.heap.size[loh]   13,393,217,096 bytes (13.4 GB)
dotnet.gc.heap.total_allocated              97 MB/sec
dotnet.exceptions[SqliteException]          4/sec

Que' est suffisant pour savoir ce qui est errant, sans lire de code encore. Grand trou d'objets a été de 13.4 GB et s’est accrue à près d’M SK1 MB / sec. Et quelque chose affectait des objets énormes continuellementMSC4 et le GenMSSK5 GC ne pouvait pas se tenir avec euxMST6

Un rafraîchissement rapide sur le LOH ( car chaque .NET dev touche ce dernier en bout de ligne

Le Large Object Heap est l’une des surprises les plus courantes lorsque vous présentez pour la première fois un long service -running .NET. Il vaut la peine de le redéfinir parce que presque tout le monde l’obtient de la manière difficile.

  • Le GC est composé de trois générations :
  • Tout ce qui est plus grand que 85 KB ne va pas dans ces générations. Il va directement sur le Grand trou d'objets.
  • Le LOH n'est recueilli qu'au cours d'un Gen2 GC, qui est la collecte la plus coûteuse .NET fait. GenM SK3 se produit fréquemmentMSC4 et le temps de démarrage essaie dur d'y éviter
  • pire, par défaut le LOH est non compacte quand il est recueilli; il libère simplement les trous. Donc même après une génétique 2, le LOH se fragmenteM SK3 vous avez de l’espace libreMSC4 mais c’est ce qui est incorrectMST5 des trous de taille appropriée pour la prochaine allocationMSP7 De nouveaux grands objets élargissent le chevauchement plutôt que de le réutiliserMSV8
  • Efficacité nette: continuer à attribuer de grands objets à un rythme constantM SK1 et votre processus ' sa empreinte de mémoire marche vers le haut, que les objets soient ou non encore renvoyés. Il semble comme une fuite même s’il n’est pas une

Les sources les plus importantes de croissance accidentelle des LOH dans les services réels .NETM SK1 selon ma expérience:

Source Pourquoi finit-il sur le LOH ?
JsonSerializer.Serialize(obj) Retour d'un string Un objet de 100 MB devient une chaîne UTF de \200 MB de -16 d'une seule allocation.
MemoryStream vous laissez augmenter sans limite Le tampon interne double au-delà de 85 KB et demeure sur le LOH
byte[] buf = new byte[n] pour le grand n Répartition directe du LOH ; très courante dans le fichier
List<T> qui augmente depuis ~10 K référence-articles typés croisement de l’arrée d’appui 85 KB et débarque sur le LOH S
string.Concat / StringBuilder sur un grand texte Final ToString() est une seule allocation énorme
XmlSerializer / DataContractSerializer de grands graphiques La même forme que le cas JSON

Si vous ne vous souvenez qu’une règle de la colonne d’oeil: être soupçonné de tout chemin de code qui produit un grand objet contigu unique sur le temps-temps ou par demande. Il s’agit de la forme qui écrase les longs services d’exploitation, - et ,; elle est exactement celle que nous allons trouver dans StyloBot.

flowchart LR
    classDef input fill:none,stroke:#3b82f6,stroke-width:2px
    classDef store fill:none,stroke:#f59e0b,stroke-width:2px
    classDef async fill:none,stroke:#a855f7,stroke-width:2px
    classDef problem fill:none,stroke:#ef4444,stroke-width:2px

    A["Per-request handler<br/>Add to collection"]:::input --> B["Long-lived List / Dict / Buffer"]:::store
    C["Timer / autosave<br/>every N min"]:::async --> D["JsonSerializer / MemoryStream<br/>single &gt;85 KB allocation"]:::problem
    B --> D
    D --> E["Large Object Heap"]:::problem
    E --> F["Gen2 GC<br/>infrequent, expensive"]:::async
    F --> G["LOH not compacted by default<br/>fragments build up"]:::problem
    G --> H["Process RSS marches upward"]:::problem

Outils qui aident à le diagnostiquer:

  • dotnet-counters pour les numéros vivants (la jauge LOH au-dessus ).

  • dotnet-gcdump et dotnet-dump pour des snapshots que vous pouvez ouvrir dans Visual Studio ou PerfView.

  • PerfView elle-même pour tracer les allocations jusqu’à un stack.

  • Profileurs de JetBrains CLI ( ce que j’ai réellement utilisé pour cette révision de travail). Maintenant emballé comme appropriée . Outils mondiaux du NETM SK3 si silencieux

    dotnet tool install -g JetBrains.dotMemory.GlobalTools
    dotnet tool install -g JetBrains.dotTrace.GlobalTools
    
    dotMemory get-snapshot <pid> --save-to-dir=./snapshots
    dotTrace attach <pid> --profiling-type=Sampling --timeout=60s --save-to=./trace.dtp
    

    Division de la main-d’œuvre pour ce réaménagement: dotnet-counters je lui ai dit que le LOH était le problème; la photo de l’image de souvenir me a dit que la cause était le timer d’auto-conservation.

Si vous en avez vraiment besoin, il y a des cages d’évacuation. GCSettings.LargeObjectHeapCompactionMode = CompactOnce force une compaction LOH de 1 - off . RecyclableMemoryStream des tampons de base Microsoft pour éviter la répartition en premier lieu. Les deux sont des plasters. La bonne solution est presque toujours d’arrêter de produire l’objet géant en premier placeM SK2

Retour au diagnostic

Ainsi, chaque fois que vous voyez une croissance constante du LOH dans un service de longue durée, la question est presque toujours la même. Quels sont les critères d’attribution des objets très grands, quelle cadence et pourquoi? La forme de la réponse (timer? gestionnaire des demandes ? serialisateurM SK3 pool de tamponMSC4 vous dit où regarder dans le code

Les exceptions sont également importantes. 4/sec de SqliteException est assez bas pour ne pas apparaître dans les registres quiconque lit, mais suffisamment élevé pour vous dire qu’un chemin de code se rétryne silencieusement . C’est ce type d’aiguillage que l’examen périodique repère la réponse à un incident qui n’y arrive jamais

Règle: lorsque quelque chose semble mal, confirmer avec dotnet-counters before guessing. Les chiffres atténueront le rayon de recherche d’une ordre de grandeur

Étape 3: La mauvaise approcheM SK1l'odeur d'abstraction (exemplaire amélioré: HNSW pour le cachingMSC4

C’est là que la discipline devient intéressante, car la temptation est toujours de ajustement le symptôme plutôt que de remettre en question l’abstraction.

Vocab rapide (pour les prochaines sectionsM SK1

  • Vector: un ensemble fixe de floats d’une taille de - - eM SK3g. float[64]. Chacune des fentes est une propriété mesurée de la demande. float[].
  • Comparaison des vecteurs: habituellement similitude des cosine - la cosine de l'angle entre deux vecteursM SK1 Plus proche de 1.0 = plus similaire. La mathématique réelle est un produit ponctuel divisé par deux normesMSC5 une méthode appeléeMNK6
  • Environ le voisin le plus proche (ANNM SK1: " étant donné ce vecteurM SK2 trouver le N le plus proche sur les millions, rapideMSC4 bruteMST5 la comparaison de force est OMSSK6NMSS7 par demandeMSL8 Les algorithmes ANN obtiennent des résultats sub--milliseconds au prix occasionnel d’avoir manqué le TÉLÉCOPIEURS match le plus proche.
  • HNSW: l’un de ces algorithmes ANN. Il construit un graphe stratifié où la couche supérieure a quelques puits - des nodes connectés et les couches inférieures remplissent en détailM SK3 Vous entrez au sommet et zoomez dansMSC4 Excellent pour indexer une grande Établissement stable ensemble de vecteurs. Non conçu pour "toute demande ajoute un et les anciens vont disparaîtreM SK2 - le point pertinent de ce tout postMSC4
  • Centres: la moyenne d'un groupe de vecteurs - un vecteur qui résume le regroupement. Si vous avez des vecteurs dont l'apparence est presque identique à celle de 10,000, vous pouvez les jeter et conserver un centroidM SK5 le centroid est un suppléant défaillant mais compactMSC6 Les stratégies de compression utilisent cela pour transformer une grande partie de l'histoire brute en une petite histoire sommaire.

Pour StyloBot,, la croissance du LOH a entraîné trois classes structuralement identiques dans le processus HNSW (Hiérarchique de petits mondes navigables géographiquesoriginal Malkov & papier Yashunin) utilisé pour la recherche de similitude au-dessus de la signatureM SK1 session , et vecteurs d'intention. Chacun avait un champ commeMSC4

private readonly List<float[]> _graphVectors = new();

Chacune a été fournie par un gestionnaire d'apprentissage qui s'est abonné à LearningEventType.FullDetection, qui déclenche un feu toutes les demandes HTTP. Chacune des demandes a ajouté un vecteur

Et tous les cinq minutes, ,, un Timeur de sauvegarde automatique a serialisé l’ensemble du graphique à JSON et l’a écrit sur le disque.

private readonly TimeSpan AutoSaveInterval = TimeSpan.FromMinutes(5);

À l’échelle de démonstration, signatures.vectors.json était 104 MBM SK1 En production: intent.meta.json à 70 MBM SK1 intent.vectors.json à 51 MBM SK1 La serialisation de JSON crée une chaîne contiguë dans la mémoire à |100+ MB. Cette chaîne va directement vers le LOH\ .\ Trois indices\ ,\ tous les cinq minutes\ MSC6\ croissant continuellement\ M SK7\

flowchart LR
    classDef input fill:none,stroke:#3b82f6,stroke-width:2px
    classDef async fill:none,stroke:#a855f7,stroke-width:2px
    classDef problem fill:none,stroke:#ef4444,stroke-width:2px

    R["HTTP request"]:::input --> H["LearningHandler<br/>FullDetection event"]:::async
    H --> L["List&lt;float[]&gt;<br/>graph vectors<br/>(no eviction)"]:::problem
    L -.5 min timer.-> J["JsonSerializer.Serialize<br/>~100 MB string"]:::problem
    J --> LOH["Large Object Heap"]:::problem
    L -->|grows every request| L
    LOH -.fragmentation.-> RSS["Process RSS climbs"]:::problem

Maintenant: la solution facile est d’ajouter un cap. MaxVectors = 10_000, Évacuation de la LRU , l’expédition à bord de celle-ci . Il aurait pu fonctionnerM SK3 Les chiffres seraient dépassés, le tableau de bord serait apparu bienMNK5 Il serait également erronéMRK6

HNSW est une excellente technologie et je l’utilise délibérément ailleurs (Self-Databases de vecteurs hébergées avec Qdrant, Recherche et indexation hybrides de RAG, GraphRAG minimal viable). Pinecone, Favoriser, pgvector, et Qdrant tous les utilisateurs l’utilisent à l’interne. "pour chaque empreinte digitale active, conserver une petite fenêtre de vecteurs comportementaux récents afin que la détection puisse comparer la demande actuelle avec le comportement antérieur d’un vecteur similaire Les empreintes digitales des bottes se répètent ( demeurer chaude ); les empreintements digitales humaines ne sont pas non plus visibles Le plus simple possible Post list the in-process HNSW index as a feature ; this post is the followingM SK2up admitting where that was the wrong fit.

Il y a un test rapide de l’humidité, :, si vous imaginez couper une bouchonneuse sur la structure. , est le résultat sémantiquement toujours ce que vous voulez.

Règle: Lorsque vous voyez une croissance sans limites, la question n’est pas "comment capter cette structure ?" mais "est-ce la bonne structure pour ce que nous faisons? La première dissimule le symptôme; la seconde entend ce qu’elle vous dit.

Étape 4: Choisissez la forme appropriée , pas seulement un cap

Une fois que vous avez désigné la mauvaise abstraction, le remplacement écrit habituellement lui-même. Pour StyloBot, il y avait deux couches.

La couche chaude: a délimitée BoundedVectorCache<TEntry>, un enveloppement épais autour ConcurrentDictionary avec accès-évacuation prioritaire de la fréquence. Le coefficient de retenue donne aux entrées classifiées par le bot -un poids de survie MSC3xM SK4 de sorte que l’auto-cache

retentionScorer: (_, entry) => entry.WasBot ? 2.0 : 1.0

La couche persistante (FOSS): trois nouvelles tables SQLite (signature_centroids, session_centroids, intent_centroids) entreposage de centrides comprimés à partir d'un VectorCompactionService. Vektors stockés sous forme de floats bruts MemoryMarshal.AsBytes:

internal static byte[] PackFloats(float[] v) =>
    MemoryMarshal.AsBytes(v.AsSpan()).ToArray();

Système de serialisation binaire compacte. Non 100+ Strings JSON MBM SK2 Aucun LOH.

Note de niveau d’entreposage. SQLite est la version FOSS / single-binary backend - le bon appel pour "mettre l’exe sur un Pi et marcher à l’extérieurM SK4 StyloBot commercial utilise PostgreSQL avec pgvector, vous donne une recherche de similitude indexée appropriée (HNSWM SK2IVFFlat dans la base de données - sur un corps stable , où l’HNSW appartient, à une échelle horizontaleM SK3 et état partagé dans toute la flottilleMSC4 La même règle architecturaleMska5 un backend différentM Ska6 sqlite-vss est une pierre angulaire si vous éloignez SQLite mais que vous voulez rester un seul utilisateur.

La recherche de similitude sur la couche persistante FOSS est brute-force cosine sur toutes les lignesM SK1 accéléré avec SIMD (System.Numerics.Tensors.TensorPrimitives). À l'échelle compacteM SK1centroïde ( centaines à quelques milliers de rangées), un balayage complet prend mSSK4 ms sur une PiMSC5 Celle-ci

Une note sur L1/LM SK1 compaction, depuis "compaction" fait du travail réel dans cette sectionM SK3 Le service de compaction fonctionne tous les soirs et réduit l'historique en deux passes

  • L1: prenez tous les vecteurs bruts écrits aujourd'hui , groupez ceux qui sont très similaires M SK2 et remplacez chaque groupe par un centroïde ( la moyenne MNK4 CheapMNK5 se produit fréquemment MMK6 modeste réduction de taille
  • L2: prendre LM SK1 centroïdes qui sont eux-mêmes semblables au cours des jours, / semaines et les fusionner de nouveau, MSC3 aggressif, ; se produit moins fréquemment,; réduction de grande envergure.

Il s'agit du même modèle LSM-moteurs de stockage d'arbres (RocksDB,LevelDB+, Cassandra=)Utilisation pour SSTablesM SK5Niveau 0contient des données récentes finesMSC7dont les données sont consolidéesMNK8les niveaux inférieurs contiennent des données compactesMMK9donts sommairesMNK10En empruntant, cela fonctionne parce que le mode d'accès est le mêmeMRK11la plupart des lectures atteignent les données récenteMRK12une petite fraction des lecturements ont besoin de la longue queueM SK13 Rien ne permet de garder chaque ligne brute pour toujours.

flowchart LR
    classDef input fill:none,stroke:#3b82f6,stroke-width:2px
    classDef store fill:none,stroke:#f59e0b,stroke-width:2px
    classDef async fill:none,stroke:#a855f7,stroke-width:2px
    classDef good fill:none,stroke:#22c55e,stroke-width:2px

    R["HTTP request"]:::input --> FP["Fast path<br/>BoundedVectorCache.TryGet"]:::good
    FP -->|hit| S["Use similarity signal"]:::good
    FP -->|miss| N["null<br/>other detectors still run"]:::good
    R -.post-response.-> BG["Background learning handler"]:::async
    BG --> DB["SQLite (FOSS)<br/>or Postgres+pgvector (paid)"]:::store
    BG --> WARM["Warm cache for next time"]:::async
    WARM --> FP
    NC["Nightly VectorCompactionService<br/>L1 then L2"]:::async --> DB
    DB --> NC

La forme générale (Cache chaude limitée pour l'état stable + entrepôt compact persistant de l'histoire + compacteur périodique entre eux) apparaît de plus en plus dans les systèmes d’apprentissage à long terme -en cours. Il vaut la peine de garder le ' dans votre trousse d’outilsM SK4 Lorsque quelqu’un s’adresse à l’HNSW ou au pgvector pour une charge de travail qui, selon lui, est en fait un cache, , c’est la chose la moins coûteuse

Règle: La structure de données la plus simple qui convient au modèle de temps d'exécution, non celui qui convient à l'ensemble de données ' sa taille sur papier

Étape 5: Faire en sorte que le chemin rapide tolére les erreurs

C’est une chose subtile, car il s’agit de retenue plutôt que d’addition.

Si le chemin de détectation rapide dans StyloBot est synchrone, et que la vérification des similitudes n’est pas une étude du dictionnaire qui bloque-, il y a temptation à bloquer ce chemin.

if (!_cache.TryGet(signatureId, out var entry))
    return null; // no signal this request - other 48 detectors still run

Une erreur signifie Aucun signal de similitude pour cette demande. Les autres détecteurs continuent de fonctionner. Un gestionnaire d'arrière-plan demande SQLite après que la requête a été remplie et que le cache est chauffé pour la prochaine fois.

C'est là où la plupart des systèmes de cache vont mal.

Règle: si vous pouvez't tolérer une erreur , vous n’avez pas'il n’y a pas de cacheM SK3 Vous avez une foule fantaisie

Étape 6: Vérification tous les accumulateur, non seulement l'actuelle forte

Une fois que vous avez trouvé une structure non liée, supposez qu’il y en a d’autres.

La vérification est mécanique. qui décide quand les entrées quittent la zone, quel est le cas le pire dans un trafic hostile? Si vous pouvez't répondre à toutes les trois dans une seule phrase , c’est la même discipline que le Signales éphémères Modèle: tout ce qui s'accumule doit se dégraderM SK1 évacuer , ou compacter.)

Pour StyloBot, la plupart des accumulateurs étaient déjà bien munis

  • EphemeralPatternReputationCache: cap dur à 10,000 entrées avec décomposition de fond et évacuation des LRU Neutral → Suspect → ConfirmedBad la machine étatique, et l'hysterèse asymétrique vivent dans Apprentissage pour devenir plus rapide)
  • BehavioralPatternAnalyzer: IMemoryCache avec des limites d'identité par - (50 voies de routesM SK3 | | 100 horaires, \ 15-min TTLMSC7
  • DriftDetectionHandler: 10,000 échantillons
  • SessionEscalationService: 35-minute TTL avec horlogeM SK2évacuation dirigée

On a besoin d’une attention au-delà des classes HNSW: MarkovTracker._cohortBaselines. Le Chaîne Markov (separée des chaînes de signatures perM SK2, qui avaient déjà été évacuées par les LRU à MaxTrackedSignatures). Les lignes de base des cohortes (une par cohorte de trafic, comme "centre de données -nouveau"ou S"résidentiel-retournement") n'ont pas eu d'évacuation du tout. La fixationM SK9 évacuer les cohorts les plus froides SSK10les transitions totales les moins élevées) lorsque le dictionnaire dépasse SelfMaintenanceOptions.MarkovCohortSize.

Règle: Chaque collecte d’un seul tonne répond à trois questions ou c’est-à-dire :

Étape 7: Faire chaque lien configurable

Le mode de défaillance suivant après "no bound" est "bound thatM SK3s wrong for this hardware MaxEntries = 10_000 est bon jusqu’à ce que quelqu’un effectue votre service sur Pi4, ou dans un conteneur avec 256 MBM SK2

Pour StyloBot, chaque limite a atterri sous un seul SelfMaintenanceOptions blocage dans appsettings.json. Les paramètres par défaut fonctionnent pour un serveur standard. LowMemory Présélection statique:

public static SelfMaintenanceOptions LowMemory => new()
{
    SignatureCacheSize  = 1_000,
    SessionCacheSize    = 500,
    IntentCacheSize     = 300,
    MarkovCohortSize    = 2_000,
    CacheSlidingExpiration = TimeSpan.FromHours(1),
};
builder.Services.AddBotDetection(opts =>
{
    opts.SelfMaintenance = SelfMaintenanceOptions.LowMemory;
});

Ou par l'entremise appsettings.json pour l'environnement-ajustement spécifiqueM SK1

{
  "BotDetection": {
    "SelfMaintenance": {
      "SignatureCacheSize": 1000,
      "SessionCacheSize": 500,
      "IntentCacheSize": 300,
      "CentroidRetentionDays": 14
    }
  }
}

Le point plus profond, :, une limite que ' fixe en un seul endroit est quelque chose dont les opérateurs peuvent raisonnablement penser. const int les déclarations est quelque chose que personne ne peut expliquer.

Règle: tous les limites que l’opérateur pourrait vouloir changer pour leur matériel vie dans un bloc de configuration , ne pas éparpillée à travers la base de code .

À quoi ressemble "fixed"

Pour StyloBot spécifiquement (FOSS buildM SK1 LowMemory preset):

Composante Component Avant Après M
Indice de la signature HNSW LOH non lié
Indice de l'HNSW des séances LOH non lié
Indice de l'intensité du HNSW LOH non lié
Les tampons de sauvegarde automatique du JSON MSC2 LOH MB tous les MSC3 min SMC4
Lignes de référence de la cohorte de Markov Non lié
La couche de vecteur totale 13+ GB LOH <6 Mo

Le modèle de détection est inchangé. Ce qui a changé où l'on trouve des preuves de similitude et quand il est permis de modifier le chemin rapide. Centroïdes survivants remarrages dans SQLite (FOSS) ou PostgresM SK3pgvector MSC4paidMSC5 la compaction nocturne produit encore LMST6LMSS7 aucun n'exige une mémoire non limitéeMSST8

Sur un Pi4 avec le LowMemory preset, la construction FOSS se situe sous M SK2 MB RSS après le réchauffement , indéfinimentMNK4 Postgres payésMMK5 les déploiements appuyés héritent de la même chaleurMRK6 discipline des caches MNK7 la couche persistante s'élargit simplement horizontalement au lieu d'être vivant à côté du processusMZK8 En tout état de cause

La leçon générale

L’ajout d’un cap limite la mémoire sans changer l’architecture. La mauvaise abstraction demeure erronéeM SK1 le symptôme devient simplement plus silencieux . La personne suivante qui touche le système hérite d’une structure qui pratiquement fonctionne, ce qui est pire que celle qui ne fonctionne pas de toute évidence.

Le modèle qui se répète dans les systèmes d’apprentissage à long terme

  • Capter le chemin chaud: boundedM SK1 in-memory , conçu pour tolérer les missesMSC4 politique d'évacuation alignée sur la charge de travail
  • Compression de l'histoire: store binaire persistante compacte , comprimée périodiquement , accessible hors du fil de demande
  • Vérification de tout le reste: chaque accumulateur répond à ce qui l'oblige , ce qui le évacue
  • Centraliser les boutons: un bloc de configuration , pas quinze constLa plupart des enquêtes menées dans le cadre de l’enquête ont été effectuées par les autorités locales.
  • Calendrier de la recherche: le bug n'existe que dans le système en cours

Fixer la forme, non le symptôme.


Autres lectures

StyloBot : série de sorties

HNSW et recherche vectorielle dans ce blog

Références externes

Source de la mise en œuvre: github.comM SK1scottgal/stylobot. moteur en direct , tableaux de bord M SK2 et contrôles commerciaux à stylobot.net.

Finding related posts...
logo

© 2026 Scott Galloway — Unlicense — All content and source code on this site is free to use, copy, modify, and sell.