ஒரு பயனர் ஒரு டிக்கெட் திறக்கிறார்: மொபைல் ஆப் மிகவும் மெதுவாகச் செயல்படுகிறது. நீங்கள் உங்கள் டாஷ்போர்டுகளைச் சரிபார்க்கிறீர்கள். CPU பயன்பாடு சீராக உள்ளது. பிழை விகிதங்கள் (Error rates) பூஜ்ஜியத்தில் உள்ளன. உங்கள் APM விளக்குகள் நம்பிக்கையளிக்கும் வகையில் பச்சை நிறத்தில் உள்ளன. அனைத்துக் கண்ணுக்குத் தெரியும் அளவீடுகளின்படி, பேக்எண்ட் (backend) ஆரோக்கியமாக உள்ளது. ஆனால் பயனர் சொல்வது தவறு அல்ல. அந்தத் தாமதம் உண்மையானது, மேலும் அது ஒரு Android சாதனுக்கும் உங்கள் சர்வருக்கும் இடையிலான நீண்ட, நிழலான வழித்தடத்தில் எங்கோ நிகழ்கிறது.

பிரச்சனை என்னவென்றால், பெரும்பாலான observability கருவிகள் பயன்பாட்டு எல்லையில் (application boundary) நின்றுவிடுகின்றன. உங்கள் framework ஒரு கோரிக்கையை (request) பகுப்பாய்வு செய்த பிறகு என்ன நடக்கிறது என்பதை மட்டுமே அவை அளவிடுகின்றன. அவை தரவுத்தள வினவல்கள் (database queries), cache hits மற்றும் கீழ்நிலைச் சேவை அழைப்புகளை (downstream service calls) மட்டுமே கண்காணிக்கின்றன. அவை தவறவிடுவதென்னவென்றால், அந்த கோரிக்கையின் இயங்குமுறை (mechanics): ஒரு OkHttp அழைப்பு Android HTTP stack-க்குள் செலவிடும் நேரம், நிலையற்ற மொபைல் நெட்வொர்க்கில் கடந்து செல்லும் நேரம், TLS handshake பேச்சுவார்த்தை மற்றும் உங்கள் குறியீடு இயங்குவதற்கு முன்பே kernel queues-க்குள் நடக்கும் அமைதியான காத்திருப்பு ஆகியவற்றை அவை கவனிக்கத் தவறிவிடுகின்றன. உங்கள் APM அமைதியாக இருக்கும் அதே நேரத்தில், இந்த இடைவெளிகள் மில்லி விநாடிகள் அல்லது முழு விநாடிகளைக் கூட விழுங்கிவிடுகின்றன.

Encryption (குறியாக்கம்) இந்தத் தெரியாத நிலையை முழுமையாக்குகிறது. நவீன Android செயலிகள் அனைத்தையும் BoringSSL மூலம் வழிநடத்துகின்றன. ஒரு பாக்கெட் (packet) kernel network stack-ஐ அடையும் போது, HTTP தலைப்புகள் (headers) குறியாக்கம் செய்யப்பட்டிருக்கும். ஒரு சாதாரண tcpdump அல்லது network hook, தெளிவற்ற TLS records-ஐ மட்டுமே பார்க்கும். போக்குவரத்து நடப்பதை உங்களால் கவனிக்க முடியும், ஆனால் அதை உங்களால் படிக்க முடியாது. ஒரு குறிப்பிட்ட kernel-level TCP segment-ஐ ஒரு குறிப்பிட்ட பயனரின் API அழைப்போடு உங்களால் நிச்சயமாக இணைக்க முடியாது. உங்களுக்குத் தேவையான trace context அந்த ciphertext-க்குள் சிக்கியிருக்கிறது.

eBPF இந்தச் சூழலை மாற்றுகிறது, ஏனெனில் இது உங்கள் பயன்பாட்டு குறியீட்டை (application code) மாற்றாமல், அமைப்பை உள்ளிருந்து வெளியே நோக்கி instrument செய்ய அனுமதிக்கிறது. உங்கள் ஆப் தனது சொந்த தாமதத்தைத் (latency) தெரிவிக்கக் கேட்பதற்குப் பதிலாக, நீங்கள் நேரடியாக kernel மற்றும் முக்கியமான userspace நூலகங்களுடன் (libraries) சிறிய நிரல்களை இணைக்கிறீர்கள். இந்த நிரல்கள் நிகழ்வுகள் நடக்கும்போதே அவற்றைக் கவனித்து, உங்களுக்குத் தேவையானவற்றை எடுத்து, ஒரு ring buffer-க்கு அனுப்புகின்றன. ஒரு லேசான header தவிர உங்கள் Android APK-க்குள் எந்த SDK bloat-உம் இல்லை, மேலும் உங்கள் backend வகுப்புகளை (classes) மாற்றி எழுதும் எந்த instrumentation agent-உம் இல்லை.

