نسخة احتياطية رقيقة لأنظمة ملفات Linux. كيفية إنشاء نسخ عمل من قاعدة بيانات MySQL بحجم 20 تيرابايت في XNUMX ثانية

نسخة احتياطية رقيقة لأنظمة ملفات Linux. كيفية إنشاء نسخ عمل من قاعدة بيانات MySQL بحجم 20 تيرابايت في XNUMX ثانية

اسمي يوري ، أنا رئيس مجموعة إدارة النظام في Citymobil. سأشارك اليوم تجربتي مع تقنية التزويد الرقيقة لأنظمة ملفات Linux وأخبرك كيف يمكن تطبيقها في عمليات CI / CD التكنولوجية للشركة. سنحلل الموقف عندما نحتاج إلى نسخ من قاعدة بيانات MySQL أقرب ما يمكن إلى الإصدار "القتالي" المتاح للقراءة والكتابة ، وذلك من أجل اختبار الكود تلقائيًا عند تسليمه إلى الإنتاج.

مقدمة: لماذا تعطي نصيحة سيئة؟

سؤال منطقي ، نظرًا لوجود آليات راسخة لترحيل مخططات قاعدة البيانات إلى بيئات الاختبار. لماذا يتم إحضار نظام إدارة قواعد البيانات الرئيسي غير المقسم إلى مثل هذه الأحجام على الإطلاق؟ وليس كل البيانات مطلوبة للاختبار. سأحاول أن أشرح.

منذ حوالي عام ، على خلفية النمو النشط لمجمع سيارات الأجرة لدينا (في عام 2018 ، نما بنحو 15 مرة من حيث الرحلات المكتملة) ، وزاد حجم البيانات وتحميل الخادم وتكرار بدء التشغيل. نحن في الحالة التالية:

  • نمت قاعدة بيانات MySQL الرئيسية إلى حوالي 1000 جدول بإجمالي 2,5 تيرابايت ، وتستمر في النمو.
  • لم تكن هناك طريقة لتقسيم القاعدة وتدميرها بسرعة. لم يكن هذا مسموحًا به من خلال النهج القديم "أكتب إلى قاعدة البيانات ما أريده وكيف أريد" ، وهو مجموعة من JOINs وتبعيات الجدول الداخلية.
  • لم تكن هناك آلية لترحيل مخطط قاعدة البيانات لبيئات الاختبار.
  • لم يكن هناك اختبار تلقائي للرمز عند بدء التشغيل.

المشكلة الأخيرة التي أردت حلها في أسرع وقت ممكن. تمت كتابة اختبارات ساعي البريد بالفعل لاختبار وحدة PHP الرئيسية ، لكن قاعدة البيانات المحدثة كانت مفقودة. في الوقت نفسه ، لم نتمكن من إنشاء نسخة متماثلة في الليل ، وجعلها نسخة رئيسية ومنحها تمزيقها لأجزاء خلال النهار: عدد كبير جدًا من عمليات الطرح والتغييرات ، بما في ذلك في البيانات ومخطط قاعدة البيانات ، سيكون لها جعل الحامل غير صالح للعمل بحلول منتصف النهار. وسيكون من غير المجدي قصر عمليات الطرح على يوم عمل فقط.

ومع ذلك ، تم الانتهاء من المهمة: تلقينا أول منصة عمل في غضون أسبوعين. على مدار العام الماضي ، خضع للعديد من التغييرات وما زال يستخدم.

بعد ذلك ، سأصف بالتفصيل جميع خطوات ومراحل تطوير حلنا. سترى أن هذه الطريقة تستحق الحق في الوجود.

ما هو "الحجز الرقيق"؟
هذه تقنية أجهزة أو برامج (اسم آخر هو أحجام متفرقة) ، مما يسمح لك بتخصيص المزيد من الموارد المطلوبة أكثر مما هو متاح. في الوقت نفسه ، يجب أن يفي الحجم المخصص بمعايير كافية (بقدر الحاجة) وفي الوقت المناسب (للوقت المطلوب). بشكل عام ، يتم استخدام التوفير الرقيق في أنظمة التخزين المختلفة من أجل توفير مساحة على القرص بالكميات المطلوبة التي تتجاوز ما هو متاح بالفعل. يتم دعم هذه التقنية بواسطة أنظمة ملفات مختلفة ، مثل LVM2 و ZFS و BTRFS. يستخدم على نطاق واسع في برامج Hypervisor الافتراضية. سمحت لنا النسخة الاحتياطية الرفيعة بإنشاء العديد من نسخ هذا القسم بسرعة من لقطات قسم البيانات الرئيسية التي نحتاجها (دليل البيانات في MySQL DBMS).

