ක්ෂුද්‍ර සේවා ගෘහ නිර්මාණ ශිල්පයේ මෙහෙයුම් විශ්ලේෂණ: උදව් සහ ඉක්මන් Postgres FDW

ක්ෂුද්‍ර සේවා ගෘහ නිර්මාණ ශිල්පය, මේ ලෝකයේ සෑම දෙයක්ම මෙන්, එහි වාසි සහ අවාසි ඇත. සමහර ක්‍රියාවලීන් එය සමඟ පහසු වන අතර අනෙක් ඒවා වඩාත් අපහසු වේ. වෙනස් වීමේ වේගය සහ වඩා හොඳ පරිමාණය සඳහා, ඔබ කැපකිරීම් කළ යුතුය. ඒවායින් එකක් වන්නේ විශ්ලේෂණවල වැඩිවන සංකීර්ණත්වයයි. මොනොලිත් එකක සියලුම මෙහෙයුම් විශ්ලේෂණ SQL විමසුම් දක්වා විශ්ලේෂණ අනුරුවක් දක්වා අඩු කළ හැකි නම්, බහු සේවා ගෘහ නිර්මාණ ශිල්පය තුළ සෑම සේවාවකටම තමන්ගේම දත්ත සමුදායක් ඇති අතර එක් විමසුමක් කළ නොහැකි බව පෙනේ (නැතහොත් එය කළ හැකිද?). අපගේ සමාගමෙහි මෙහෙයුම් විශ්ලේෂණ ගැටළුව විසඳූ ආකාරය සහ මෙම විසඳුම සමඟ ජීවත් වීමට අප ඉගෙන ගත් ආකාරය ගැන උනන්දුවක් දක්වන අය සඳහා - සාදරයෙන් පිළිගනිමු.

ක්ෂුද්‍ර සේවා ගෘහ නිර්මාණ ශිල්පයේ මෙහෙයුම් විශ්ලේෂණ: උදව් සහ ඉක්මන් Postgres FDW
මගේ නම Pavel Sivash, DomClick හි මම විශ්ලේෂණාත්මක දත්ත ගබඩාව නඩත්තු කිරීම සඳහා වගකිව යුතු කණ්ඩායමක වැඩ කරමි. සාම්ප්‍රදායිකව, අපගේ ක්‍රියාකාරකම් දත්ත ඉංජිනේරු විද්‍යාව ලෙස වර්ග කළ හැකි නමුත්, ඇත්ත වශයෙන්ම, කාර්යයන් පරාසය වඩා පුළුල් ය. දත්ත ඉංජිනේරු විද්‍යාව සඳහා ETL/ELT ප්‍රමිතියක් ඇත, දත්ත විශ්ලේෂණය සහ ඔබේම මෙවලම් සංවර්ධනය සඳහා මෙවලම් සඳහා සහාය සහ අනුවර්තනය. විශේෂයෙන්ම, මෙහෙයුම් වාර්තාකරණය සඳහා, අපි ඒකලිතයක් ඇති බව "මවා පෙන්වීමට" තීරණය කළ අතර විශ්ලේෂකයින්ට අවශ්ය සියලු දත්ත අඩංගු වන එක් දත්ත සමුදායක් ලබා දෙන්නෙමු.