நான்கு பகுதி அமைப்பு (The Four-Part Setup)

இந்தத் தரவுப் பாதையை (pipeline) உருவாக்குவதில் நான்கு வெவ்வேறு கண்காணிப்பு அடுக்குகள் உள்ளன.

1. traceparent anchor. Android சாதனத்தில், ஒவ்வொரு வெளிச்செல்லும் கோரிக்கைக்கும் (outbound request) ஒரு W3C traceparent header-ஐச் சேர்க்கும் OkHttp interceptor-ஐ நீங்கள் சேர்க்கிறீர்கள். மொபைல் பக்கத்தில் தேவைப்படும் ஒரே மாற்றம் இதுதான், மேலும் இது மிகக் குறைவானது. இந்த header குறியாக்கம் செய்யப்பட்ட payload-க்குள் உங்கள் backend வரை பயணிக்கிறது. இது HTTP அடுக்கிற்குள் இருப்பதால், TLS engine இறுதியில் வெளிப்படுத்தும் plaintext-க்குள் இது அப்படியே இருக்கும்.

2. TCP வருகை நேரம் (TCP arrival timing). Backend host-இல், நெட்வொர்க் இடைமுகத்துடன் (network interface) இணைக்கப்பட்ட eBPF Traffic Control hooks-ஐப் பயன்படுத்துகிறீர்கள். தனிப்பட்ட TCP segments வரும்போது இந்த நிரல்கள் இயங்குகின்றன. அவை விளிம்பிலேயே (edge) sequence numbers மற்றும் timestamps-களைப் பிடிக்கின்றன. உங்கள் பயன்பாடு ஒரு பைட்டைப் படிப்பதற்கு நீண்ட காலத்திற்கு முன்பே, பிட்கள் கம்பியிலிருந்து (wire) வெளியேறி உங்கள் இயந்திரத்திற்குள் எப்போது நுழைந்தன என்பதை இப்போது நீங்கள் துல்லியமாக அறியலாம்.

3. Decryption probes. உங்கள் backend, OpenSSL அல்லது BoringSSL ஆகியவற்றைப் பயன்படுத்தி TLS-ஐ முடிக்கிறது. இங்கேதான் கட்டமைப்பு சுவாரஸ்யமடைகிறது. uprobes—dynamic userspace probes—பயன்படுத்தி, TLS நூலகத்திற்குள் SSL_write மற்றும் SSL_read-உடன் நீங்கள் இணைகிறீர்கள். plaintext தரவு encryption engine வழியாகச் செல்லும் கணமே இந்தச் செயல்பாடுகள் இயங்குகின்றன. உங்கள் eBPF நிரல் அந்த decrypted buffer-ஐப் படித்து, traceparent header-க்காகத் தேடி, அதை எடுக்கிறது. இப்போது நீங்கள் எந்தச் சான்றிதழ்களையோ (certificates) அல்லது சாவிகளையோ (keys) ஒரு தனிப்பயன் கருவியில் கையாளாமலேயே, ஒரு மூல TCP flow மற்றும் ஒரு குறிப்பிட்ட மொபைல் கோரிக்கை ஆகியவற்றிற்கு இடையே நேரடி மேப்பிங்கை (mapping) kernel பெற்றுள்ளது.

4. Kernel queueing. தரவு குறியாக்க நீக்கம் செய்யப்பட்டு தயாரான பிறகும், உங்கள் பயன்பாடு அதை உடனடியாகப் பயன்படுத்தாமல் இருக்கலாம். socket buffers மற்றும் scheduling events-களைக் கையாளும் தொடர்புடைய kernel செயல்பாடுகளுடன் (functions) நீங்கள் kprobes-ஐ இணைக்கிறீர்கள். இது queueing latency-ஐ அளவிடுகிறது: உங்கள் செயல்முறை (process) CPU-க்காகப் போட்டியிடுவதால் அல்லது இன்னும் read()-ஐ அழைக்காததால், கோரிக்கைகள் kernel நிலைக்குள் காத்திருக்கும் நேரம் இதுவாகும்.

நான்கு சிக்னல் ஆதாரங்களும் நிகழ்வுகளை eBPF ring buffer-இல் எழுதுகின்றன. userspace-இல் இயங்கும் ஒரு sidecar process இந்த buffer-ஐக் காலி செய்து, traceparent ID மூலம் நிகழ்வுகளைத் தொடர்புபடுத்தி, ஒவ்வொரு கோரிக்கைக்கும் ஒற்றை, ஒருங்கிணைந்த காலவரிசையை (timeline) உருவாக்குகிறது. முன்பு தொடர்பில்லாத kernel noise-ஆக இருந்தவை இப்போது ஒரு கட்டமைக்கப்பட்ட trace-ஆக மாறுகின்றன.

