తెల్లవారుజామున మూడు గంటలకు మీ డేటాబేస్ CPU 100% కి చేరుకుంటుంది. ట్రాఫిక్ సాధారణంగా కనిపిస్తుంది. రిక్వెస్ట్ల సంఖ్య అసాధారణంగా ఎక్కువగా లేదు. అయినప్పటికీ, మీ ప్రైమరీ స్టోర్ (primary store) విఫలమవుతోంది, ఎందుకంటే ఆ రిక్వెస్ట్లన్నీ ఒకే సమయంలో సరిగ్గా ఒకే విషయాన్ని అడుగుతున్నాయి.
ఇదే 'థండరింగ్ హెర్డ్' (thundering herd) సమస్య.
ఒక కచేరీ (concert) తర్వాత స్టేడియంను ఊహించుకోండి. ఒక గంట వ్యవధిలో, అదే ఎగ్జిట్ డోర్ ద్వారా అందరూ సులభంగా బయటకు వెళ్ళవచ్చు. కానీ, మొత్తం గుంపు ఒకే పది సెకన్ల వ్యవధిలో ఆ ఒక్క డోర్ ద్వారా బయటకు వెళ్లాలని నిర్ణయించుకుంటే, ఆ డోర్ విరిగిపోదు. అది కేవలం ఆ కన్కరెన్సీని (concurrency) తట్టుకోలేకపోతుంది. ఇక్కడ సమస్య డోర్లో లేదు, ఆ తొక్కిసలాటలోనే ఉంది.
ఈ సమస్య ఎక్కడ ఏర్పడుతుంది
అనేక ప్రాసెస్లు ఒకే ఈవెంట్పై సింక్రొనైజ్ అయినప్పుడు ఈ విధానం కనిపిస్తుంది. దీనికి హానికరమైన ట్రాఫిక్ అవసరం లేదు. సాధారణ వ్యవస్థలే తమకు తాము ఇలా చేసుకుంటాయి.
క్యాచీ ఎక్స్పైరేషన్ (Cache expiration). ఒక ముఖ్యమైన (hot) క్యాచీ కీ గడువు ముగుస్తుంది. అది ప్రొడక్ట్ క్యాటలాగ్, ఫీచర్ ఫ్లాగ్ లేదా యూజర్ పర్మిషన్ల జాబితా కావచ్చు. వేలాది అప్లికేషన్ సర్వర్లు ఒకేసారి ఆ ఖాళీని గమనిస్తాయి. ప్రతి సర్వర్, మంచి ఉద్దేశంతో, తన స్వంత డేటాబేస్ కనెక్షన్ను తెరిచి, ఆ విలువను తిరిగి నిర్మించడానికి (rebuild) ఒకే భారీ క్వెరీని రన్ చేస్తుంది. ఒకే ఖరీదైన ప్రశ్నను వేల సార్లు సమాంతరంగా (in parallel) సమాధానం ఇవ్వడానికి డేటాబేస్ ఎప్పుడూ కాన్ఫిగర్ చేయబడలేదు. ఒక్క ఎక్స్పైర్డ్ కీ మొత్తం క్లస్టర్ను దెబ్బతీస్తుంది.
కనెక్షన్ పూల్ వేకప్స్ (Connection pool wakeups). కొన్ని ఆర్కిటెక్చర్లలో, అనేక వర్కర్ ప్రాసెస్లు పని కోసం వేచి చూస్తూ ఒకే కండిషన్ కోసం ఆగిపోతాయి. ఆ కండిషన్ నెరవేరినప్పుడు, ఆపరేటింగ్ సిస్టమ్ నిద్రపోతున్న ప్రతి వర్కర్ను నిద్రలేపుతుంది. కానీ కేవలం ఒక వర్కర్ మాత్రమే కనెక్షన్ను లేదా టాస్క్ను పొందుతుంది. మిగిలిన వారంతా నిద్రలేచి, తాము పోటీలో ఓడిపోయినట్లు తెలుసుకుని, మళ్ళీ నిద్రపోతారు. ఈ చక్రం కాంటెక్స్ట్ స్విచ్ల (context switches) వల్ల CPUని వృధా చేస్తుంది మరియు అసలైన ఉత్పాదక పనిని దెబ్బతీస్తుంది.
రీట్రై స్టార్మ్స్ (Retry storms). ఒక డౌన్స్ట్రీమ్ సర్వీస్ చిన్న అంతరాయానికి గురవుతుంది. ప్రతి క్లయింట్ టైమ్ అవుట్ (timeout) ను గమనించి, మళ్ళీ ప్రయత్నించే ముందు ఒక నిర్ణీత సమయం (ఉదాహరణకు సరిగ్గా ఒక సెకను) వేచి ఉంటుంది. ఆ సర్వీస్ మళ్ళీ కోలుకుని ట్రాఫిక్ను స్వీకరించినప్పుడు, ప్రతి క్లయింట్ ఒకే క్షణంలో దానిని చేరుతుంది. అప్పుడు ఆ సర్వీస్ మళ్ళీ విఫలమవుతుంది. ఈ చక్రం మళ్ళీ మళ్ళీ జరుగుతుంది.
క్రాన్ కొలిజన్స్ (Cron collisions). మీరు మెయింటెనెన్స్, రిపోర్ట్ జనరేషన్ లేదా క్యాచీ వార్మింగ్ వంటి పనులను ఇన్స్టెన్స్ల సమూహం అంతటా సరిగ్గా 00:00 UTC వద్ద రన్ అయ్యేలా షెడ్యూల్ చేస్తే, మీరు ఒక ట్రాఫిక్ స్పైక్ను సృష్టిస్తారు. ఈ లోడ్ ఊహించదగినదే అయినప్పటికీ, దాని సాంద్రత (concentration) ప్రమాదకరంగా ఉంటుంది.
రిక్వెస్ట్ కోలీసింగ్ (Request Coalescing)
క్యాచీ స్టాంపీడ్స్ (cache stampedes) కు అత్యంత ప్రభావవంతమైన పరిష్కారం ఏమిటంటే, ఒకే ప్రశ్నను ఒకటి కంటే ఎక్కువసార్లు సమాధానం ఇవ్వడం ఆపివేయడం. క్యాచీ మిస్ (cache miss) జరిగినప్పుడు, 2,500 థ్రెడ్లు ఒక్కొక్కటి డేటాబేస్ క్వెరీని రన్ చేయడం మీకు వద్దు. ఒక థ్రెడ్ క్వెరీని రన్ చేయాలి, మిగిలిన 2,499 థ్రెడ్లు ఆ ఫలితం కోసం వేచి ఉండాలి.
ఈ విధానాన్ని తరచుగా రిక్వెస్ట్ కోలీసింగ్ (request coalescing) అని పిలుస్తారు. Go భాషలో, golang.org/x/sync లోని singleflight ప్యాకేజీ దీనికి ఒక ప్రామాణిక అమలును (canonical implementation) అందిస్తుంది. ఒక నిర్దిష్ట కీ కోసం Doని పిలిచే మొదటి గోరూటీన్ (goroutine) పనిని ప్రారంభిస్తుంది. అదే కీతో పిలిచే తదుపరి కాల్స్ అదే Do కాల్ కోసం వేచి ఉంటాయి. పని పూర్తయినప్పుడు, వేచి ఉన్న వారందరికీ ఫలితం ఒకేసారి అందుతుంది. మీరు ఒకే ఒక క్వెరీని అమలు చేసి, 2,500 రిక్వెస్ట్లకు సేవలు అందించారు.
మీరు ప్రామిసెస్ (promises), ఫ్యూచర్స్ (futures) లేదా ఛానెల్స్ (channels) ఉపయోగించి ఇన్-మెమరీ కన్కరెంట్ మ్యాప్తో ఇలాంటి ప్రవర్తనను నిర్మించవచ్చు. ఇన్-ఫ్లైట్ (in-flight) రిక్వెస్ట్ను అటామిక్గా (atomically) తనిఖీ చేయడం మరియు నమోదు చేయడం ఇందులో కీలకం. ఒకవేళ రిక్వెస్ట్ విఫలమైతే, అందరికీ ఎర్రర్ వస్తుంది, ఇది సాధారణంగా సరైన పద్ధతి. ఒకవేళ అది విజయవంతమైతే, అందరికీ క్యాచీ చేయబడిన విలువ అందుతుంది మరియు తదుపరి రిక్వెస్ట్లు నేరుగా క్యాచీని చేరుతాయి. దీనివల్ల డేటాబేస్ లోడ్ భారీ స్పైక్ నుండి స్థిరంగా మారుతుంది.
ప్రాబబిలిస్టిక్ ఎర్లీ ఎక్స్పైరేషన్ (Probabilistic Early Expiration)
కొన్నిసార్లు మీరు స్టాంపీడ్ను నిర్వహించడం కంటే దానిని పూర్తిగా నివారించాలనుకుంటారు. ఇక్కడే XFetch వంటి అల్గారిథమ్ల ద్వారా అమలు చేయబడే ప్రాబబిలిస్టిక్ ఎర్లీ ఎక్స్పైరేషన్ (probabilistic early expiration) ఉపయోగపడుతుంది.
కఠినమైన ఎక్స్పైరేషన్ సమయానికి బదులుగా, ప్రతి క్యాచీ చేయబడిన విలువ ఒక సాఫ్ట్ ఎక్స్పైరేషన్ విండోను కలిగి ఉంటుంది. ఒక రిక్వెస్ట్ వచ్చినప్పుడు, అప్లికేషన్ ఒక రాండమ్ నంబర్ను రూపొందిస్తుంది మరియు ఒక సాధారణ ప్రాబబిలిటీ చెక్ను వర్తింపజేస్తుంది. ఒకవేళ అది 'అవును' అని చెబితే, ఆ ఒక్క రిక్వెస్ట్ క్యాచీని ముందుగానే రిఫ్రెష్ చేస్తుంది. లేకపోతే, ఆ రిక్వెస్ట్ కొంచెం పాతదైన (stale) విలువను అందిస్తుంది.
ప్రతి రిక్వెస్ట్ తన స్వంత పాచికలను వేయడం వల్ల, రిఫ్రెష్లు ఆ సాఫ్ట్ విండో అంతటా విస్తరిస్తాయి. ఒక క్లయింట్ కఠినమైన ఎక్స్పైరేషన్కు నలభై సెకన్ల ముందు రిఫ్రెష్ చేయవచ్చు, మరొకరు పన్నెండు సెకన్ల ముందు, మరియు మిగిలిన వారు అసలు చేయకపోవచ్చు. ఈ పని ఒకేసారి జరిగే స్పైక్ నుండి నెమ్మదిగా...