الكابينة الأولى ، تقنية Thin LVM

يمكن أيضًا تسمية هذا الفصل بـ "كيفية عمل أسرع اللقطات الممكنة لكميات كبيرة من البيانات باستخدام رقيقة LVM، مما يقلل من استقرار نظام ملفات MySQL و DBMS إلى مستويات فاحشة.

نظرًا لأننا استخدمنا LVM بالفعل لبناء أقسام نظام التشغيل الرئيسية ، فقد قررنا البدء بها. بادئ ذي بدء ، احتجنا إلى آلة فيزيائية منفصلة - نسخة طبق الأصل من قاعدة بيانات MySQL الرئيسية الخاصة بنا ، والتي يمكننا من خلالها إنشاء لقطة طبق الأصل عند الطلب ورفعها بجوار مثيل MySQL منفصل. طوال مدة الاختبار ، سمحنا بتعديل العمليات ليتم تطبيقها على هذه الحالة ، وعند الانتهاء من الاختبارات ، قمنا بحذفها بأمان. كان تكوين الخادم على هذا النحو:

  • 2 × Intel Silver 4114 (10x2,2 جيجا هرتز HT)
  • 8 x 32 جيجا بايت DDR4
  • 8 × 1920 جيجا بايت Intel SSD في Adaptec RAID-10

يمكنك كتابة مقال منفصل حول موضوع الاختيار بين وحدة تحكم RAID وبرنامج RAID MD. دعني أقول فقط إن اختيارنا تأثر بعاملين:

  • في وقت تعيين المهمة ، قمنا بتثبيت جميع نظم إدارة قواعد البيانات (DBMS) على وحدات تحكم RAID ، لذلك يمكننا القول أن هذا حدث تاريخيًا.
  • كان الاختلاف في الأداء في اختبارات واختبارات نظام الملفات التركيبي مع العمليات المختلفة في MySQL ضئيلًا.

قمنا بتقسيم RAID-10 الناتج: لقد أنشأنا مجموعة وحدة تخزين واحدة (VG) لوحدة التخزين بأكملها (مع زيادة مقدارها 6,7 جيجابايت تقريبًا) وأنشأنا قسمًا منطقيًا (وحدة التخزين المنطقية ، LV) لنظام سعة 50 جيجابايت. في الوضع الطبيعي ، نحدد باقي المكان ضمن القسم c MySQL. لكننا احتجنا إلى حجز رقيق ، لذلك أنشأنا أولاً ما يسمى التجمع ، الذي أنشأنا بداخله قسمًا بحجم 3,5 تيرابايت ضمن / var / lib / mysql (بناءً على أحجام قاعدة البيانات المقدرة):

lvcreate -l 100%FREE -T vga/thin
lvcreate -V 3.5T -T vga/thin -n mysql

قمنا بتهيئة القسم في ext4 ، وقمنا بتثبيته ، وسجلنا نسخة طبق الأصل وحصلنا على الحامل الأصلي. ثم قمنا بعمل ربط على شكل واجهة برمجة تطبيقات (API) التي يجب أن تنشئ لقطات ، وترفع مثيل قاعدة بيانات MySQL على منفذ معين ، وتحذف النسخة التي تم إنشاؤها. نظرًا لأن هذا يستخدم استدعاءات النظام فقط ، فقد اخترنا bash المعتادة كلغة برمجة نصية ، ونشرنا حلاً مفتوح المصدر مثل ربط HTTP → bash API Goexpose، مكتوب في Go.

يومًا ما سننشر نصوص bash النصية الخاصة بنا في مصدر مفتوح ، لكن في الوقت الحالي سأصف ببساطة الخوارزمية الأساسية:

إنشاء لقطة رئيسية رئيسية:

  1. نتوقف عن النسخة المتماثلة الرئيسية.
  2. نضع قفلًا على العمليات باستخدام لقطة رئيسية.
  3. إنشاء لقطة رئيسية جديدة.
  4. ابدأ MySQL وقم بإزالة القفل.