முழுப் பாதையைப் படித்தல் (Reading the Full Path)

ஒன்றிணைக்கப்பட்ட வெளியீடு பொதுவாக ஒரு flame graph அல்லது தாமதத்தை நான்கு உறுதியான பகுதிகளாகப் பிரிக்கும் ஒரு கட்டமைக்கப்பட்ட span tree ஆகத் தரப்படுகிறது:

  • Network transit time: ஆண்ட்ராய்டு ரேடியோ கோரிக்கையின் கடைசி பைட்டை (byte) அனுப்பியதிலிருந்து, பேக்எண்ட் NIC அதைத் பெறும் வரையிலான கால அளவு. செல்லுலார் ஏற்ற இறக்கங்கள் (cellular volatility) இங்குதான் நிகழ்கின்றன.
  • TLS handshake duration: குறியாக்கம் செய்யப்பட்ட சுரங்கப்பாதையை (encrypted tunnel) பேச்சுவார்த்தை மூலம் அமைப்பதில் செலவிடப்படும் நேரம். நிலையற்ற நெட்வொர்க்குகளில், இது உண்மையான தரவுப் பரிமாற்றத்தை விடப் பல மடங்கு அதிகமாக இருக்கலாம்.
  • Kernel queueing latency: ஒரு பகுதி (segment) வந்தடைந்த பிறகு, ஆனால் யூசர்ஸ்பேஸ் (userspace) அதைத் பயன்படுத்துவதற்கு முன்பு, கர்னல் பஃபர்கள் (buffers) மற்றும் ஷெட்யூலர் வரிசைகளில் (scheduler queues) செலவிடப்படும் நேரம்.
  • Application processing time: உங்கள் பேக்எண்ட் பிரேம்வொர்க் மற்றும் பிசினஸ் லாஜிக் (business logic) உண்மையில் பயன்படுத்தும் பகுதி.

800ms கால அளவு கொண்ட ஒரு மொபைல் கோரிக்கை, உங்கள் JSON பார்சருக்குள் (JSON parser) வெறும் 40ms மட்டுமே செலவிடுவதைக் நீங்கள் கண்டறியலாம். மற்றொரு 200ms, தரவு இழப்புள்ள (lossy) இணைப்பில் தேங்கி நிற்கும் TLS ஹேண்ட்ஷேக்கில் காணாமல் போகிறது. மற்றுமொரு 300ms, அதிகப்படியான சுமை கொண்ட ஹோஸ்ட்டில் (oversubscribed host) கர்னல் பேக்லாக்கில் (kernel backlog) மறைந்துவிடுகிறது. உங்கள் APM வெறும் 40ms-ஐ மட்டுமே அறிக்கையிட்டது. கர்னல் அளவிலான தெளிவு (kernel-level visibility) இல்லையென்றால், நீங்கள் முற்றிலும் தவறான விஷயத்தை மேம்படுத்தியிருப்பீர்கள்.

உண்மையில் அளவிடக்கூடிய கூடுதல் சுமை (Overhead That Actually Scales)

பாரம்பரிய APM ஏஜெண்டுகள் (APM agents) உங்கள் மொழி ரன்டைமிற்குள் (language runtime) அழைப்புகளைத் தடுப்பதன் மூலம் (intercepting) அவற்றின் தெளிவைப் பெறுகின்றன. அவை மெத்தட்களை (methods) சுற்றியும், ஸ்பான் ஆப்ஜெக்ட்களை (span objects) ஒதுக்கியும், உங்கள் ப்ராசஸ் ஹீப்பிற்குள் (process heap) டெலிமெட்ரி தரவை வரிசைப்படுத்தவும் (serialize) செய்கின்றன. சுமை அதிகரிக்கும் போது, அந்த கூடுதல் சுமை (overhead) விரைவாகத் தொடர்கிறது. வரிசைப்படுத்தும் செலவுகள் (Serialization costs) உயர்கின்றன. Garbage collection அழுத்தம் அதிகரிக்கிறது. உங்கள் அப்ளிகேஷனின் சொந்த வளங்களைக் கொண்டே நீங்கள் இந்தத் தெளிவிற்காகச் செலவிடுகிறீர்கள்.