පොදුවේ, අපි විවිධ විකල්ප සලකා බැලුවා. අංගසම්පූර්ණ ගබඩාවක් තැනීමට හැකි විය - අපි පවා උත්සාහ කළෙමු, නමුත්, අවංකව කිවහොත්, ගබඩාවක් තැනීමේ සහ එහි වෙනස්කම් කිරීමේ තරමක් මන්දගාමී ක්‍රියාවලිය සමඟ තර්කනයේ තරමක් නිතර සිදුවන වෙනස්කම් ඒකාබද්ධ කිරීමට අපට නොහැකි විය (යමෙකු සාර්ථක වුවහොත් , කෙසේද යන්න අදහස් දැක්වීමේදී ලියන්න). විශ්ලේෂකයින්ට පැවසීමට හැකි විය: “යාලුවනේ, පයිතන් ඉගෙන ගෙන විශ්ලේෂණ අනුරූ වෙත යන්න,” නමුත් මෙය බඳවා ගැනීම සඳහා අමතර අවශ්‍යතාවයක් වන අතර හැකි නම් මෙය වළක්වා ගත යුතු බව පෙනෙන්නට තිබුණි. FDW (Foreign Data Wrapper) තාක්‍ෂණය භාවිතා කිරීමට උත්සාහ කිරීමට අපි තීරණය කළෙමු: මූලික වශයෙන්, මෙය සම්මත dblink එකක් වන අතර එය SQL ප්‍රමිතියේ ඇත, නමුත් එයටම වඩා පහසු අතුරු මුහුණතක් ඇත. එය මත පදනම්ව, අපි විසඳුමක් සාදා, එය අවසානයේ අල්ලා, අපි එය මත පදිංචි විය. මට බොහෝ දේ ගැන කතා කිරීමට අවශ්‍ය බැවින් එහි විස්තර වෙනම ලිපියක මාතෘකාව වන අතර සමහර විට එකකට වඩා වැඩි ය: දත්ත සමුදා ක්‍රම සමමුහුර්ත කිරීමේ සිට පුද්ගලික දත්ත පාලනය කිරීම සහ පුද්ගලීකරණය කිරීම දක්වා. මෙම විසඳුම සැබෑ විශ්ලේෂණාත්මක දත්ත සමුදායන් සහ ගබඩා සඳහා ආදේශකයක් නොවන බව වෙන්කරවා ගැනීම ද අවශ්‍ය වේ; එය විසඳන්නේ විශේෂිත ගැටළුවක් පමණි.

ඉහළ මට්ටමේ එය මේ ආකාරයෙන් පෙනේ:

ක්ෂුද්‍ර සේවා ගෘහ නිර්මාණ ශිල්පයේ මෙහෙයුම් විශ්ලේෂණ: උදව් සහ ඉක්මන් Postgres FDW
පරිශීලකයින්ට ඔවුන්ගේ වැඩ දත්ත ගබඩා කළ හැකි PostgreSQL දත්ත ගබඩාවක් ඇත, සහ වඩාත්ම වැදගත් වන්නේ, සියලුම සේවාවන්හි විශ්ලේෂණාත්මක අනුරූ මෙම දත්ත සමුදායට FDW හරහා සම්බන්ධ කිරීමයි. මෙය දත්ත සමුදා කිහිපයකට විමසුමක් ලිවීමට හැකි වන අතර එය කුමක් වුවත් එය වැදගත් නොවේ: PostgreSQL, MySQL, MongoDB හෝ වෙනත් දෙයක් (ගොනුව, API, හදිසියේ සුදුසු දවටනයක් නොමැති නම්, ඔබට ඔබේම ලිවිය හැකිය). හොඳයි, සෑම දෙයක්ම විශිෂ්ටයි! අපි වෙන් වෙනවාද?

සෑම දෙයක්ම ඉතා ඉක්මනින් හා සරලව අවසන් වූවා නම්, බොහෝ විට, ලිපියක් නොතිබෙනු ඇත.

Postgres දුරස්ථ සේවාදායකයන් වෙත ඉල්ලීම් සකසන ආකාරය පිළිබඳව පැහැදිලි වීම වැදගත් වේ. මෙය තාර්කික බවක් පෙනේ, නමුත් බොහෝ විට මිනිසුන් ඒ ගැන අවධානය යොමු නොකරයි: Postgres විසින් ඉල්ලීම දුරස්ථ සේවාදායකයන් මත ස්වාධීනව ක්‍රියාත්මක වන කොටස් වලට බෙදා, මෙම දත්ත එකතු කර, අවසාන ගණනය කිරීම් සිදු කරයි, එබැවින් විමසුම් ක්‍රියාත්මක කිරීමේ වේගය බොහෝ සෙයින් රඳා පවතී. එය ලියා ඇති ආකාරය. එය ද සටහන් කළ යුතුය: දුරස්ථ සේවාදායකයකින් දත්ත පැමිණෙන විට, එහි තවදුරටත් දර්ශක නොමැත, උපලේඛකයාට උපකාර වන කිසිවක් නැත, එබැවින් ඔහුට උදව් කිරීමට සහ උපදෙස් දිය හැක්කේ අපට පමණි. තවද මට වඩාත් විස්තරාත්මකව කතා කිරීමට අවශ්‍ය වන්නේ මෙයයි.

සරල විමසුමක් සහ එය සමඟ සැලැස්මක්

Postgres විසින් දුරස්ථ සේවාදායකයක මිලියන 6ක පේළි වගුවක් විමසන ආකාරය පෙන්වීමට, අපි සරල සැලැස්මක් දෙස බලමු.