إنشاء قاعدة بيانات على منفذ عشوائي من snapmain:

  1. نضع قفلًا على مثيل محدد لقاعدة البيانات (المنفذ).
  2. تحقق من حظر إنشاء اللقطة الرئيسية. إذا كان الأمر كذلك ، فإننا ننتظر ونعيد التحقق كل 5 ثوان.
  3. تحقق مما إذا كان هناك قسم LV قديم للمثيل.
    3.1 إذا كان هناك ، فقم بإيقاف مثيل MySQL باستخدام kill -9 وحذف قسم LV.
  4. قم بإنشاء مثيل جديد من snapmain.
  5. قم بإعداد الدلائل وتحميلها لهذه الحالة.
  6. نزيل علامات الرقيق (الملفات) ونبدأ مثيل MySQL.
  7. نحن نصنع سيدًا منه.
  8. إزالة الحجب.

حذف قاعدة بيانات على منفذ عشوائي:

  1. نضع قفلًا على مثيل محدد لقاعدة البيانات (المنفذ).
  2. اقتل مثيل MySQL باستخدام kill -9.
  3. قم بإلغاء تحميل الدلائل.
  4. احذف قسم LV وحرر القفل.

مثال على أوامر لنسخ أقسام من طبعة قاعدة بيانات جديدة:

lvcreate -n stage_3307 -s vga/snapmain
lvchange -ay -K vga/stage_3307
mount -o noatime,nodiratime,data=writeback /dev/mapper/vga-stage_3307 /mnt/stage_3307

سأخبرك الآن عن المشكلة الرئيسية التي واجهناها عند استخدام الحجوزات الرقيقة. واجهنا أداء محركات SSD. حدث هذا بسبب خصائص Thin LVM: فهو يعمل بشكل أساسي على مستوى الجهاز مع قطع منخفضة المستوى بحجم افتراضي يبلغ 4 ميغابايت. كيف بدا:

  1. أنشئ لقطة من قسم main / var / lib / mysql.
  2. نبدأ في التكرار للحاق بالسيد.
  3. يؤدي أي تغيير في جداول النسخ المتماثلة إلى تخزين قطع البيانات القديمة غير المتغيرة في قسم اللقطة.
  4. يؤدي أي تغيير في مثيل الاختبار البارز إلى تخزين قطع البيانات القديمة غير المعدلة في قسم اللقطة المستنسخة لذلك المثال.
  5. نحصل على تحميل عمليات الإدخال / الإخراج بنسبة 100٪ لكل جهاز ، مما يؤدي إلى إبطاء أي عمليات ويتخلف تدريجياً عن النسخة المتماثلة.
  6. بحلول نهاية يوم العمل ، نحصل على كشك متأخّر بعدة ساعات.

كيف تعاملنا مع هذا من أجل الحصول على نتيجة أكثر عقلانية (النقاط البارزة):

تحكم RAID:

  • يتم تعطيل جميع أنواع التخزين المؤقت افتراضيًا.
  • تعيين إعادة الكتابة (عندما تدخل البيانات إلى المخزن المؤقت ، تكتمل الكتابة قبل إجراء الحفظ الفعلي على القرص).

نظام الملفات:

  • في نقطة التحميل / var / lib / mysql ، كتبنا noatime ، nodiratime ، البيانات = إعادة الكتابة
  • تم إيقاف تشغيل نظام ext4 Journal مع tune2fs.

الخلية:

  • المنصوص عليها innodb_flush_method = O_DSYNC (زيادة سرعة الكتابة ، وبالتالي تقليل الموثوقية).
  • تسجيل معطل ، لا نحتاج إلى سجلات.
  • المنصوص عليها innodb_buffer_pool_size = 4G (كلما كان حجم تجمع InnoDB أصغر ، تموت MySQL بشكل أسرع عند إيقافه ، وكلما أسرعنا في إنشاء لقطة).

هذه ليست قائمة كاملة ، خاصة بالنسبة لـ MySQL. ومع ذلك ، فإن التغييرات المتبقية طفيفة وغالبًا لا تنطبق دائمًا وليست بالضبط. على سبيل المثال ، في محاولة لتفريغ الأقراص ، ابتعدنا inodb_parallel_doublewrite_path in / dev / shm ، والذي في بعض الحالات ، عند بدء مثيل تم إنهاؤه بشكل غير صحيح ، يوفر لنا ما يصل إلى 5 ثوانٍ.