eBPF நிரல்கள் ஒரு கர்னல் விர்ச்சுவல் மெஷினுக்குள் (kernel virtual machine) இயங்குகின்றன மற்றும் JIT-தொகுக்கப்பட்ட (JIT-compiled) நேட்டிவ் மெஷின் இன்ஸ்ட்ரக்ஷன்களாக (native machine instructions) மாற்றப்படுகின்றன. அவை லோட் செய்யப்படுவதற்கு முன்பு ஒரு சரிபார்ப்பவர் (verifier) அவற்றின் பாதுகாப்பைச் சரிபார்க்கிறார். ஒவ்வொரு புரோபும் (probe) பாதையில் மைக்ரோசெகண்டுகளை மட்டுமே சேர்க்கிறது, மில்லிசெகண்டுகளை அல்ல. நிகழ்வுகளைப் பொருத்துவதற்கும் வரைபடங்களை உருவாக்குவதற்கும் (rendering graphs) தேவையான கடினமான வேலைகள், உங்கள் சேவையின் ஹாட் பாத்திற்கு (hot path) வெளியே, சைட்காரில் (sidecar) நடைபெறுகின்றன. நீங்கள் ஹீப் ஒதுக்கீடுகளை (heap allocations) அதிகரிக்கவில்லை. கோரிக்கை கையாளுதலுக்குள் (request handling) வரிசைப்படுத்தும் வரிகளை (serialization taxes) நீங்கள் சேர்க்கவில்லை. ஒரு வினாடிக்கு ஆயிரக்கணக்கான கோரிக்கைகளை அனுப்பும் சேவைகளுக்கு, இந்த வேறுபாடு முக்கியமானது.

இயக்குவதற்குத் தேவையானவை

இது ஒரு மந்திரத் தீர்வு (magic bullet) அல்ல, மேலும் நீங்கள் எளிதாக ஆன்/ஆஃப் செய்யக்கூடிய ஒரு மேலாண்மை செய்யப்பட்ட SaaS அல்ல. உங்கள் புரோப்கள் கர்னல் கட்டமைப்புகளைப் பாதுகாப்பாகத் தாண்டிச் செல்ல BTF வகைத் தகவல் உள்ளிட்ட நவீன eBPF ஆதரவுடன் கூடிய ஒரு பேக்எண்ட் கர்னல் உங்களுக்குத் தேவை. உங்கள் uprobes இலக்கு வைக்கக்கூடிய குறியீடுகளை (symbols) உங்கள் TLS லைப்ரரி வெளிப்படுத்த வேண்டும்; நீங்கள் ஒரு ஸ்டேட்டிகலி லிங்க்டு பைனரியை (statically linked binary) அல்லது மிகவும் மாற்றியமைக்கப்பட்ட OpenSSL பில்டைக் கொண்டு அனுப்பினால், அதைக் கணக்கில் கொள்ள வேண்டியிருக்கும். மொபைல் கிளையன்ட் மற்றும் TLS முனையப் புள்ளிக்கு (termination point) இடையில் உள்ள எந்தவொரு ப்ராக்ஸிகள் அல்லது எட்ஜ் கேட்வேக்களிலும் (edge gateways) உங்கள் traceparent ஹெடர் அப்படியே இருப்பதை நீங்கள் சரிபார்க்க வேண்டும்.

ஆனால், மூல காரணத்தைக் கண்டறிய முடியாத "மொபைல் மெதுவாக உள்ளது" என்ற டிக்கெட்டுகளால் சோர்வடைந்த குழுக்களுக்கு, இந்த கட்டமைப்பு சடங்கு ரீதியான யூகங்களுக்குப் பதிலாகத் தெளிவான சமிக்ஞைகளை (hard signal) வழங்குகிறது. நீங்கள் நெட்வொர்க் நிலையைப் பற்றி ஊகிப்பதை நிறுத்திவிட்டு, குறிப்பிட்ட குழாய்கள் (pipes) வழியாக குறிப்பிட்ட கோரிக்கைகளை அளவிடத் தொடங்குகிறீர்கள்.

உண்மையான முடிவு (The Real Takeaway)

உங்கள் பிரேம்வொக்கை அடையும் போது மாயமாகத் தெரியவரும் ஒரு மங்கலான ஓட்டமாக (opaque stream) குறியாக்கம் செய்யப்பட்ட மொபைல் டிராஃபிக்கை நீங்கள் கருத வேண்டிய அவசியமில்லை. ஒரு எளிய OkHttp ஹெடரை பேக்எண்டில் மூலோபாய ரீதியாக வைக்கப்பட்ட eBPF புரோப்களுடன் இணைப்பதன் மூலம், உங்கள் மொபைல் செயலியை மீண்டும் எழுதாமலும், பாரம்பரிய APM கோருவது போல உங்கள் பேக்எண்ட் குறியீட்டை மாற்றாமலும், ஒரு ஆண்ட்ராய்டு சாதனத்திலிருந்து TLS டிகிரிப்ஷன், கர்னல் வரிசைகள் மற்றும் உங்கள் அப்ளிகேஷன் லாஜிக் வரை ஒரு தனிப்பட்ட கோரிக்கையை உங்களால் பின்தொடர முடியும். இது வெறும் படிப்படியான முன்னேற்றம் மட்டுமல்ல. இது முழுமையான (end-to-end) கண்காணிப்புத் திறன்...