explain analyze verbose  
SELECT count(1)
FROM fdw_schema.table;

Aggregate  (cost=418383.23..418383.24 rows=1 width=8) (actual time=3857.198..3857.198 rows=1 loops=1)
  Output: count(1)
  ->  Foreign Scan on fdw_schema."table"  (cost=100.00..402376.14 rows=6402838 width=0) (actual time=4.874..3256.511 rows=6406868 loops=1)
        Output: "table".id, "table".is_active, "table".meta, "table".created_dt
        Remote SQL: SELECT NULL FROM fdw_schema.table
Planning time: 0.986 ms
Execution time: 3857.436 ms

VERBOSE ප්‍රකාශය භාවිතයෙන් දුරස්ථ සේවාදායකය වෙත යවනු ලබන විමසුම සහ වැඩිදුර සැකසීම සඳහා අපට ලැබෙන ප්‍රතිඵල (RemoteSQL රේඛාව) බැලීමට අපට ඉඩ සලසයි.

අපි තව ටිකක් ඉදිරියට ගොස් අපගේ ඉල්ලීමට පෙරහන් කිහිපයක් එකතු කරමු: එකක් සඳහා බූලියන් ක්ෂේත්‍රය, සිදුවීමෙන් එකක් කාල මුද්රාව පරතරය තුළ සහ එකකින් jsonb.

explain analyze verbose
SELECT count(1)
FROM fdw_schema.table 
WHERE is_active is True
AND created_dt BETWEEN CURRENT_DATE - INTERVAL '7 month' 
AND CURRENT_DATE - INTERVAL '6 month'
AND meta->>'source' = 'test';

Aggregate  (cost=577487.69..577487.70 rows=1 width=8) (actual time=27473.818..25473.819 rows=1 loops=1)
  Output: count(1)
  ->  Foreign Scan on fdw_schema."table"  (cost=100.00..577469.21 rows=7390 width=0) (actual time=31.369..25372.466 rows=1360025 loops=1)
        Output: "table".id, "table".is_active, "table".meta, "table".created_dt
        Filter: (("table".is_active IS TRUE) AND (("table".meta ->> 'source'::text) = 'test'::text) AND ("table".created_dt >= (('now'::cstring)::date - '7 mons'::interval)) AND ("table".created_dt <= ((('now'::cstring)::date)::timestamp with time zone - '6 mons'::interval)))
        Rows Removed by Filter: 5046843
        Remote SQL: SELECT created_dt, is_active, meta FROM fdw_schema.table
Planning time: 0.665 ms
Execution time: 27474.118 ms

විමසුම් ලිවීමේදී ඔබ අවධානය යොමු කළ යුතු කරුණ වන්නේ මෙයයි. පෙරහන් දුරස්ථ සේවාදායකය වෙත මාරු කර නැත, එයින් අදහස් වන්නේ එය ක්‍රියාත්මක කිරීම සඳහා, පෝස්ට්ග්‍රෙස් විසින් පේළි මිලියන 6ම ඉවත් කර පසුව දේශීයව පෙරීම (පෙරහන පේළිය) සහ එකතු කිරීම සිදු කිරීමයි. සාර්ථකත්වය සඳහා යතුර වන්නේ විමසුමක් ලිවීමයි, එවිට පෙරහන් දුරස්ථ යන්ත්‍රයට මාරු කරනු ලැබේ, අපි අවශ්‍ය පේළි පමණක් ලබාගෙන එකතු කරමු.

ඒක බූලියන් ෂිට් එකක්

බූලියන් ක්ෂේත්ර සමඟ සෑම දෙයක්ම සරලයි. මුල් ඉල්ලීමේ දී, ගැටළුව ක්‍රියාකරු නිසා විය is. ඔබ එය ප්රතිස්ථාපනය කරන්නේ නම් =, එවිට අපි පහත ප්රතිඵලය ලබා ගනිමු:

explain analyze verbose
SELECT count(1)
FROM fdw_schema.table
WHERE is_active = True
AND created_dt BETWEEN CURRENT_DATE - INTERVAL '7 month' 
AND CURRENT_DATE - INTERVAL '6 month'
AND meta->>'source' = 'test';

