लंबे समय से चलाने वाली सेवाएं धीरे-धीरे बेबंधी स्मृति की ओर झुकती हैं।
स्टिलियोबोट रिलेस शृंखला
- व्यवहार, पहचान नहीं: क्यों StyloBot ग्राहक व्यवहारिक रूप से मॉडल करता है
- व्यवहार-Aware ASP.NET UI: सर्वर-\ऐसे पता लगाने के परिणाम पर सतह दे दिया
- लंबे समय तक चलाने वाले नेट सेवाओं में असीमित विकास का पता लगाना और स्थिर करनाविश्वसनीयता अनुशासन
- व्यवहार-Aware TypeScript UI: Express , Fastify, और ब्राउज़र घटक
- साइडकार वास्तुकला: कैसे पता लगाने की इंजन गैर--.NET स्टाक से जुड़ता है
- तेजी से सीखनाअनुकूलन सीखने की प्रणाली
- उस चीज़ का परीक्षण जो सफल नहीं हो रही है: सत्यापन अनुशासन
- स्टिलियोएक्सट्रैक्ट - एक स्थानीय सीखने के HTML को मार्कडाउन रूपांतरक में: एचटीएमएल\→\मार्कडाउन परत जो डिटेक्टर के साथ जोड़ता है,\walker bug lucidVIEW पकड़ा\M SK3\ और कुत्तेfood लूप कि यह ईमानदार बना
काम किया गया उदाहरण है StyloBot सदिश परत, लेकिन अनुशासन यह नहीं है't StylobotMSC2specificM SK3 किसी भी लंबे समय से चलाया जा रहा है। -running |.NET सेवा जो ट्रैफिक से स्थिति संचित करता है अंततः एक वर्ग की बग को बढ़ाता है जिसे आप कर सकते हैं।
व्यवहार मॉडल में है व्यवहार, पहचान नहीं; ASP.NET सतह में व्यवहार-Aware ASP.NET UI; स्रोत पर github.comM SK1scottgal/stylobot.
लंबे समय तक चलाने वाली सेवाएं एकत्रित होती हैं।1 प्रत्येक कैश।2 प्रत्येक शिक्षण भंडार।3 प्रत्येक।4 हम केवल अंतिम एन अनुरोधों को बनाएँगे।6 बफर एक छोटा सा संचयक है।7 प्रत्येक अपने आप में ठीक दिखता है।8 एक साथ।9 किसी प्रक्रिया पर, जो एक सप्ताह से चल रही है।11 वे एक स्मृति आकार बनाते हैं कोई भी परीक्षण कभी पुनः उत्पन्न नहीं करता।12
आप इस द्वारा पकड़ जानबूझकर उसे ढूंढनाएक प्रक्रिया के बारे में , जो आकार के उभरने के लिए काफी लंबे समय से चल रहा है
खेल का पहला टुकड़ा यह है कि यह पंचांग प्रविष्टि है
हर कुछ रिलीज़्स, मैं सुविधाओं को जोड़ना बंद कर देता हूँ और सिर्फ वास्तविक ट्रैफिक के नीचे चल रहे सिस्टम पर देखता हूँ क्या इस में से कोई गलत दिखता है कोई विशिष्ट बग नहीं
नवीनतम समीक्षा इस पोस्ट के बारे में बग पकड़ा है: Mostlylucid.BotDetection.Demo प्रक्रिया में बैठी है एक 20 GB निवासी संश्लेषित परीक्षण ट्रैफिक के तहत सेट
नियम अपने metrics को पढ़ने के लिए कैलेन्डर पर एक बार-बार स्लॉट रखें
के लिए सही उपकरण " कुछ गलत है dotnet-countersयह मुफ़्त है
StyloBot डेमो पर कुछ कमांड बाद में
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
कि क्या पता करने के लिए पर्याप्त है ' बड़ा वस्तु ढेर GB में था और लगभग 100 MB / सेकण्ड | . | Something was allocating enormous objects continuously |, | and the Gen
एक बड़ी वस्तु ढेर सबसे आम आश्चर्यों में से एक है जब आप पहली बार एक लंबे समय के लिए प्रोफ़ाइल करता है।
वास्तविक सेवाओं में आकस्मिक LOH वृद्धि के सबसे बड़े स्रोत
| स्रोत | यह LOH पर क्यों समाप्त होता है | ||||
|---|---|---|---|---|---|
JsonSerializer.Serialize(obj) एक लौटता है string एक 100 MB ऑब्जेक्ट को एक एकल आबंटन में सभी 200 MB UTF स्ट्रिंग बन जाता है |
|||||
MemoryStream आप बिना सीमाओं के बढ़ने दें |
आंतरिक बफर बीस गुना कर देता है पिछले | 85 | KB और LOH पर रहता है | ||
byte[] buf = new byte[n] बड़े के लिए n |
प्रत्यक्ष LOH आवंटन | ||||
List<T> जो पिछले ~10 के संदर्भ से बढ़ता है - टाइप किए गए आइटमों को |
बैकिंग एरे क्रॉस | 85 | KB और LOH पर उतरता है | ||
string.Concat / StringBuilder बड़ी पाठ पर |
अंतिम ToString() एक एकल विशाल आवंटन है |
||||
XmlSerializer / DataContractSerializer बड़े आलेखों के |
JSON केस की तरह ही आकार है |
यदि आप केवल एक अंगूठे के नियम को याद रखें किसी भी कोड पथ का संदिग्ध होना जो एक एकल निकटवर्ती बड़ी वस्तु को एक टाइमर पर या अनुरोध के लिए उत्पन्न करता है कि '' एमएसके0 '' वह आकार है जो लंबे '' एम एसके1 '' चलाने वाली सेवाओं को नष्ट करता है & #44; और यह '' एम्सके3 '' वही आकार है जिसे हम '' एमएसएसके4 '' स्टिलियोबोट में ढूंढने के लिए तैयार हैं
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 >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
उपकरण जो इसके निदान में मदद करते हैं
dotnet-counters जीवंत संख्याओं के लिए (पर LOH गेज
dotnet-gcdump और dotnet-dump स्न्यापसॉट के लिए आप विजुअल स्टूडियो या PerfView में खोल सकते हैं
PerfViewComment एक स्टैक तक आबंटन को ट्रेस करने के लिए स्वयं
जेटब्रेन्स सीएलआई प्रोफ़ाइलर (मैं वास्तव में इस पुनर्कार्य के लिए प्रयोग किया था क्या ). अब उचित के रूप में पैकेजित | . | नेट वैश्विक उपकरणों |, | तो सिरless & #44; / | रिमोट & #39; / | आईसी रनटाइम प्रोफाइलिंग को कोई राइडर GUI नहीं की जरूरत है
JetBrains.dotMemory.GlobalToolsसंलग्न करें .dmw कार्यक्षेत्र बाद में देखने के लिए जो बनाए गए रूट LOH आबंटन रख रहे हैंJetBrains.dotTrace.GlobalToolsनमूना लेने के लिए एक ही आकार / ट्रेसिंग | / | समयरेखा डेटा |. | जब आप को एक कंटर की बजाय एक स्टैक की आवश्यकता होती है तो इसका उपयोग करें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
इस पुनर्कार्य के लिए श्रम विभाजन dotnet-counters मुझे बताया LOH समस्या थी
अगर आपको सचमुच इनकी ज़रूरत है तो बचने की गुफाएं हैं GCSettings.LargeObjectHeapCompactionMode = CompactOnce एक LOH संकुचन को विवश करता है RecyclableMemoryStream माइक्रोसॉफ्ट पूल बफर से पहले स्थान पर आबंटन से बचने के लिए।
तो जब भी आप एक लंबे समय तक चलाने वाली सेवा में स्थिर LOH वृद्धि देखते हैं, सवाल लगभग हमेशा एक ही है क्या ' बहुत बड़ी वस्तुओं को आबंटित करता है उत्तर का आकार (timer? अनुरोध ह्यान्डलरM SK2 सिरियलाइजर? बफर पूल?) आपको बताता है कि कोड में कहाँ देखना है
अपवाद प्रति भी महत्वपूर्ण था 4/sec के SqliteException लॉग में नहीं दिखाई देने के लिए पर्याप्त कम है कोई भी पढ़ता है, लेकिन आपको बताने के लिए काफी उच्च है एक कोड पथ चुपचाप पुनः प्रयास कर रहा है
नियम जब कुछ गलत दिखता है dotnet-counters अनुमान करने से पहले. संख्याएं खोज त्रिज्या को बड़े पैमाने पर संकुचित करेंगे
यह है जहाँ अनुशासन दिलचस्प हो जाता है। चिपकाना संक्षेप को प्रश्न करने के बजाय लक्षण
अगले कुछ सेक्शनों के लिए त्वरित vocab (
- सदिशफ्लोटों का एक स्थिर array
float[64]प्रत्येक स्लॉट अनुरोध की मापित संपत्ति है।float[].- भेक्टर समानतासामान्यतः : कोसाइन समानता दो सदिशों के बीच कोण की कोज्या
- **लगभग निकटतम पड़ोसी (ANN)**इस सदिश को देखते हुए : " मिलियनों में से सबसे नजदीक N को खोजने के लिए , तेजी से ". Brute-\force तुलना O है। सच्चा निकटतम मैच.
- एचएनएसडब्ल्यूउन एएनएन एल्गोरिथ्मों में से एक। स्थिर सदिशों की सेट. के लिए डिज़ाइन नहीं है | " | हर अनुरोध एक जोड़ता है और पुराने मिट जाते हैं
- Centroidएक समूह के वेक्टरों की औसत - एक सदिश जो क्लस्टर को संक्षेपित करता है
स्टिलोबोट के लिए LOH वृद्धि ने तीन वर्गों को जन्म दिया, जो संरचनात्मक रूप से समान थे एचएनएसडब्ल्यू (Hierarchical Navigable Small Worldमूल माल्कोव & यशुनिन कागजहस्ताक्षर पर समानता खोज के लिए प्रयोग किया जाता है
private readonly List<float[]> _graphVectors = new();
प्रत्येक एक सीखने वाले हैंडलर द्वारा दिया गया था कि सदस्यता LearningEventType.FullDetection, जो पर गोली चलाता है हर एचटीटीपी निवेदनEvery request added a vector
और हर पांच मिनट के लिए-,-एक स्वचालित सहेजने की समय-सीमा एक पूरे ग्राफ को JSON में क्रमबद्ध किया और यह डिस्क पर लिखा
private readonly TimeSpan AutoSaveInterval = TimeSpan.FromMinutes(5);
डेमो पैमाने पर signatures.vectors.json उत्पादन में intent.meta.json में 70 MB intent.vectors.json में 51 MB. JSON क्रमबद्धता मेमोरी में एक निकटवर्ती स्ट्रिंग बनाती है। यह स्ट्रिङ सीधे LOH तक जाता है।
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<float[]><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
अब एक cap जोड़ने के लिए आसान है MaxVectors = 10_000LRU निष्कासन
एचएनएसडब्ल्यू उत्कृष्ट प्रौद्योगिकी है और मैं इसे अन्य स्थानों पर जानबूझकर प्रयोग करता हूँQdrant के साथ Self-Hosted भेक्टर डाटाबेस, आरएजी हाइब्रिड खोज और अनुक्रमण, न्यूनतम व्यवहार्य ग्राफराग). पाइनकॉनCity name (optional, probably does not need a translation), वियत करना, पीजीवेक्टर, और क्वारंटCity name (optional, probably does not need a translation) सभी इसे आंतरिक रूप से उपयोग करते हैं प्रत्येक सक्रिय अंगुली छाप के लिए " हाल के व्यवहार सदिशों का एक छोटा विंडो बनाए रखें ताकि पता लगाना वर्तमान अनुरोध को पिछले व्यवहार से समान के साथ तुलना कर सकता है बोट अंगुलियों की छाप दोहराएँ (ताप रहे हैं | ); | मानव अंगुलियाँ नहीं |' | t | МSK3 |evict यथासंभव सरल प्रक्रम HNSW अनुक्रमणिका को एक विशेषता के रूप में सूचीबद्ध पोस्ट इस पोस्ट का अनुसरण है
वहाँ श्लेष्मा के लिए एक त्वरित परीक्षण है ' 1 यदि आप संरचना पर एक कठोर टोपी डालने की कल्पना कर रहे हैं , परिणाम अभी भी सेमेंटिक रूप में वह चीज़ है जिसे आप चाहते थे
नियम जब आप असीम विकास को देखते हैं-, हम इस संरचना को कैसे कैप करते हैं लेकिन यह हम क्या कर रहे हैं के लिए सही संरचना है पहला लक्षण छिपाता है
एक बार जब आप' गलत अमूर्ति का नाम दिया है, प्रतिस्थापन आमतौर पर स्वयं लिखता है
गर्म परत एक सीमांत BoundedVectorCache<TEntry>, एक पतली wrapper के आसपास ConcurrentDictionary अभिगम के साथ-frequency priority eviction
retentionScorer: (_, entry) => entry.WasBot ? 2.0 : 1.0
स्थायी परत (FOSS तीन नए SQLite तालिकाएँ (signature_centroids, session_centroids, intent_centroids) एक रात से संकुचित केंद्रक भंडारण VectorCompactionService. रेशे फ्लोट के रूप में भंडारित भेक्टर MemoryMarshal.AsBytes:
internal static byte[] PackFloats(float[] v) =>
MemoryMarshal.AsBytes(v.AsSpan()).ToArray();
कॉम्पैक्ट बाइनरी सिरियलाइजेशन. No 100+ MB JSON stringsM SK2 No LOH
भंडारण स्तर नोट SQLite FOSS है / एकल | - | द्विआधारी बैकएण्ड (backend) पीजीवेक्टरआपको सही अनुक्रमित समानता खोज देता है डाटाबेस के अंदर एक स्थिर कॉर्पोस पर - जहां एचएनएसडब्ल्यू का भाग होता है, , क्षैतिज पैमाने पर ), और फ्लीट के बीच साझा स्थिति में , समान वास्तुकला नियम sqlite-vss एक कदम है यदि आप SQLite बाहर बढ़ना चाहते हैं लेकिन एकल रहना चाहते हैं
FOSS स्थायी परत पर समानता खोज brute हैSystem.Numerics.Tensors.TensorPrimitivesसंकुचित पर एक पूर्ण स्कैन पीआई पर ~1-2 ms लेता है।
पर एक नोट संकुचनसे , कम्पैक्टेशन के बाद " इस अनुभाग में वास्तविक कार्य कर रहा है
यह एक ही नमूना है LSM-ट्री भंडारण इंजनों (RocksDBM SK2 LevelDB , CassandraMSC4 SSTables के लिए उपयोग के लिए: स्तर |0 हाल की अच्छी रखता है-ग्रीन डेटा | ; निचले स्तरों को संकुचित रखता हैं, संक्षेपित डेटा \M SK10 यह उधार लेने से काम करता है क्योंकि अभिगम नमूने एक ही है: अधिक से अधिक पठन हाल के डेटा पर प्रहार करते हैं; पठनों का एक छोटा हिस्सा लंबे पूंछ की आवश्यकता होती है\m; हर कच्चे पंक्ति को हमेशा रखने में कोई लाभ नहीं करता है\M.
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
सामान्य आकार (स्थिर स्थिति के लिए बांडित गर्म कैश + इतिहास के लिए कॉम्पैक्ट स्थायी भंडार |+ | इन दोनों के बीच एक आवधिक संकुचितकलंबे समय तक चल रही सीखने वाली प्रणाली में बार-बार दिखाई देता है। यह आपके उपकरण कीट में रखने के लिए अच्छा होता है। जब कोई एचएनएसवी या pgvector को कार्यभार के लिए पहुँचता है तो यह वास्तव में एक कैश है।
नियम सबसे सरल डेटा संरचना जो फिट करता है पसंद करें रनटाइम पैटर्न, यह एक नहीं है जो डेटासेट फिट करता है
एक सूक्ष्म है, क्योंकि यह अधिक के बजाय संयम के बारे में है
यदि धीमी पथ में एक डाटाबेस की खोज शामिल है, तो इस पर द्रुत पथ को ब्लॉक करने का प्रयास होता है।
if (!_cache.TryGet(signatureId, out var entry))
return null; // no signal this request - other 48 detectors still run
चूक का अर्थ होता है कोई समानता संकेत इस अनुरोधअन्य डिटेक्टर अभी भी चल रहे हैं. एक पृष्ठभूमि हैंडलर क्वेरी SQLite अनुरोध पूरा करने के बाद और अगले बार के लिए कैश को गर्म करता है
यह है जहाँ अधिकतर कैशिंग गलत हो जाता है
नियम अगर आप कर सकते हैं-'- एक गलती सहन नहीं करता है-,- आप नहीं करते-M SK2- एक कैश नहीं रखता है
एक बार जब आप एक असंबंधित संरचना मिला है
लेखापरीक्षायांत्रिक है क्या उसके आकार को बंधता है-, जो निर्णय करता है जब प्रविष्टियां बाहर निकलती हैं। यदि आप कर सकते हैं-'- एक वाक्य में सभी तीनों का उत्तर नहीं देता। अल्पावधि संकेत मॉडल: जो भी जमा होता है उसे नष्ट होना चाहिए
स्टिलियोबोट के लिए अधिकतर संग्राहक पहले से ही अच्छे थे
EphemeralPatternReputationCacheपृष्ठभूमि क्षय और LRU निष्कासन के साथ प्रविष्टियों में : हार्ड कैप Neutral → Suspect → ConfirmedBad स्थिति मशीन, और असमित हिस्ट्रेसिस में रहते हैं तेजी से सीखना)BehavioralPatternAnalyzerIMemoryCache per-identity limits M SK2 paths, 100 timingsDriftDetectionHandlerनमूनेSessionEscalationService: 35-minute TTL with timer-driven evictionHNSW वर्गों से परे ध्यान की आवश्यकता थी MarkovTracker._cohortBaselines. मार्कोव श्रृंखला ट्रैकर per-cohort baseline transition matrices को रखता है (separate from per-signature chainsM SK3 which already had LRU eviction at MaxTrackedSignatures). कोहट आधाररेखाएं |( |एक प्रति यातायात कोह्ट जैसे | " |डेटा सेंटर | МSK3 |नयाँ | एमएसके4 | या | म. एस. सी. 5 | आवासीय |एमएसके6 | वापसी | मीएसके7 | कोई निकालना नहीं था | . | स्थिरता | мsk9 | सबसे ठंडे कोहटे निकालने के लिए | ( | कुल संक्रमणों में कम | sk11 | जब शब्दकोश अधिक होता है SelfMaintenanceOptions.MarkovCohortSize.
नियम प्रत्येक एकलटन संग्रह तीन प्रश्नों का उत्तर देता है या यह
अगले विफल मोड के बाद "no bound" यह है कि इस हार्डवेयर के लिए गलत है MaxEntries = 10_000 तब तक ठीक है जब तक कि कोई आपके सेवा को Pi 4, या एक कन्टेनर के साथ चलाता है
StyloBot के लिए , प्रत्येक सीमा एक ही के नीचे उतरी SelfMaintenanceOptions ब्लाक इन appsettings.jsonडिफ़ॉल्ट एक मानक सर्वर के लिए काम करता है LowMemory स्थिर पूर्वनिर्धारित
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;
});
या appsettings.json पर्यावरण के लिए
{
"BotDetection": {
"SelfMaintenance": {
"SignatureCacheSize": 1000,
"SessionCacheSize": 500,
"IntentCacheSize": 300,
"CentroidRetentionDays": 14
}
}
}
गहरे बिन्दु, एक स्थान पर सेट किया गया एक बांड, एक बाण्ड के बारे में कुछ ऑपरेटरों को समझ सकता है const int घोषणाओं के बारे में कोई भी तर्क नहीं कर सकता है
नियम प्रत्येक सीमा ऑपरेटर अपने हार्डवेयर जीवन के लिए एक कॉन्फ़िग ब्लॉक में परिवर्तन करना चाहेगा
विशेष रूप से स्टिलियोबोट के लिए (FOSS निर्माण
अवयव | Component |---|---|---| हस्ताक्षर HNSW सूचकांक सत्र HNSW सूचकांक आशय HNSW सूचकांक | JSON स्वचालित सहेजने के बफर | मार्कव कोहर्ट आधाररेखाएं कुल भेक्टर परत 13+ GB LOH | मेगाबाइट |
पहचान मॉडल अपरिवर्तित है. क्या बदला है जहाँ समानता के प्रमाण मौजूद हैं और जब यह ''' तेज पथ पर प्रभाव डालने के लिए अनुमति देता है. Centroids survive restarts in SQLite
पीआई पर LowMemory पूर्वनिर्धारित के साथ 4 में एफएसओएस निर्माण गर्म होने के बाद MB RSS के नीचे बैठता है, अपरिभाषित रूप से. पैसे दिए गए पोस्टग्रिज़ के अंतर्गत चलाया जाता है।
एक कैप जोड़ने से वास्तुकला को बदले बिना स्मृति बाधित होती है।
लम्बे शिक्षण प्रणालियों में पुनरावर्तित पैटर्न
constसआकार को ठीक करें
स्टिलियोबोट जारी श्रृंखला
इस ब्लॉग में एचएनएसएस और भेक्टर खोज
M, ef_constructट्युनिंगarray_cosine_distance विपरीत array_cosine_similarity)बाह्य संदर्भ
dotnet-counters संदर्भ: उपकरण जो पहली बार LOH के ऊपर आ गया थाकार्यान्वयन के लिए स्रोत github.comM SK1scottgal/stylobot. लाइव इंजन stylobot.net.
© 2026 Scott Galloway — Unlicense — All content and source code on this site is free to use, copy, modify, and sell.