
నేడు, పెద్ద ఖరీదైన ప్రాజెక్టులలో మాత్రమే కాకుండా, ఎల్లప్పుడూ మరియు ప్రతిచోటా సేవల యొక్క అధిక లభ్యత అవసరం. "క్షమించండి, నిర్వహణ ప్రోగ్రెస్లో ఉంది" అనే సందేశంతో తాత్కాలికంగా అందుబాటులో లేని సైట్లు ఇప్పటికీ సర్వసాధారణం, కానీ సాధారణంగా చిరునవ్వును కలిగిస్తాయి. మేఘాలలో ఈ జీవితాన్ని జోడిద్దాం, అదనపు సర్వర్ను ఎప్పుడు ప్రారంభించాలో మీకు APIకి ఒక కాల్ మాత్రమే అవసరం మరియు మీరు "హార్డ్వేర్" ఆపరేషన్ గురించి ఆలోచించాల్సిన అవసరం లేదు. మరియు క్లస్టర్ టెక్నాలజీలు మరియు రిడెండెన్సీని ఉపయోగించి ఒక క్లిష్టమైన వ్యవస్థ ఎందుకు విశ్వసనీయంగా నిర్మించబడలేదనే దానికి ఎటువంటి సాకులు లేవు.
మా సేవల్లోని డేటాబేస్ల విశ్వసనీయతను నిర్ధారించడానికి మేము ఏ పరిష్కారాలను పరిగణించాము మరియు మేము ఏమి వచ్చామో మీకు తెలియజేస్తాము. అంతేకాకుండా సుదూర ముగింపులతో కూడిన డెమో.
అధిక లభ్యత ఆర్కిటెక్చర్లో వారసత్వం
వివిధ ఓపెన్సోర్స్ సిస్టమ్ల అభివృద్ధి సందర్భంలో ఇది మరింత మెరుగ్గా కనిపిస్తుంది. డిమాండ్ పెరిగినందున లెగసీ సొల్యూషన్స్ అధిక లభ్యత సాంకేతికతలను జోడించవలసి వచ్చింది. మరియు వారి నాణ్యత మారుతూ ఉంటుంది. తదుపరి తరం పరిష్కారాలు వాటి నిర్మాణం యొక్క ప్రధాన భాగంలో అధిక లభ్యతను ఉంచుతాయి. ఉదాహరణకు, మొంగోడిబి స్థానాలు క్లస్టరింగ్ను దాని ప్రాథమిక ఉపయోగ సందర్భం. క్లస్టర్ క్షితిజ సమాంతరంగా ఉంటుంది, ఇది ఈ DBMS యొక్క బలమైన పోటీ ప్రయోజనం.
PostgreSQLకి తిరిగి వెళ్దాం. ఇది పురాతనమైన ప్రసిద్ధ ఓపెన్సోర్స్ ప్రాజెక్ట్లలో ఒకటి, దీని మొదటి విడుదల గత శతాబ్దం 95వ సంవత్సరంలో జరిగింది. చాలా కాలం వరకు, ప్రాజెక్ట్ బృందం అధిక లభ్యతను సిస్టమ్ ద్వారా పరిష్కరించాల్సిన సమస్యగా పరిగణించలేదు. అందువల్ల, డేటా కాపీలను రూపొందించడానికి రెప్లికేషన్ టెక్నాలజీ 8.2లో వెర్షన్ 2006లో మాత్రమే అంతర్నిర్మితమైంది, అయితే ఇది ఫైల్-ఆధారితంగా (లాగ్ షిప్పింగ్) ఉంది. 2010లో, వెర్షన్ 9.0 స్ట్రీమింగ్ రెప్లికేషన్ను ప్రవేశపెట్టింది మరియు ఇది అనేక రకాల క్లస్టర్లను రూపొందించడానికి ఆధారం. వాస్తవానికి, ఎంటర్ప్రైజ్ SQL లేదా ఆధునిక NoSQL తర్వాత PostgreSQLతో పరిచయం ఉన్న వ్యక్తులకు ఇది చాలా ఆశ్చర్యం కలిగిస్తుంది - సంఘం నుండి వచ్చే ప్రామాణిక పరిష్కారం సింక్రోనస్ లేదా అసమకాలిక ప్రతిరూపణతో కూడిన మాస్టర్-రెప్లికా జత. అదే సమయంలో, స్టాక్లో, మాస్టర్ యొక్క స్విచ్ మానవీయంగా చేయబడుతుంది మరియు క్లయింట్లను మార్చే సమస్య కూడా స్వతంత్రంగా పరిష్కరించబడాలని ప్రతిపాదించబడింది.
మేము విశ్వసనీయమైన PostgreSQLని ఎలా రూపొందించాలని నిర్ణయించుకున్నాము మరియు దీని కోసం మేము ఎంచుకున్నది
అయినప్పటికీ, స్థిరమైన శ్రద్ధ అవసరం లేని లోపాలను తట్టుకునే పరిష్కారాన్ని రూపొందించడంలో సహాయపడే భారీ సంఖ్యలో ప్రాజెక్ట్లు మరియు సాధనాలు లేకుంటే PostgreSQL అంత ప్రజాదరణ పొంది ఉండేది కాదు. మేఘంలో (MCS) సింగిల్ PostgreSQL సర్వర్లు మరియు అసమకాలిక రెప్లికేషన్తో కూడిన మాస్టర్-రెప్లికా జతలు DBaaS ప్రారంభించినప్పటి నుండి అందుబాటులో ఉన్నాయి.
సహజంగానే, మేము ప్రతి ఒక్కరికీ జీవితాన్ని సులభతరం చేయాలని మరియు అత్యంత అందుబాటులో ఉన్న సేవలకు ప్రాతిపదికగా పనిచేసే PostgreSQL ఇన్స్టాలేషన్ను అందుబాటులో ఉంచాలని కోరుకుంటున్నాము, స్విచ్ చేయడానికి నిరంతరం పర్యవేక్షించడం మరియు మేల్కొలపడం అవసరం లేదు. ఈ విభాగంలో పాత నిరూపితమైన పరిష్కారాలు మరియు తాజా పరిణామాలను ఉపయోగించే కొత్త యుటిలిటీల తరం రెండూ ఉన్నాయి.
నేడు, అధిక లభ్యత సమస్య రిడెండెన్సీపై ఆధారపడి ఉండదు (అది చెప్పకుండానే ఉంటుంది), కానీ ఏకాభిప్రాయం-లీడర్ ఎన్నికల అల్గారిథం. చాలా తరచుగా, పెద్ద ప్రమాదాలు సర్వర్లు లేకపోవడం వల్ల కాదు, ఏకాభిప్రాయంతో సమస్యల కారణంగా సంభవిస్తాయి: కొత్త నాయకుడు ఎంపిక చేయబడలేదు, ఇద్దరు నాయకులు వేర్వేరు డేటా సెంటర్లలో కనిపించారు, మొదలైనవి. ఉదాహరణ - Github MySQL క్లస్టర్లో జరిగిన ప్రమాదం - వారు రాశారు .
ఈ విషయంలో గణిత ఆధారం చాలా తీవ్రమైనది. ఒక వైపు, ఉంది , ఇది HA పరిష్కారాలను నిర్మించే అవకాశాలపై సైద్ధాంతిక పరిమితులను విధిస్తుంది, మరోవైపు, ఏకాభిప్రాయాన్ని నిర్ణయించడానికి గణితశాస్త్రపరంగా నిరూపితమైన అల్గారిథమ్లు, и . దీని ఆధారంగా, చాలా ప్రజాదరణ పొందిన DCS (వికేంద్రీకృత ఏకాభిప్రాయ వ్యవస్థలు) ఉన్నాయి - జూకీపర్, etcd, కాన్సుల్. అందువల్ల, నిర్ణయాత్మక వ్యవస్థ స్వతంత్రంగా వ్రాసిన ఒక రకమైన అల్గోరిథంపై పనిచేస్తే, మీరు దానిని తీవ్ర హెచ్చరికతో వ్యవహరించాలి. భారీ సంఖ్యలో సిస్టమ్లను విశ్లేషించిన తర్వాత, మేము ప్రధానంగా Zalando చే అభివృద్ధి చేయబడిన ఓపెన్సోర్స్ సిస్టమ్ అయిన Patroniలో స్థిరపడ్డాము.
లిరికల్ డైగ్రెషన్గా, మేము మల్టీ-మాస్టర్ సొల్యూషన్లను కూడా పరిగణించామని నేను చెబుతాను, అంటే రికార్డింగ్ కోసం అడ్డంగా స్కేల్ చేయగల క్లస్టర్లు. అయితే, రెండు ప్రధాన కారణాల వల్ల వారు అటువంటి క్లస్టర్ను సృష్టించకూడదని నిర్ణయించుకున్నారు. మొదట, ఇటువంటి పరిష్కారాలు అధిక సంక్లిష్టతను కలిగి ఉంటాయి మరియు తదనుగుణంగా, మరింత దుర్బలత్వం కలిగి ఉంటాయి. అన్ని కేసులకు స్థిరమైన పరిష్కారం చేయడం కష్టం. రెండవది, ఈ సందర్భంలో PostgreSQL స్వచ్ఛంగా (స్థానికంగా) నిలిచిపోతుంది, కొన్ని విధులు అందుబాటులో ఉండవు మరియు కొన్ని అనువర్తనాలు ఆపరేషన్ సమయంలో దాచిన బగ్లను కలిగి ఉండవచ్చు.
పాత్రోని
కాబట్టి పాత్రోని ఎలా పని చేస్తుంది? డెవలపర్లు చక్రాన్ని తిరిగి ఆవిష్కరించలేదు మరియు నిరూపితమైన DCS పరిష్కారాలలో ఒకదానిని ప్రాతిపదికగా ఉపయోగించాలని ప్రతిపాదించారు. కాన్ఫిగరేషన్ల సమకాలీకరణ, నాయకుడిని ఎన్నుకోవడం మరియు కోరం వంటి అన్ని సమస్యలు అతనికి వదిలివేయబడతాయి. మేము దీని కోసం etcdని ఎంచుకున్నాము.
తరువాత, Patroni PostgreSQL మరియు రెప్లికేషన్ సెట్టింగ్లలోని అన్ని సెట్టింగ్ల యొక్క సరైన అప్లికేషన్తో పాటు స్విచ్ఓవర్ మరియు ఫెయిల్ఓవర్ (అంటే మాస్టర్ యొక్క రెగ్యులర్ మరియు ఎమర్జెన్సీ స్విచింగ్)పై ఆదేశాల అమలుతో వ్యవహరిస్తుంది. ప్రత్యేకంగా MCS క్లౌడ్లో, మీరు మాస్టర్ యొక్క క్లస్టర్, సింక్రోనస్ రెప్లికా మరియు ఒకటి లేదా అంతకంటే ఎక్కువ అసమకాలిక ప్రతిరూపాలను సృష్టించవచ్చు. సింక్రోనస్ ప్రతిరూపం యొక్క ఉనికి కనీసం 2 సర్వర్లలో డేటా యొక్క భద్రతను నిర్ధారిస్తుంది మరియు ఈ ప్రతిరూపమే ప్రధాన "మాస్టర్ అభ్యర్థి" అవుతుంది.
etcd ఒకే సర్వర్లలో అమలు చేయబడినందున, సరైన కోరం కోసం సిఫార్సు చేయబడిన సర్వర్ల సంఖ్య 3 లేదా 5. అటువంటి క్లస్టర్ చదవడానికి అడ్డంగా స్కేల్ చేస్తుంది (పైన వ్రాయడం కోసం నేను స్కేలింగ్ గురించి వ్రాసాను). ఏది ఏమైనప్పటికీ, అసమకాలిక ప్రతిరూపాలు ముఖ్యంగా అధిక లోడ్ల క్రింద ఆలస్యం అయ్యే అవకాశం ఉందని గుర్తుంచుకోండి.
అటువంటి రీడ్ రెప్లికాస్ (హాట్ స్టాండ్బై) ఉపయోగం రిపోర్టింగ్ లేదా అనలిటిక్స్ టాస్క్ల కోసం సమర్థించబడుతుంది మరియు మాస్టర్ సర్వర్పై లోడ్ నుండి ఉపశమనం పొందుతుంది.
మీరు అలాంటి క్లస్టర్ను మీరే చేయాలనుకుంటే, మీకు ఇది అవసరం:
- 3 లేదా అంతకంటే ఎక్కువ సర్వర్లను సిద్ధం చేయండి, వాటి మధ్య IP చిరునామా మరియు ఫైర్వాల్ నియమాలను కాన్ఫిగర్ చేయండి;
- etcd, Patroni, PostgreSQL సేవల కోసం ప్యాకేజీలను ఇన్స్టాల్ చేయండి;
- etcd క్లస్టర్ను కాన్ఫిగర్ చేయండి;
- PostgreSQLతో పని చేయడానికి పాట్రోని సేవను కాన్ఫిగర్ చేయండి.
అంటే, మొత్తంగా, మీరు డజను కాన్ఫిగరేషన్ ఫైల్లను సరిగ్గా కంపోజ్ చేయాలి మరియు ఎక్కడా పొరపాటు చేయకూడదు. దీని కోసం మీరు ఖచ్చితంగా Ansible వంటి కాన్ఫిగరేషన్ నిర్వహణ సాధనాన్ని ఉపయోగించాలి. అయినప్పటికీ, ఇప్పటికీ అత్యధికంగా అందుబాటులో ఉన్న TCP బాలన్సర్ లేదు. దీన్ని తయారు చేయడం ఒక ప్రత్యేక పని.
రెడీమేడ్ క్లస్టర్ అవసరం, కానీ వీటన్నింటితో టింకర్ చేయకూడదనుకునే వారి కోసం, మేము జీవితాన్ని సరళీకృతం చేయడానికి ప్రయత్నించాము మరియు మా క్లౌడ్లోని పాత్రోనిలో రెడీమేడ్ క్లస్టర్ను తయారు చేసాము, మీరు దీన్ని ఉచితంగా పరీక్షించవచ్చు. క్లస్టర్తో పాటు, మేము చేసాము:
- TCP బాలన్సర్; వేర్వేరు పోర్ట్లలో ఇది ఎల్లప్పుడూ ప్రస్తుత మాస్టర్, సింక్రోనస్ లేదా ఎసిన్క్రోనస్ రెప్లికాను వరుసగా సూచిస్తుంది;
- క్రియాశీల పాత్రోని మాస్టర్ను మార్చడానికి API.
వాటిని MCS క్లౌడ్ API మరియు వెబ్ కన్సోల్ ద్వారా యాక్సెస్ చేయవచ్చు.
డెమో
MCS క్లౌడ్లో PostgreSQL క్లస్టర్ సామర్థ్యాలను పరీక్షించడానికి, DBMSతో సమస్యల విషయంలో లైవ్ అప్లికేషన్ ఎలా ప్రవర్తిస్తుందో చూద్దాం.
కృత్రిమ ఈవెంట్లను లాగ్ చేసి స్క్రీన్పై రిపోర్ట్ చేసే అప్లికేషన్ కోసం కోడ్ క్రింద ఉంది. లోపాల విషయంలో, ఇది దీన్ని నివేదిస్తుంది మరియు మేము దానిని Ctrl + C కలయికతో ఆపే వరకు లూప్లో దాని పనిని కొనసాగిస్తుంది.
from __future__ import print_function
from datetime import datetime
from random import randint
from time import sleep
import psycopg2
def main():
try:
connection = psycopg2.connect(user = "admin",
password = "P@ssw0rd",
host = "89.208.87.38",
port = "5432",
database = "myproddb")
cursor = connection.cursor()
cursor.execute("SELECT version();")
record = cursor.fetchone()
print("Connection opened to", record[0])
cursor.execute(
"INSERT INTO log VALUES ({});".format(randint(1, 10000)))
connection.commit()
cursor.execute("SELECT COUNT(event_id) from log;")
record = cursor.fetchone()
print("Logged a value, overall count: {}".format(record[0]))
except Exception as error:
print ("Error while connecting to PostgreSQL", error)
finally:
if connection:
cursor.close()
connection.close()
print("Connection closed")
if __name__ == '__main__':
try:
while True:
try:
print(datetime.now())
main()
sleep(3)
except Exception as e:
print("Caught error:n", e)
sleep(1)
except KeyboardInterrupt:
print("exit")
అప్లికేషన్ పని చేయడానికి PostgreSQL అవసరం. APIని ఉపయోగించి MCS క్లౌడ్లో క్లస్టర్ని క్రియేట్ చేద్దాం. సాధారణ టెర్మినల్లో, OS_TOKEN వేరియబుల్ APIని యాక్సెస్ చేయడానికి టోకెన్ను కలిగి ఉంటుంది (ఓపెన్స్టాక్ టోకెన్ ఇష్యూ కమాండ్తో పొందవచ్చు), మేము ఈ క్రింది ఆదేశాలను టైప్ చేస్తాము:
క్లస్టర్ను సృష్టించండి:
cat <<EОF > pgc10.json
{"cluster":{"name":"postgres10","allow_remote_access":true,"datastore":{"type":"postgresql","version":"10"},"databases":[{"name":"myproddb"}],"users":[{"databases":[{"name":"myproddb"}],"name":"admin","password":"P@ssw0rd"}],"instances":[{"key_name":"shared","availability_zone":"DP1","flavorRef":"d659fa16-c7fb-42cf-8a5e-9bcbe80a7538","nics":[{"net-id":"b91eafed-12b1-4a46-b000-3984c7e01599"}],"volume":{"size":50,"type":"DP1"}},{"key_name":"shared","availability_zone":"DP1","flavorRef":"d659fa16-c7fb-42cf-8a5e-9bcbe80a7538","nics":[{"net-id":"b91eafed-12b1-4a46-b000-3984c7e01599"}],"volume":{"size":50,"type":"DP1"}},{"key_name":"shared","availability_zone":"DP1","flavorRef":"d659fa16-c7fb-42cf-8a5e-9bcbe80a7538","nics":[{"net-id":"b91eafed-12b1-4a46-b000-3984c7e01599"}],"volume":{"size":50,"type":"DP1"}}]}}
EOF
curl -s -H "X-Auth-Token: $OS_TOKEN"
-H 'Accept: application/json'
-H 'Content-Type: application/json'
-d @pgc10.json https://infra.mail.ru:8779/v1.0/ce2a41bbd1434013b85bdf0ba07c770f/clusters