لماذا نوقف MySQL قبل أخذ لقطة؟ بعد كل شيء ، يمكننا إزالته من نسخة متماثلة صالحة للعمل. هذا صحيح ، باستثناء أن مثيل قاعدة البيانات الجديدة في هذه اللقطة سيتم اعتباره تالفًا افتراضيًا وسيتطلب فحصًا كاملاً عند بدء التشغيل. من المؤكد أن إيقاف النسخة المتماثلة أسرع ، على الرغم من أنها تنتهي بالعملية الأطول في العملية برمتها.

ونتيجة لذلك ، حصلنا على المزيد من المواعيد المقبولة ومنصة جاهزة للعمل. على الرغم من أنه ، كما ترون من أكثر مؤامرة تراكم النسخ المتماثل للنسخة المتماثلة الرئيسية ، فإن الموقف لا يزال بعيدًا عن المثالية:
نسخة احتياطية رقيقة لأنظمة ملفات Linux. كيفية إنشاء نسخ عمل من قاعدة بيانات MySQL بحجم 20 تيرابايت في XNUMX ثانية

من بين أوجه القصور الأخرى ، تجدر الإشارة إلى الاستحالة العملية لمراقبة تجمع Thin LVM: بالإضافة إلى وظائف iostat القياسية للنظام ، من المستحيل فهم ، على سبيل المثال ، أي عنصر من عناصر التجمع ينتج الآن أكبر حمل على الملف نظام.

بشكل منفصل ، تجدر الإشارة إلى عيب كبير مرتبط بالتحسين الموضح أعلاه: لقد حصلنا على حامل YOLO. مرة واحدة تقريبًا كل شهر أو شهرين ، لا يمكن لـ ext4 تحمل مثل هذا الإساءة بحد ذاته وتعطل بشكل لا رجعة فيه ، مما يتطلب إعادة تهيئة النسخة المتماثلة وإعادة تحميلها. بعد أن انتصرنا بسرعة ، دمرنا الاستقرار بشكل ميؤوس منه.

ما هي المقاييس التي يجب مراقبتها أثناء تشغيل Thin LVM:

  • بيانات تجمع رقيقة ٪
  • البيانات الوصفية للمجمع الرقيق٪

إذا نجا جناحنا من نفاد مساحة البيانات (يكفي تنظيف الأقراص) ، فإن نفاد مساحة البيانات الوصفية سيؤدي إلى انهيار المجموعة بالكامل والحاجة إلى إعادة إنشائها من الصفر.

يصبح نظام الملفات داخل التجمع مجزأًا جدًا بمرور الوقت. أوصي بتشغيل أمر cron مرة واحدة في اليوم fstrim -v / var / lib / mysql.

المجاميع الفرعية:

  • التكنولوجيا قابلة للتطبيق بسهولة ، مثل LVM نفسها ، ولا تتطلب مهارات هندسية خاصة.
  • إنها مناسبة تمامًا لقاعدة بيانات صغيرة وليست محملة جدًا. كلما كانت قاعدة البيانات أصغر ، قل عدد الأجزاء التي تتحرك عبر نظام الملفات داخل التجمع ، وكلما انخفض الحمل على الأقراص.
  • بالنسبة لمشكلتنا ، بدأنا في البحث عن حلول أخرى ، والتي سيتم مناقشتها في القسم التالي.

الحامل الثاني بتقنية ZFS

اعتدت على العمل مع نظام ملفات ZFS منذ وقت طويل ، ولكن بعد ذلك عملت ZFS بشكل جيد مع عائلة نظام التشغيل Solaris الأصلية. كان هناك إصدار تم نقله إلى FreeBSD بمستوى تنفيذ جيد إلى حد ما. كان هناك أيضًا منفذ Linux غير مكتمل يستخدمه عدد قليل من الأشخاص. نظرًا لهيكل تخزين بيانات B-tree (بالمناسبة ، تمتلك InnoDB MySQL نفس بنية التخزين) ، كان أداء ZFS ضعيفًا في عمليات التثبيت التي تحتوي على عدد كبير جدًا من الملفات. كل هذا ، جنبًا إلى جنب مع الحاجة إلى تعلم العتاد قبل الاستخدام ، حذف نظام الملفات هذا من ممارستي لفترة طويلة. ظهر ext4 و xfs وأصبحا المعيار. ولكن بالنظر إلى أن ZFS أكثر من مناسب لمهمتنا ، فإن إصدار Linux ، وفقًا للمراجعات ، قد نما إلى منتج عاقل تمامًا (على الرغم من عدم دعمه الكامل ، ولهذا السبب من الممكن وضع النظام على ZFS بالكامل من خدش فقط بمساعدة الشامانية المختلفة) ، قررنا تجربتها.

