# StyloBot-Release-Serien – Finden und Fixieren von ungebundenem Wachstum in langen --Running-.-NET Services

*Long---Running Dienste neigen dazu, langsam in Richtung ungebundenen Gedächtnisses zu driften..-Hier finde ich, wie ich diese Drift finde, ,-wie ich darüber nachdenke, es zu fixieren, ohne über ihn zu papieren. ,-und ein bearbeitetes Beispiel von StyloBot.'-Vektorgleichheitsschicht, die es von 13-GB auf der Large Object Heap zu unter 6-MB nahm*

[<img src="/articleimages/stylobot-logo.svg" alt="StyloBot" width="120" />](https://www.stylobot.net)

> **StyloBot Release Series**
> 
> 1. [**Verhalten nicht Identität**](/blog/stylobot-fingerprint)Warum StyloBot die Kunden verhaltensmäßig modelliert
> 2. [**Verhalten Bewusst WERDEN**](/blog/behaviour-aware-ux)Die Oberfläche des Servers wurde über dieses Erkennungsergebnis gespiegelt.
> 3. **Ungebundenes Wachstum in langen --Diensten finden und beheben**Die Zuverlässigkeitsdisziplin ist ein Beispiel für die Bearbeitung von :. StyloBot.).
> 4. [**Verhalten--Bewusstsein TypScript UI**](/blog/typescript-sdk)Komponenten von , und Browsern
> 5. [**Die Sidecar-Architektur**](/blog/sidecar-architecture)Wie der Detektions-Engine mit Nicht--.-NET-Stacks verbunden wird
> 6. [**Schneller lernen**](/blog/stylobot-release-learning)Das adaptive Lernsystem (MSK) und der Urteilscache
> 7. [**Testen der Sache, die Won-' sitt Still**](/blog/stylobot-release-nondeterministic-testing)Eine BDF-Datei treibt Regression an.
> 8. [**StyloExtrahieren Sie einen lokalen HTML zu Markdown Konverter**](/blog/stylobot-release-styloextract)Die HTML-Markdown-Schicht, die sich mit dem Detektor verbindet, hat den Walker Bug lucidVIEW gefangen und die Hundefutterschleife, die es ehrlich machte.

<!--category-- ASP.NET, StyloBot, Bot Detection, Performance, Architecture -->
<datetime class="hidden">2026-06-01T10:30</datetime>

Das bearbeitete Beispiel ist der StyloBot Vektor Layer, aber die Disziplin ist: '.StyloBot.-.specific.....Jeden langen -.Running-Dienst, der Zustand aus dem Verkehr anhäuft, wächst schließlich eine Klasse von Fehlern, die Sie in Tests oder kurzen Lastläufen fangen können...Es zeigt sich nur nach Tagen des realen Verkehrs.MSC8. Dieser Beitrag ist das Playbook, mit dem ich es finde

Das Verhaltensmodell ist in [Verhalten nicht Identität](/blog/stylobot-fingerprint)Die Oberfläche von ASP-.-NET in [Verhalten Bewusst WERDEN](/blog/behaviour-aware-ux)Quelle: [github-.-com./-scottgal./-stylobot](https://github.com/scottgal/stylobot).

[TOC]

---


## Die Kategorie der Probleme

Jeder Cache, jeder Lernspeicher, jedes ,, jedes ", jedes ®', halten einfach die letzten N-Anfragen, jeder Puffer ist ein winziger Akkumulator, jeder sieht auf eigene Faust gut aus.,, zusammen mit einem Prozess, der für eine Woche geöffnet war.,, bilden sie eine Speicherform, die kein Test jemals reproduziert.

- arbeitet fein in dev-,-CII und der erste Tag in der Produktion
- nach und nach in Richtung OOM-, drift, dann entweder abstürzt oder beginnt zu paging

Du fängst das hier an *absichtlich danach suchend*Auf einem Prozess, der lange genug läuft, damit die Form entstehen kann.

## Schritt -1: , Periodische Zuverlässigkeit Bewertungen -( , der Akt des Aussehens , ) ,

Das erste Stück der Disziplin ist der Kalendereintrag.

Alle paar Releases ich stoppe das Hinzufügen von Features und schaue einfach auf das laufende System unter realistischem Traffic. *tut jeder dieser Blick falsch* Kein spezifisches Problem ,, , kein Ziel .. , nur suchend ,. , Laufende Systeme liegen leise , ; , sie nicht laut scheitern , bis sie katastrophal fehlschlagen , . , Wenn Sie nur schauen, wenn etwas bricht ,, , Sie bereits verloren ' ,

Die neueste Überprüfung gefangen den Fehler dieser Beitrag ist über . `Mostlylucid.BotDetection.Demo` Prozess sitzen an einem -20 , GB Bewohner unter synthetischem Testverkehr eingestellt , dass , ' , die Art von Zahl, die nicht überleben sollte dreißig Sekunden Aufmerksamkeit ,. ,

**Artikel 2 Absatz 1 Buchstabe b** Setzen Sie einen wiederkehrenden Steckplatz auf den Kalender, um Ihre eigenen Metriken zu lesen..-Nicht, um irgendetwas zu beheben.

## Schritt - Lassen Sie Zähler überraschen Sie

Das richtige Werkzeug für -"- ist etwas falsch -?"-auf einem -mesK2-NET-Prozess ist [`dotnet-counters`](https://learn.microsoft.com/en-us/dotnet/core/diagnostics/dotnet-counters)Es ist kostenlos, es ist bereits installiert, und es sagt Ihnen die Wahrheit darüber, was Ihr Prozess jetzt tut.

Ein paar Kommandos später auf der 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
```

Das ist genug, um zu wissen, was nicht stimmt, ohne irgendeinen Code zu lesen. [Große Objekt-Heap](https://learn.microsoft.com/en-us/dotnet/standard/garbage-collection/large-object-heap) war auf der 13.4, GB und wächst bei fast 100, MB, /,sec, ., etwas war die Zuteilung riesiger Objekte kontinuierlich und die Gen-2, GC konnte nicht mit ihnen Schritt halten

### Ein schnelles Auffrischen auf der LOH -(, weil jeder dev trifft dieses schließlich .

Die Large Object Heap ist eine der häufigsten Überraschungen beim ersten Profilieren eines langen --Laufservices, denn fast jeder trifft es auf die harte Art und Weise.

- Die GC hat drei Generationen -- (--Gen-0,-Gen.1,-Gen, die für normale Kurzfassungen verwendet werden -- --lived objects --MSC5 -- Die meisten Zuweisungen leben und sterben in Gen.MSC6, die billig zu sammeln ist.
- **Alles, was größer ist als 85. KB tut '. geht nicht in jenen Generationen** Es geht geradeaus auf die **Große Objekt-Heap**.
- Die LOH wird nur während einer **GENERALDIREKTION GC**Das ist die teuerste Sammlung, das ist ..NET tut ..Gen.2. geschieht selten und die Laufzeit versucht es zu vermeiden.
- Schlimmer: Standardmäßig ist die LOH **nicht verdichtet** Wenn es gesammelt wird, befreit es einfach die Slots... Also auch nach einem Gen-2,. die LOH wird fragmentiert ?:. Sie haben freien Raum, aber es ist '. in den falschen -.große Löcher für die nächste Allokation - .. Neue große Objekte erweitern den Haufen anstatt ihn wiederzuverwenden
- Net effect.:, halte große Objekte mit einer konstanten Rate zuordnend und dein Prozess marschiert der Memory Footprint von ' nach oben, ob die Objekte noch referenziert werden oder nicht, es sieht wie ein Leck aus, auch wenn es eins ist.

Die größten Quellen des zufälligen LOH-Wachstums in realen .-NET-Diensten... in meiner Erfahrung...

Quelle : |, warum es auf dem LOH endet.
|---|---|
| `JsonSerializer.Serialize(obj)` Rückgabe einer `string` Ein MB-Objekt wird zu einem MB-String in einer Zuweisung.
| `MemoryStream` du lässt unbounded wachsen |. Interner Puffer verdoppelt sich hinter 85. KB und bleibt auf der LOH.
| `byte[] buf = new byte[n]` für große `n` Die direkte LOH-Zuweisung von ; ist sehr häufig in der Datei / network liest sich
| `List<T>` das vorbei wächst ~10-K-Referenzen --typierte Elemente |-Backing Array Kreuze 85-KB und landet auf der LOH |-
| `string.Concat` / `StringBuilder` über den großen Text `ToString()` ist eine einzige riesige Zuweisung -|-
| `XmlSerializer` / `DataContractSerializer` von großen Graphen Die gleiche Form wie der JSON-Fall

Wenn Sie sich nur an eine Faustregel erinnern **einem Codepfad misstrauisch sein, der ein einzelnes zusammenhängendes großes Objekt auf einem Timer oder pro Anfrage erzeugt** Das ist die Form, die lange Wracks - Laufende Dienste , und es ' ist genau die Form wir ' sind im Begriff, in StyloBot zu finden

```mermaid
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
```

Werkzeuge, die helfen, es zu diagnostizieren

- [`dotnet-counters`](https://learn.microsoft.com/en-us/dotnet/core/diagnostics/dotnet-counters) für Live-Zahlen, das LOH-Messgerät über dem ).

- [`dotnet-gcdump`](https://learn.microsoft.com/en-us/dotnet/core/diagnostics/dotnet-gcdump) und [`dotnet-dump`](https://learn.microsoft.com/en-us/dotnet/core/diagnostics/dotnet-dump) für Snapshots, die Sie in Visual Studio oder PerfView öffnen können

- [PerfView](https://github.com/microsoft/perfview) selbst für Tracing-Zuweisungen bis hinunter zu einem Stack

- **JetBrains CLI Profiler** Was ich eigentlich für dieses Rework verwendet habe, ist jetzt als richtige globale Tools verpackt.,e so kopflos entfernte /e CI-Laufzeitprofilierung braucht keine Rider GUUI.MSC6e
  
  - [`JetBrains.dotMemory.GlobalTools`](https://www.jetbrains.com/help/dotmemory/Command-Line_Profiler.html)Nehmen Sie einen Speicher-Snapshot, öffnen Sie die `.dmw` workspace später zu sehen, welche erhaltenen Wurzeln halten die LOH-Zuweisungen
  - [`JetBrains.dotTrace.GlobalTools`](https://www.jetbrains.com/help/profiler/Performance_Profiling__Profiling_Using_the_Command_Line.html)Verwenden Sie dies, wenn Sie einen Stapel anstatt eines Zählers benötigen...g.., was ist das Timer tatsächlich tut??")..
  
  ```bash
  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
  ```
  
  Arbeitsteilung für diese Überarbeitung `dotnet-counters` sagte mir, die LOH war das Problem... die dotMemory-Snapshot erzählte mir, dass der Autosave-Timer die Ursache ist.

Es gibt Fluchtluken, wenn du sie wirklich brauchst `GCSettings.LargeObjectHeapCompactionMode = CompactOnce` erzwingt eine One---off LOH-Verdichtung [`RecyclableMemoryStream`](https://github.com/microsoft/Microsoft.IO.RecyclableMemoryStream) von Microsoft Pools Puffer, um die Zuteilung in erster Linie zu vermeiden .. . Beide sind Putze Die reale Fix ist fast immer zu stoppen Produktion des riesigen Objekts in den ersten Platz ?. .

### Zurück zur Diagnose

Also, wann immer du ein stetiges LOH-Wachstum in einem langen - running Service siehst, ist die Frage fast immer die gleiche. *Was ist ', die sehr große Objekte zuweisen? ,, auf welche Kadenz? , und warum?* Die Form der Antwort (-Timer-Request-Handler-?-Serializer-?-Puffer-Pool sagt Ihnen, wo Sie im Code suchen

Auch die Ausnahmen spielten eine Rolle. `4/sec` von `SqliteException` ist niedrig genug, um nicht in Protokollen zu zeigen, die jemand liest.,, aber hoch genug, Ihnen zu sagen, ein Code-Pfad stumm wieder versucht wird...

**Artikel 2 Absatz 1 Buchstabe b** wenn etwas falsch aussieht. `dotnet-counters` vor dem Raten-Erraten-.-Die Zahlen werden den Suchradius um eine Größenordnung von Magnitude verengen -.-

## Schritt: Der falsche Geruch von -Abstraktion, Beispiel: :, HNSW für Caching, ),

Dies ist, wo die Disziplin wird interessant weil die Versuchung immer *Pflaster* das Symptom, anstatt die Abstraktion in Frage zu stellen

> ### Schnell Vokabeln für die nächsten paar Abschnitte
> 
> - **Vektor**Eine feste Anordnung von Floats in der Größe von -. `float[64]`Jeder Steckplatz ist eine gemessene Eigenschaft der Anforderung.( header count, , timing, IP-Familie, , usw., MSC5 Zwei Anfragen, die ähnlich sind, wie " zu erzeugen Vektoren, die einander nahe im Raum von 64- dimensional sind. `float[]`.
> - **Ähnlichkeit der Vektoren**Üblich [Cosin-Ähnlichkeit](https://en.wikipedia.org/wiki/Cosine_similarity) Die tatsächliche Mathematik ist ein Punktprodukt, das durch zwei Normen geteilt wird.
> - **Ungefährer nächster Nachbar**" ist dieser Vektor gegeben , Finden Sie das nächste N aus Millionen , Schnell". , Brute-- , Kraft Vergleich ist O.( , N.SK7 , pro Abfrage . , ANN-Algorithmen erhalten sub ,- , Millisekunde Ergebnisse auf Kosten des gelegentlichen Fehlens der *wahr* engste Übereinstimmung
> - **HNSW**Es baut einen geschichteten Graphen, in dem die obere Schicht ein paar gut verbundene Knoten und untere Schichten im Detail ausfüllen. *stabil* Satz von Vektoren, die nicht für jede Anfrage entwickelt wurden, fügt ein und alte hinzu, gehen weg, "... den relevanten Punkt dieses ganzen Beitrags.
> - **Centroid**Ein Vektor, der den Cluster zusammenfasst Wenn Sie die Vektoren haben, die alle im Großen und Ganzen ähnlich aussehen, können Sie sie wegwerfen und einen Zentrioid halten.Der Zentraid ist ein verlustiger aber kompakter Ersatz... Verdichtungsstrategien verwenden diese, um eine Menge Rohgeschichte in eine kleine Zusammenfassung Geschichte zu verwandeln.

Das LOH-Wachstum führte zu drei Klassen, die strukturell identisch waren. [HNSW](https://en.wikipedia.org/wiki/Hierarchical_navigable_small_world_graphs) .( Hierarchische Navigation Kleine Welt . . ) .[Original Papier Malkov -&-Yashunin](https://arxiv.org/abs/1603.09320)Für die Ähnlichkeitssuche über Signature verwendet, ,.Session.,. und Intent-Vektoren, MSV3. Jeder hatte ein Feld wie :.

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

Jeder wurde von einem Lern-Handler gefüttert, der abonnierte `LearningEventType.FullDetection`, die auf Feuer **jede HTTP-Anfrage**Jede Anfrage fügte einen Vektor hinzu.

Und alle fünf Minuten ein Autosave Timer serialisierte den gesamten Graphen zu JSON und schrieb es auf Diskette

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

Im Demo-Skala `signatures.vectors.json` war 104, MB, ., in der Produktion `intent.meta.json` bei 70, MB, ,, `intent.vectors.json` bei 51 , MB , . , JSON-Serialisierung erzeugt eine zusammenhängende Zeichenkette im Speicher bei 100+ , . , Das String geht direkt auf die LOH ,. , Drei Indizes , alle fünf Minuten , und , , kontinuierlich wachsend .. ,

```mermaid
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
```

Jetzt ist der einfache Fix, um eine Kappe hinzuzufügen. `MaxVectors = 10_000`Es hätte geklappt ,. , Zahlen würden nach unten kommen ,, , Dashboard würde fein aussehen , . , Es wäre auch falsch gewesen .. ,

HNSW ist eine ausgezeichnete Technologie und ich benutze sie bewusst anderswo.[Selbstbeleuchtete Vektordatenbanken mit Qdrant](/blog/self-hosted-vector-databases-qdrant), [RAG Hybrid Suche und Indexierung](/blog/rag-hybrid-search-and-indexing), [Mindestlebenswerte GraphRAG](/blog/graphrag-minimum-viable-implementation)). [Kiefernzapfen](https://www.pinecone.io/learn/series/faiss/hnsw/), [Schwitzen](https://weaviate.io/developers/weaviate/concepts/vector-index#hnsw-index), [pgvector](https://github.com/pgvector/pgvector#hnsw)L 347 vom 20.12.2013, S. 671. [Qdrant](https://qdrant.tech/documentation/concepts/indexing/#vector-index) alle verwenden es intern, aber es ist ein Index eines corpus, nicht ein cache und was StyloBot tatsächlich brauchte, war ein Cache. *Bewahren Sie für jeden aktiven Fingerabdruck ein kleines Fenster neuer Verhaltensvektoren auf, damit die Detektion die aktuelle Anforderung gegen vergangenes Verhalten von ähnlichen vergleichen kann.* Bot-Fingerabdrücke wiederholen (, bleiben heiß);, menschliche Fingerabdrucke Don-', nicht (, verfehlt)., die früheren [So einfach wie möglich](/blog/botdetection-part3-as-simple-as-possible) post listet die in---Prozess HNSW Index als feature.;. Dieser Beitrag ist der follow--.up-Zulassung, wo das war die falsche fit?..

Es ist ein schneller Test für den Geruch, wenn du dir vorstellst, eine harte Kappe auf die Struktur zu ohrfeigen, ist das Ergebnis immer noch semantisch die Sache, die du wolltest.?. Ein geknapptes HNSW mit konstantem Churn ist ein Index, der ,.

**Artikel 2 Absatz 1 Buchstabe b** wenn du ungebundenes Wachstum siehst, ist die Frage nicht *Wie können wir diese Struktur kapieren?* aber *Dies ist die richtige Struktur für das, was wir tun* Das erste verbirgt das Symptom.Das zweite hört, was es sagt.

## Schritt - wählen Sie die richtige Form - nicht nur eine Kappe

Sobald du die falsche Abstraktion genannt hast, schreibt sich der Ersatz gewöhnlich für StyloBot aus zwei Schichten.

**Heiße Schicht** einer Begrenzung `BoundedVectorCache<TEntry>`Ein dünner Wrapper um [`ConcurrentDictionary`](https://learn.microsoft.com/en-us/dotnet/api/system.collections.concurrent.concurrentdictionary-2) Der Retentions-Scorer gibt Bot---klassifizierten Einträgen ein Überlebensgewicht von 2x, so dass sich der Cache selbst um das Verkehrsmuster organisiert.

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

**Persistente Schicht** drei neue SQLite-Tabellen`signature_centroids`, `session_centroids`, `intent_centroids`Die Lagerung der komprimierten Zentrioide aus einer Nacht `VectorCompactionService`Vektoren, die als roher Float gespeichert werden [`MemoryMarshal.AsBytes`](https://learn.microsoft.com/en-us/dotnet/api/system.runtime.interopservices.memorymarshal.asbytes):

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

Kompakte binäre Serialisierung – .- Nein – MB-JSON-Strings – .-Nein – LHH-.-Nein

> **Speicher-Tier Anmerkung ..** SQLite ist das FOSS-Single--Binary-Backend, das den richtigen Ruf für -MS K3Drop die Exe auf einem Pi und zu Fuß entfernt. [pgvector](https://github.com/pgvector/pgvector)Sie geben Ihnen die richtige indizierte Ähnlichkeitssuche *in der Datenbank* Die HNSW gehört zur horizontalen Skala von , und teilt den Zustand über eine Flotte von .. Die gleiche architektonische Regel ist ,. Verschiedenes Backend. [sqlite---vss](https://github.com/asg017/sqlite-vss) ist ein Sprungstein, wenn Sie SQLite outgrow aber wollen single-Moser bleiben wollen, ist der Cleaner Upgrade das Postgres Tier.

Ähnlichkeitssuche auf der persistenten FOSS-Schicht ist brute.-, Kraftkosinus über alle Reihen, beschleunigt mit SIMD.[`System.Numerics.Tensors.TensorPrimitives`](https://learn.microsoft.com/en-us/dotnet/api/system.numerics.tensors.tensorprimitives)Im verdichteten - Zentrioid-Skala (hunderte bis zu ein paar tausend Reihen ),, ein vollständiger Scan nimmt ~1-2. ms auf einem Pi... 4., dass ' fein ist. :, das nur im Hintergrund Handler läuft. MSC8 nie auf der Erkennung schnellen Pfad

Ein Hinweis auf **Verdichtung**Der Verdichtungsservice läuft nachts und reduziert die Geschichte in zwei Pässen.

- **ENTWICKLUNG**Nehmen Sie alle rohen Vektoren, die heute geschrieben werden., group diejenigen, die sehr ähnlich sind, und ersetzen jede Gruppe mit einem Zentrioiden.
- **ENTWICKLUNG**Nehmen Sie L.1, Zentrioide, die sich über Tage ähnlich sind, /Wochen und verschmelzen sie wieder ., Aggressive, ;, geschieht weniger häufig ;, großflächige Reduktion, andere ~10,x-)., was überlebt L. 2, ist die langfristige Form des Verkehrs, nicht das vorübergehende Rauschen.

Dies ist das gleiche Muster LSM---Tree Storage Engines -(-RocksDB-,-LevelDB-MS K3-Casandra-)-Einsatz für SSTables-:-Level hält die jüngsten Feingehalte von --geschliffenen Daten, die unteren Ebenen halten verdichtet,-Zusammengefasste Daten, weil es funktioniert, weil das Zugriffsmuster das gleiche ist :-die meisten liest Schlag aktuelle Daten, ;-ein kleiner Bruchteil der Lesegeräte benötigen den langen Schwanz .;-keine Vorteile aus halten jede rohe Reihe für immer ?.-

```mermaid
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
```

Die allgemeine Form**begrenzter Hot-Cache für den Steady-State kompakter persistenter Speicher für die Geschichte eines periodischen Verdichters zwischen ihnen**' ist es wert, in Ihrem Toolkit zu halten Wenn jemand nach HNSW oder pgvector für eine Workload erreicht, die eigentlich ein Cache ist, ist dies das billigere, was sie stattdessen für . erreichen sollten

**Artikel 2 Absatz 1 Buchstabe b** die einfachste Datenstruktur bevorzugen, die zum *Laufzeitmuster*Nicht derjenige, der zum Datensatz passt, sondern die Größe auf Papier.

## Schritt 5:. Machen Sie den schnellen Weg tolerieren Misses

Ein subtiles, weil es sich um Zurückhaltung und nicht um Hinzufügung handelt.

Wenn der langsame --Fix-Pfad einen Datenbank-Lookup beinhaltet, ist die Versuchung, den schnellen Pfadblock auf ihm zu machen... Don...'.t.. Der schnelle Erkennungspfad in StyloBot ist synchron und die Ähnlichkeitsprüfung ist ein nicht--.blocking Wörterbuch suchen.

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

Vermisst bedeutet *keine Ähnlichkeit Signal dieser Anfrage*Die anderen Detektoren laufen immer noch ,.' Ein Hintergrund-Handler fragt SQLite nach der Anfrage und erwärmt den Cache für das nächste Mal.

Dies ist, wo die meisten Caching geht falsch, die Menschen bauen einen Cache, die ,... machen den Fallback-Synchronous, für Korrektheit, und der Fehlpfad wird ein 50.ms p.99.s Spike, die .. Der Cache war gemeint, um die Dinge schneller zu machen, sondern es machte den schlimmsten Fall schlechter.

**Artikel 2 Absatz 1 Buchstabe b** Wenn du ein Miss-, nicht tolerieren kannst, hast du einen Cache - . - Du hast eine ausgefallene Warteschlange.

## Schritt zur Prüfung *jedes* Akkumulator - nicht nur die laute

Sobald Sie gefunden haben, eine unbounded Struktur, nehmen Sie an, es gibt andere, die laute ist nur derjenige, der zuerst brach

Das Audit ist mechanisch, für jedes lange Wörterbuch, für jede Liste oder für jeden Satz von : *was seine Größe begrenzt, wer entscheidet, wann die Einträge verlassen und was der schlimmste Fall unter feindlichem Verkehr ist.* Wenn du alle drei in einem Satz beantworten kannst, dann ist es ein Leck. [Vergängliche Signale](/blog/ephemeral-signals) model-:-alles, das sich ansammelt, muss zerfallen,-ausheben,-oder kompakt.)-

Für StyloBot-,- die meisten Akkumulatoren waren bereits gut ---bounded

- `EphemeralPatternReputationCache`Die Hartkappe bei Einträgen mit Hintergrundzerfall und LRU-Eviktion `Neutral` → `Suspect` → `ConfirmedBad` Zustand Maschine und die asymmetrische Hysterese leben in [Schneller lernen](/blog/stylobot-release-learning))
- `BehavioralPatternAnalyzer`ImemoryCache mit Per---Identitätsgrenzwerten (50-Wege ,, Timings ,, 15- und TTL-)
- `DriftDetectionHandler`Muster von ×, Proben von ,, TTL, -, beschnitten
- `SessionEscalationService`TTL mit Timer, -, angetriebene Räumung

Man brauchte mehr Aufmerksamkeit über die HNSW-Klassen hinaus. `MarkovTracker._cohortBaselines`Die [Markov-Kette](https://en.wikipedia.org/wiki/Markov_chain) Tracker hält per---Cohort Ausgangsübergang Matrizen -(-Separat von per-MSC2-Signaturketten, die bereits LRU-Eviktion bei `MaxTrackedSignatures`Die Kohorten-Grundlagen der Kohorte, wie z.B. "Datenzentrum, -new" oder "Residential-Returning"), hatten keine Räumung, wenn das Wörterbuch die kältesten Kohor ten übertrifft. `SelfMaintenanceOptions.MarkovCohortSize`.

**Artikel 2 Absatz 1 Buchstabe b** jede singleton Sammlung beantwortet drei Fragen oder es ' ist ein bug.:, was sie begrenzt ,, was vermeidet es MSKS3, was -' ist seine schlechteste - Gehäuseform

## Schritt 7: . Machen Sie jeden gebundenen konfigurierbar

Der nächste Fehler-Modus nach dem " ist ", dass ' falsch für diese Hardware ist. "., Hard... `MaxEntries = 10_000` ist in Ordnung, bis jemand Ihren Service auf einem Pi-4, oder in einem Container mit MSA1.

Für StyloBot.,, jedes Limit landete unter einer einzigen `SelfMaintenanceOptions` Blockieren `appsettings.json`Die Standardwerte arbeiten für einen Standardserver. `LowMemory` statische Voreinstellung

```csharp
public static SelfMaintenanceOptions LowMemory => new()
{
    SignatureCacheSize  = 1_000,
    SessionCacheSize    = 500,
    IntentCacheSize     = 300,
    MarkovCohortSize    = 2_000,
    CacheSlidingExpiration = TimeSpan.FromHours(1),
};
```

```csharp
builder.Services.AddBotDetection(opts =>
{
    opts.SelfMaintenance = SelfMaintenanceOptions.LowMemory;
});
```

Oder über `appsettings.json` für die Umgebung----spezifisches Tuning -:-

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

Der tiefere Punkt, der an einem Ort gesetzt wird, ist etwas, das Operatoren über die Grenze von . urteilen können. `const int` Deklarationen ist etwas, das niemand über die Zentralisierung der Drehknöpfe begründen kann, ist die Hälfte des Seins selbst.

**Artikel 2 Absatz 1 Buchstabe b** jede Bindung, die der Bediener für sein Hardwareleben in einem Konfigurationsblock ändern möchte... nicht über die Codebase verstreut.

## Wie "-Fixed" aussieht

Für StyloBot speziell für (-FOSS bauen,- LowMemory voreingestellt

Komponenten vor der Verwendung nach der Verwendung von |
|---|---|---|
Unterschrift HNSW Index Unbounded LOH Hot-Cache
Sitzungs-HNSW-Index Unbounded LOH - Hotcache KB
Intent-HNSW-Index: Ungebundener LOH: Hotcache für |
|, JSON-Autosave-Puffer |, 100-500, MB LOH alle 5, min. |, MKS5, MSC6,
Ausgangswerte der Markov-Kohorten
Gesamt-Vektorschicht **,13+ , GB LOH** | **<6, MB** |

Das Detektionsmodell ist unverändert. *wo Ähnlichkeitsnachweise leben* und *wenn es erlaubt ist, den schnellen Pfad zu beeinflussen*Die Centroids überleben die Neustarts in SQLite.(.FOSS-). oder Postgres+.pgvector, (.paid.MSC5. Nachtverdichtung produziert immer noch keines davon erfordert ungebundenes Gedächtnis.

Der FOSS build sitzt auf einem Pi-M4 mit dem LowMemory preset.,.Der FOSS build befindet sich nach der Aufwärmung unter 500. MB RSS auf unbestimmte Zeit..... Bezahlte Postgres...-.backed deployments erben die gleiche heiße -.Cache-Disziplin...;.Die persistente Schicht skaliert einfach horizontal, anstatt neben dem Prozess zu leben.....

## Die allgemeine Lektion

Die falsche Abstraktion bleibt falsch Das Symptom wird einfach leiser Die nächste Person, um das System zu berühren erbt eine Struktur, die fast funktioniert, Das ist schlimmer als eine, die offensichtlich tut .

Das Muster, das in langen Lernsystemen reproduziert wird

- **Besiege den heißen Pfad**:-umgeschränkt,-in-Memory-,-designed, um Misses zu tolerieren,-Eviktionspolitik ausgerichtet auf die Arbeitsbelastung
- **Komprimieren Sie die Geschichte**Kompakter binärer persistenter Speicher, regelmäßig verdichtet, vom Anforderungsgewinde aus zugegriffen
- **Prüfen Sie alles andere**Jeder Akkumulator antwortet, was es begrenzt, ,, was vermeidet es ,, was das Schlimmste Fall
- **Zentralisieren Sie die Knöpfe**Ein Konfigurationsblock, nicht fünfzehn `const`s
- **Planen Sie die Suche**Der Fehler existiert nur im laufenden System

Befestigen Sie die Form nicht das Symptom

---


## Literaturhinweise

**StyloBot Release-Serie**

- [Verhalten nicht Identität](/blog/stylobot-fingerprint)Das Verhaltensmodell und das Leiden-Clustering dieser Ebene ist der Speicher für
- [Verhalten Bewusst WERDEN](/blog/behaviour-aware-ux)Die Oberfläche, die Erkennungsurteile in Razor verbraucht
- [Schneller lernen](/blog/stylobot-release-learning)Das vierstufige Lernsystem und das Urteil-Cache, dass diese Speicherdisziplin begrenzt bleibt
- [Bot Detection Part Signature Pipeline und StyloBot Architecture](/blog/botdetection-part2-signature-pipeline-and-stylobot-architecture)Die Original-Erkennung der Triebwerksarchitektur post
- [Bot Detection Teil so einfach wie möglich](/blog/botdetection-part3-as-simple-as-possible)Der zwei- oder mehrzeilige Tropfen von -in der (, bei dem der HNSW Index erstmals eingeführt wurde

**HNSW und Vektorsuche in diesem Blog**

- [Selbstbeleuchtete Vektordatenbanken mit Qdrant](/blog/self-hosted-vector-databases-qdrant)HNSW-Tieftauchgang :, Graphenschichten `M`, `ef_construct`Abstimmung
- [RAG Hybrid Suche und Indexierung](/blog/rag-hybrid-search-and-indexing)HNSW als dichte Hälfte einer hybriden Retrieval-Pipeline
- [Mindestlebenswerte GraphRAG](/blog/graphrag-minimum-viable-implementation)- DuckDB VSS HNSW gotcha`array_cosine_distance` vs. `array_cosine_similarity`)
- [Semantische Suche mit ONNX und Qdrant](/blog/semantic-search-with-onnx-and-qdrant)Konfiguration der HNSW-Konfiguration über den Qdrant-Client
- [GraphRAG-:® Warum Vektorsuche auf der Corpus-Ebene nach unten bricht](/blog/graphrag-knowledge-graphs-for-rag)Warum reines ANN ausreichend im Maßstab ist

**Externe Verweise**

- [HNSW-Wikipedia-)](https://en.wikipedia.org/wiki/Hierarchical_navigable_small_world_graphs)
- [Malkow & Yashunin , *Effiziente und robuste Näherungssuche mit Hierarchical Navigable Small World Graphen*](https://arxiv.org/abs/1603.09320)Das Original Papier
- [Pinecone, MSK, Faiss, HNSW-Erklärer](https://www.pinecone.io/learn/series/faiss/hnsw/)
- [Qdrant-Indizierungskonzepte](https://qdrant.tech/documentation/concepts/indexing/#vector-index)
- [Große Objekt-Heap docs](https://learn.microsoft.com/en-us/dotnet/standard/garbage-collection/large-object-heap)
- [`dotnet-counters` Bezug](https://learn.microsoft.com/en-us/dotnet/core/diagnostics/dotnet-counters)Das Werkzeug, das die LOH in den ersten Platz tauchte
- [JetBrains dotMemory-Befehl„-"-Profiler](https://www.jetbrains.com/help/dotmemory/Command-Line_Profiler.html)Der CLI-Speicherprofiler für diese Nachbearbeitung
- [JetBrains dotTrace-Befehl„-"-Profiler](https://www.jetbrains.com/help/profiler/Performance_Profiling__Profiling_Using_the_Command_Line.html)Die gleiche Form für die Leistung des Zeitlinienprofils
- [sqlite---vss](https://github.com/asg017/sqlite-vss)Wenn und wenn die persistente Schicht Brute Kraft auswächst

Quelle für die Implementierung [github-.-com./-scottgal./-stylobot](https://github.com/scottgal/stylobot)Live-Engine, Dashboard und kommerzielle Steuerungen am [Stylobot-.-net](https://www.stylobot.net).