క్లస్టర్ యాక్టివ్ స్థితికి మారినప్పుడు, అన్ని ఫీల్డ్లు ప్రస్తుత విలువలను అందుకుంటాయి - క్లస్టర్ సిద్ధంగా ఉంది.
GUIలో:

పట్టికను కనెక్ట్ చేయడానికి మరియు సృష్టించడానికి ప్రయత్నిద్దాం:
psql -h 89.208.87.38 -U admin -d myproddb
Password for user admin:
psql (11.1, server 10.7)
Type "help" for help.
myproddb=> CREATE TABLE log (event_id integer NOT NULL);
CREATE TABLE
myproddb=> INSERT INTO log VALUES (1),(2),(3);
INSERT 0 3
myproddb=> SELECT * FROM log;
event_id
----------
1
2
3
(3 rows)
myproddb=>

అప్లికేషన్లో మేము PostgreSQLకి కనెక్ట్ చేయడానికి ప్రస్తుత సెట్టింగ్లను సూచిస్తాము. మేము TCP బాలన్సర్ యొక్క చిరునామాను నిర్దేశిస్తాము, తద్వారా మాస్టర్ చిరునామాకు మానవీయంగా మారవలసిన అవసరాన్ని తొలగిస్తాము. దాన్ని లాంచ్ చేద్దాం. మీరు చూడగలిగినట్లుగా, ఈవెంట్లు విజయవంతంగా డేటాబేస్లోకి లాగిన్ అయ్యాయి.