لأسباب واضحة ، تم اختيار الحامل بتكوين مماثل (باستثناء وحدة تحكم RAID). تم تركيب ثمانية محركات أقراص SSD بسعة 1920 جيجا بايت. لم تكن هناك رغبة في كتابة صورة الشبكة الخاصة بي لتحميل الخادم لتعريف ZFS ، لذلك أخذنا قضمة من جميع الأقراص سعة 50 جيجابايت وصنعنا MD RAID-10 عليها للنظام. تم دمج الـ 1950 جيجا بايت المتبقية على كل قرص في نظير ZFS لـ RAID-10:

zpool create zpool mirror /dev/sda2 /dev/sdb2 mirror /dev/sdc2 /dev/sdd2 mirror /dev/sde2 /dev/sdf2 mirror /dev/sdg2 /dev/sdh2

أقسام مصنوعة لـ MySQL:

zfs create zpool/mysql
zfs set compression=gzip zpool/mysql
zfs set recordsize=128k zpool/mysql
zfs set atime=off zpool/mysql
zfs create zpool/mysql/data
zfs set recordsize=16k zpool/mysql/data
zfs set primarycache=metadata zpool/mysql/data
zfs set mountpoint=/var/lib/mysql zpool/mysql/data

يرجى ملاحظة أننا قمنا بتمكين ضغط بيانات gzip الأصلي. لدينا الكثير من موارد المعالج على الخادم ولم يتم استخدامها بشكل كامل. ونتيجة لذلك ، تحولت 3 تيرابايت من قاعدة بياناتنا إلى 1,6 تيرابايت ، وبما أن الرابط الضعيف ، كما في الحالة السابقة ، هو أقصى أداء للقرص ، بيانات أقل ، كلما كان ذلك أفضل ، نحصل منذ البداية على مكافأة رائعة من ZFS! أثناء ساعة الذروة عند التحميل الكامل ، يستغرق الأمر ما يصل إلى 4 مراكز للحفاظ على تشغيل gzip ، لكننا لا نمانع.

ذهب المزيد من التنفيذ بشكل أسرع. تم نقل إعدادات النسخة المتماثلة MySQL من حامل LVM كمخطط. اضطررت إلى قضاء بعض الوقت في إعادة كتابة البرامج النصية لأوامر ZFS ، ولكن بشكل عام ، ظلت الخوارزميات كما هي. مثال لقطة:

zfs set snapdir=visible zpool/mysql/data
zfs create zpool/stage_3307
zfs clone zpool/mysql/data@snapmain zpool/stage_3307/data
zfs set mountpoint=/mnt/stage_3307 zpool/stage_3307/data

من الضبط الإضافي: تم أخذ أقسام ZFS مع البيانات الوصفية وسجلات l2arc و zil في الذاكرة. بالنسبة لمهمتنا ، كما اتضح لاحقًا ، كان هذا زائدًا عن الحاجة ، لكن في الوقت الحالي تركنا هذا التحسين ، ليس من الصعب تغييره في بعض الأحيان. من الآثار السلبية - بعد إعادة تشغيل الخادم ، يجب عليك إعادة إنشاء مناطق الذاكرة المقابلة. لا تضيع البيانات. حالة لقطة zpool:

logs
      /dev/shm/zil_slog.img  ONLINE       0     0     0
cache
      /dev/shm/l2arc.img     ONLINE       0     0     0

في هذا التكوين ، بدأنا في اختبار المقعد وحصلنا على نتائج ممتازة: مع وجود نسختين من قاعدة البيانات قيد التشغيل في وقت واحد (ونسخة متماثلة رئيسية نشطة) على اللقطات ، حصلنا على استخدام للقرص بنسبة 50-60٪.

لقد تخلصنا من مشكلتنا الرئيسية ، والتي يمكن رؤيتها في الرسم البياني لنسخ النسخ المتراكم (قارن مع الرسم البياني السابق في قسم LVM الرقيق):
نسخة احتياطية رقيقة لأنظمة ملفات Linux. كيفية إنشاء نسخ عمل من قاعدة بيانات MySQL بحجم 20 تيرابايت في XNUMX ثانية