Aggregate  (cost=508010.14..508010.15 rows=1 width=8) (actual time=19064.314..19064.314 rows=1 loops=1)
  Output: count(1)
  ->  Foreign Scan on fdw_schema."table"  (cost=100.00..507988.44 rows=8679 width=0) (actual time=33.035..18951.278 rows=1360025 loops=1)
        Output: "table".id, "table".is_active, "table".meta, "table".created_dt
        Filter: ((("table".meta ->> 'source'::text) = 'test'::text) AND ("table".created_dt >= (('now'::cstring)::date - '7 mons'::interval)) AND ("table".created_dt <= ((('now'::cstring)::date)::timestamp with time zone - '6 mons'::interval)))
        Rows Removed by Filter: 3567989
        Remote SQL: SELECT created_dt, meta FROM fdw_schema.table WHERE (is_active)
Planning time: 0.834 ms
Execution time: 19064.534 ms

ඔබට පෙනෙන පරිදි, පෙරහන දුරස්ථ සේවාදායකයකට පියාසර කළ අතර, ක්රියාත්මක කිරීමේ කාලය තත්පර 27 සිට 19 දක්වා අඩු විය.

ක්රියාකරු බව සඳහන් කිරීම වටී is ක්රියාකරුට වඩා වෙනස් = මක්නිසාද යත් එය Null අගය සමඟ වැඩ කළ හැකි බැවිනි. එහි තේරුම එයයි සැබෑ නොවේ ෆිල්ටරයේ False සහ Null අගයන් තබයි, නමුත් != ඇත්ත වැරදි අගයන් පමණක් ඉතිරි වනු ඇත. එබැවින්, ක්රියාකරු ප්රතිස්ථාපනය කරන විට නැත OR ක්‍රියාකරු සමඟ කොන්දේසි දෙකක් පෙරහන වෙත ලබා දිය යුතුය, උදාහරණයක් ලෙස, කොහේද (col != True) හෝ (col is null).

අපි බූලියන් සමඟ කටයුතු කර ඇත, අපි ඉදිරියට යමු. දැනට, වෙනත් වෙනස්කම් වල බලපෑම ස්වාධීනව සලකා බැලීම සඳහා Boolean ෆිල්ටරය එහි මුල් ස්වරූපයට ආපසු යමු.

timestanptz? hz

සාමාන්‍යයෙන්, දුරස්ථ සේවාදායකයන් සම්බන්ධ ඉල්ලීමක් නිවැරදිව ලියන්නේ කෙසේද යන්න පිළිබඳව ඔබට බොහෝ විට අත්හදා බැලිය යුතු අතර, මෙය සිදුවන්නේ මන්දැයි පැහැදිලි කිරීමක් සොයන්න. මේ ගැන අන්තර්ජාලයෙන් සොයා ගත හැකි තොරතුරු ඉතා අල්පය. එබැවින්, අත්හදා බැලීම් වලදී අපට පෙනී ගියේ ස්ථාවර දින පෙරහනක් දුරස්ථ සේවාදායකය වෙත පිපිරුමක් සමඟ පියාසර කරන බවයි, නමුත් අපට ගතිකව දිනය සැකසීමට අවශ්‍ය වූ විට, උදාහරණයක් ලෙස, now() හෝ CURRENT_DATE, මෙය සිදු නොවේ. අපගේ උදාහරණයේ දී, අපි පෙරහනක් එක් කළ අතර එමඟින් create_at තීරුවේ අතීතයේ හරියටම මාස 1 ක දත්ත අඩංගු විය (CURRENT_DATE - INTERVAL 'මාස 7' සහ CURRENT_DATE - INTERVAL 'මාස 6'). මෙම නඩුවේදී අප කළේ කුමක්ද?

explain analyze verbose
SELECT count(1)
FROM fdw_schema.table 
WHERE is_active is True
AND created_dt >= (SELECT CURRENT_DATE::timestamptz - INTERVAL '7 month') 
AND created_dt <(SELECT CURRENT_DATE::timestamptz - INTERVAL '6 month')
AND meta->>'source' = 'test';