షెడ్యూల్ చేయబడిన మాస్టర్ స్విచ్ ఓవర్
ఇప్పుడు షెడ్యూల్ చేయబడిన మాస్టర్ స్విచ్ సమయంలో మా అప్లికేషన్ యొక్క ఆపరేషన్ని పరీక్షిద్దాం:

మేము అప్లికేషన్ను పర్యవేక్షిస్తున్నాము. అప్లికేషన్ నిజంగా అంతరాయం కలిగిందని మేము చూస్తున్నాము, అయితే దీనికి కొన్ని సెకన్లు మాత్రమే పడుతుంది, ఈ ప్రత్యేక సందర్భంలో, గరిష్టంగా 9.

కారు ప్రమాదం
ఇప్పుడు వర్చువల్ మెషీన్, ప్రస్తుత మాస్టర్ క్రాష్ని అనుకరించడానికి ప్రయత్నిద్దాం. మీరు హారిజోన్ ఇంటర్ఫేస్ ద్వారా వర్చువల్ మెషీన్ను ఆఫ్ చేయవచ్చు, కానీ ఇది సాధారణ షట్డౌన్ అవుతుంది. అటువంటి స్విచ్ పాత్రోనితో సహా అన్ని సేవల ద్వారా ప్రాసెస్ చేయబడుతుంది.
మాకు ఊహించలేని షట్డౌన్ అవసరం. అందువల్ల, పరీక్ష ప్రయోజనాల కోసం వర్చువల్ మెషీన్ను - ప్రస్తుత మాస్టర్ని - ప్రామాణికం కాని పద్ధతిలో మూసివేయమని నేను మా నిర్వాహకులను కోరాను.