بالإضافة إلى ذلك ، وبفضل هذا ، قمنا بتسريع جميع العمليات بشكل كبير: يستغرق إنشاء لقطة كاملة مع إيقاف وبدء نسخة متماثلة ما يصل إلى 40 ثانية ، ونشر مثيل MySQL جديد من لقطة يستغرق ما يصل إلى 20 ثانية. وهو ما يرضي أكثر من كل منا واختبارات الكود لدينا.

المجاميع الفرعية:

  • النتائج مرضية تماما حاجتنا لنسخة من قاعدة بيانات الإنتاج لاختبار الكود.
  • تتطلب التكنولوجيا الدخول: تحتاج إلى فهم ماهية ZFS وكيفية التعامل معها.
  • لم نتحقق من الوضع الحالي لـ ZFS بعدد كبير (من مليون) من الملفات الصغيرة. لكننا نفترض أن المشكلة لا تزال قائمة ، لذلك لا أوصي بنظام الملفات هذا لأي تخزين للملفات.

ما هي الخطوة التالية؟

لا يوجد شيء آخر نقوم به في إطار الجناح ، فالنتيجة تناسبنا. ربما ، في المستقبل ، سنضيف استثناءات للجداول غير الضرورية للاختبار لإعداد النسخ المتماثل للحامل ، وسيؤدي ذلك إلى تقليل حجم قاعدة البيانات بشكل أكبر. لم نختبر نظام BTRFS وتطبيقه الضئيل. ومع ذلك ، لم تعد هذه المهمة تستحق العناء ، حيث تم تحقيق الهدف الرئيسي. بشكل عام ، بالطبع ، أريد الابتعاد عن النهج أعلاه - لتنفيذ عمليات ترحيل قاعدة بيانات العمل إلى بيئة اختبار ، وإنشاء دائرة قاعدة بيانات اختبار منفصلة ، والبدء في تقسيم قاعدة البيانات الرئيسية. نحن ننفذ الكثير من هذا بالفعل ، والذي سنقوم بتغطيته بالتأكيد في المقالات المستقبلية.

نتائج

تم حل المشكلة الأصلية ، وإن كان ذلك بطريقة غير عادية. في الاستنتاجات المؤقتة ، تم وصف مزايا وعيوب كل من التقنيات المطبقة ، لذلك دعونا نقرر أي تقنية يمكن استخدامها ومتى:

  • Thin LVM - في قواعد البيانات الصغيرة وعندما لا تريد أو لا يتوفر لديك الوقت لتعلم ZFS.
  • ZFS - إذا كان لديك خبرة في ذلك أو كانت لديك فرصة لقضاء الوقت في التعلم في أي موقف.

في مستوى أعلى من العرض ، لا تعد هذه المقالة مجرد مقارنة لتقنية نظامي ملفات. الفكرة الرئيسية التي أود نقلها وتعزيزها هي أنه لا يجب أن تخاف من التفكير خارج الصندوق في المواقف الحاسمة للأعمال ، وأخذ الوصفات الجاهزة فقط. ذات مرة ، كان بإمكاننا هز رؤوسنا في القسم الفني بأكمله والقول إن مهمة إنشاء نسخ ثلاثة تيرابايت من قاعدة البيانات في أقل من دقيقة أمر مستحيل ، ولسنا بحاجة إلى تقنيات محفوفة بالمخاطر ، فلنقم بذلك بشكل صحيح. كان ذلك ممكنًا ، لكننا كنا سنخسر حوالي ستة أشهر إلى عام والكثير من رحلات العملاء (السفر هو مؤشر أعمالنا الرئيسي) بدون اختبارات وأثناء التنفيذ. من خلال العمل خارج الصندوق ، لم نفقد الكثير من الوقت للتنفيذ ، واكتسبنا خبرة في التقنيات القديمة الجديدة والمنسية ، وقدمنا ​​الاختبار في نفس اللحظة التي كنا في حاجة إليها حقًا. مما لا شك فيه أن هذا كان له أثر إيجابي على جميع مؤشراتنا. الخيار لك دائمًا ، ونحن ، من جانبنا ، سنواصل الحديث عن الإنجازات الحالية والمستقبلية المثيرة للاهتمام في مدونتنا.

المصدر: www.habr.com