Aggregate  (cost=306875.17..306875.18 rows=1 width=8) (actual time=4789.114..4789.115 rows=1 loops=1)
  Output: count(1)
  InitPlan 1 (returns $0)
    ->  Result  (cost=0.00..0.02 rows=1 width=8) (actual time=0.007..0.008 rows=1 loops=1)
          Output: ((('now'::cstring)::date)::timestamp with time zone - '7 mons'::interval)
  InitPlan 2 (returns $1)
    ->  Result  (cost=0.00..0.02 rows=1 width=8) (actual time=0.002..0.002 rows=1 loops=1)
          Output: ((('now'::cstring)::date)::timestamp with time zone - '6 mons'::interval)
  ->  Foreign Scan on fdw_schema."table"  (cost=100.02..306874.86 rows=105 width=0) (actual time=23.475..4681.419 rows=1360025 loops=1)
        Output: "table".id, "table".is_active, "table".meta, "table".created_dt
        Filter: (("table".is_active IS TRUE) AND (("table".meta ->> 'source'::text) = 'test'::text))
        Rows Removed by Filter: 76934
        Remote SQL: SELECT is_active, meta FROM fdw_schema.table WHERE ((created_dt >= $1::timestamp with time zone)) AND ((created_dt < $2::timestamp with time zone))
Planning time: 0.703 ms
Execution time: 4789.379 ms

උප විමසුමේ දිනය කල්තියා ගණනය කර සූදානම් කර ඇති විචල්‍යය පෙරණයට ලබා දෙන ලෙස අපි සැලසුම්කරුට පැවසුවෙමු. මෙම ඉඟිය අපට විශිෂ්ට ප්‍රති result ලයක් ලබා දුන්නේය, ඉල්ලීම 6 ගුණයකින් වේගවත් විය!

නැවතත්, මෙහිදී ප්‍රවේශම් වීම වැදගත් වේ: උප විමසුමේ දත්ත වර්ගය අප පෙරහන කරන ක්ෂේත්‍රයට සමාන විය යුතුය, එසේ නොමැතිනම් වර්ග වෙනස් බැවින්, පළමුව සියල්ල ලබා ගැනීම අවශ්‍ය බව සැලසුම්කරු තීරණය කරයි. දත්ත සහ දේශීයව පෙරහන්.

දින පෙරහන එහි මුල් අගයට යවමු.

ෆ්‍රෙඩී එදිරිව. Jsonb

සාමාන්‍යයෙන්, Boolean ක්ෂේත්‍ර සහ දින දැනටමත් අපගේ විමසුම ප්‍රමාණවත් ලෙස වේගවත් කර ඇත, නමුත් තවත් එක් දත්ත වර්ගයක් ඉතිරිව තිබුණි. මෙහි සාර්ථකත්වයක් ඇතත්, අවංකව කිවහොත්, එයින් පෙරීම සමඟ සටන තවමත් අවසන් නැත. ඉතින්, අපි පෙරණය පසුකර යාමට සමත් වූයේ එලෙසිනි jsonb දුරස්ථ සේවාදායකයට ක්ෂේත්‍රය.

explain analyze verbose
SELECT count(1)
FROM fdw_schema.table 
WHERE is_active is True
AND created_dt BETWEEN CURRENT_DATE - INTERVAL '7 month' 
AND CURRENT_DATE - INTERVAL '6 month'
AND meta @> '{"source":"test"}'::jsonb;

Aggregate  (cost=245463.60..245463.61 rows=1 width=8) (actual time=6727.589..6727.590 rows=1 loops=1)
  Output: count(1)
  ->  Foreign Scan on fdw_schema."table"  (cost=1100.00..245459.90 rows=1478 width=0) (actual time=16.213..6634.794 rows=1360025 loops=1)
        Output: "table".id, "table".is_active, "table".meta, "table".created_dt
        Filter: (("table".is_active IS TRUE) AND ("table".created_dt >= (('now'::cstring)::date - '7 mons'::interval)) AND ("table".created_dt <= ((('now'::cstring)::date)::timestamp with time zone - '6 mons'::interval)))
        Rows Removed by Filter: 619961
        Remote SQL: SELECT created_dt, is_active FROM fdw_schema.table WHERE ((meta @> '{"source": "test"}'::jsonb))
Planning time: 0.747 ms
Execution time: 6727.815 ms