అదే సమయంలో, మా అప్లికేషన్ పని చేస్తూనే ఉంది. సహజంగానే, మాస్టర్ యొక్క అటువంటి అత్యవసర మార్పిడి గుర్తించబడదు.
2019-03-29 10:45:56.071234
Connection opened to PostgreSQL 10.7 on x86_64-pc-linux-gnu, compiled by gcc (GCC) 4.8.5 20150623 (Red Hat 4.8.5-36), 64-bit
Logged a value, overall count: 453
Connection closed
2019-03-29 10:45:59.205463
Connection opened to PostgreSQL 10.7 on x86_64-pc-linux-gnu, compiled by gcc (GCC) 4.8.5 20150623 (Red Hat 4.8.5-36), 64-bit
Logged a value, overall count: 454
Connection closed
2019-03-29 10:46:02.661440
Error while connecting to PostgreSQL server closed the connection unexpectedly
This probably means the server terminated abnormally
before or while processing the request.
Caught error:
local variable 'connection' referenced before assignment
……………………………………………………….. - здесь какое-то количество ошибок
2019-03-29 10:46:30.930445
Error while connecting to PostgreSQL server closed the connection unexpectedly
This probably means the server terminated abnormally
before or while processing the request.
Caught error:
local variable 'connection' referenced before assignment
2019-03-29 10:46:31.954399
Connection opened to PostgreSQL 10.7 on x86_64-pc-linux-gnu, compiled by gcc (GCC) 4.8.5 20150623 (Red Hat 4.8.5-36), 64-bit
Logged a value, overall count: 455
Connection closed
2019-03-29 10:46:35.409800
Connection opened to PostgreSQL 10.7 on x86_64-pc-linux-gnu, compiled by gcc (GCC) 4.8.5 20150623 (Red Hat 4.8.5-36), 64-bit
Logged a value, overall count: 456
Connection closed
^Cexit
మీరు చూడగలిగినట్లుగా, అప్లికేషన్ 30 సెకన్లలోపు దాని పనిని కొనసాగించగలిగింది. అవును, నిర్దిష్ట సంఖ్యలో సేవా వినియోగదారులకు సమస్యలను గమనించడానికి సమయం ఉంటుంది. అయితే, ఇది తీవ్రమైన సర్వర్ వైఫల్యం; ఇది చాలా తరచుగా జరగదు. ఈ సందర్భంలో, ఒక వ్యక్తి (నిర్వాహకుడు) స్విచ్చింగ్ స్క్రిప్ట్తో సిద్ధంగా ఉన్న కన్సోల్లో కూర్చుంటే తప్ప, అంత త్వరగా స్పందించడానికి అతనికి సమయం ఉండదు.
తీర్మానం
అటువంటి క్లస్టర్ నిర్వాహకులకు అద్భుతమైన ప్రయోజనాన్ని అందిస్తుంది అని నాకు అనిపిస్తోంది. వాస్తవానికి, డేటాబేస్ సర్వర్ల యొక్క తీవ్రమైన విచ్ఛిన్నాలు మరియు వైఫల్యాలు అప్లికేషన్కు మరియు తదనుగుణంగా వినియోగదారుకు గుర్తించబడవు. మీరు తొందరపడి దేన్నీ పరిష్కరించాల్సిన అవసరం లేదు మరియు తాత్కాలిక కాన్ఫిగరేషన్లు, సర్వర్లు మొదలైన వాటికి మారండి. మరియు అటువంటి పరిష్కారాన్ని క్లౌడ్లో రెడీమేడ్ సేవగా ఉపయోగించినట్లయితే, మీరు దానిని సిద్ధం చేయడానికి సమయాన్ని వృథా చేయవలసిన అవసరం లేదు. మరింత ఆసక్తికరంగా ఏదైనా చేయడం సాధ్యమవుతుంది.
మూలం: www.habr.com