ක්රියාකරුවන් පෙරීම වෙනුවට, ඔබ එක් ක්රියාකරුවෙකු සිටීම භාවිතා කළ යුතුය jsonb වෙනස් එකක් තුළ. මුල් පිටපත වෙනුවට තත්පර 7 යි 29. මේ දක්වා පෙරහන් හරහා සම්ප්‍රේෂණය කිරීමේ එකම සාර්ථක විකල්පය මෙයයි jsonb දුරස්ථ සේවාදායකයකට, නමුත් මෙහිදී එක් සීමාවක් සැලකිල්ලට ගැනීම වැදගත්ය: අපි දත්ත සමුදායේ 9.6 අනුවාදය භාවිතා කරමු, නමුත් අප්‍රේල් මස අවසානය වන විට අපි අවසාන පරීක්ෂණ සම්පූර්ණ කර 12 අනුවාදයට යාමට සැලසුම් කරමු. අපි යාවත්කාලීන කළ පසු, එය බලපෑවේ කෙසේද යන්න ගැන අපි ලියන්නෙමු, මන්ද බොහෝ බලාපොරොත්තු ඇති වෙනස්කම් රාශියක් ඇත: json_path, නව CTE හැසිරීම, පහළට තල්ලු කිරීම (10 අනුවාදයේ සිට පවතී). මට ඇත්තටම එය ඉක්මනින් උත්සාහ කිරීමට අවශ්‍යයි.

ඔහුව ඉවර කරන්න

එක් එක් වෙනස් කිරීම් ඉල්ලීම් වේගයට බලපාන ආකාරය අපි තනි තනිව පරීක්ෂා කළෙමු. අපි දැන් බලමු ෆිල්ටර් තුනම නිවැරදිව ලිව්වොත් මොකද වෙන්නේ කියලා.

explain analyze verbose
SELECT count(1)
FROM fdw_schema.table 
WHERE is_active = True
AND created_dt >= (SELECT CURRENT_DATE::timestamptz - INTERVAL '7 month') 
AND created_dt <(SELECT CURRENT_DATE::timestamptz - INTERVAL '6 month')
AND meta @> '{"source":"test"}'::jsonb;

Aggregate  (cost=322041.51..322041.52 rows=1 width=8) (actual time=2278.867..2278.867 rows=1 loops=1)
  Output: count(1)
  InitPlan 1 (returns $0)
    ->  Result  (cost=0.00..0.02 rows=1 width=8) (actual time=0.010..0.010 rows=1 loops=1)
          Output: ((('now'::cstring)::date)::timestamp with time zone - '7 mons'::interval)
  InitPlan 2 (returns $1)
    ->  Result  (cost=0.00..0.02 rows=1 width=8) (actual time=0.003..0.003 rows=1 loops=1)
          Output: ((('now'::cstring)::date)::timestamp with time zone - '6 mons'::interval)
  ->  Foreign Scan on fdw_schema."table"  (cost=100.02..322041.41 rows=25 width=0) (actual time=8.597..2153.809 rows=1360025 loops=1)
        Output: "table".id, "table".is_active, "table".meta, "table".created_dt
        Remote SQL: SELECT NULL FROM fdw_schema.table WHERE (is_active) AND ((created_dt >= $1::timestamp with time zone)) AND ((created_dt < $2::timestamp with time zone)) AND ((meta @> '{"source": "test"}'::jsonb))
Planning time: 0.820 ms
Execution time: 2279.087 ms

ඔව්, ඉල්ලීම වඩාත් සංකීර්ණ ලෙස පෙනේ, මෙය බලහත්කාර ගාස්තුවකි, නමුත් ක්රියාත්මක කිරීමේ වේගය තත්පර 2 ක් වන අතර එය 10 ගුණයකට වඩා වේගවත් වේ! අපි කතා කරන්නේ සාපේක්ෂව කුඩා දත්ත කට්ටලයකට එරෙහිව සරල විමසුමක් ගැන ය. සැබෑ ඉල්ලීම් මත, අපට සිය ගුණයකින් වැඩි වීමක් ලැබුණි.

සාරාංශගත කිරීම සඳහා: ඔබ FDW සමඟ PostgreSQL භාවිතා කරන්නේ නම්, සියලු පෙරහන් දුරස්ථ සේවාදායකය වෙත යවා ඇත්දැයි පරීක්ෂා කරන්න, එවිට ඔබ සතුටු වනු ඇත... අවම වශයෙන් ඔබ විවිධ සේවාදායකයන්ගෙන් වගු අතර සම්බන්ධ වන තුරු. නමුත් එය තවත් ලිපියක් සඳහා වූ කතාවකි.

ඔබගේ අවදානය පිළිබඳ ස්තූතියි! අදහස් දැක්වීමේදී ඔබේ අත්දැකීම් පිළිබඳ ප්‍රශ්න, අදහස් සහ කථා ඇසීමට මම කැමතියි.

මූලාශ්රය: www.habr.com

අදහස් එක් කරන්න