الانتقال إلى المحتوى الرئيسي

أنواع الاختبارات

توجد الاختبارات التالية في ClickHouse:

الاختبارات الوظيفية

تُعد الاختبارات الوظيفية الأبسط والأسهل استخدامًا. يمكن اختبار معظم ميزات ClickHouse باستخدام الاختبارات الوظيفية، وهي إلزامية لكل تغيير في شيفرة ClickHouse يمكن اختباره بهذه الطريقة. يرسل كل اختبار وظيفي استعلامًا واحدًا أو عدة استعلامات إلى خادم ClickHouse قيد التشغيل، ثم يقارن النتيجة بالمرجع. توجد الاختبارات في الدليل ./tests/queries. يمكن أن يكون كل اختبار أحد نوعين: .sql و .sh.
  • اختبار .sql هو برنامج SQL نصي بسيط يُمرَّر إلى clickhouse-client عبر pipe.
  • اختبار .sh هو برنامج نصي يُشغَّل مباشرةً.
تكون اختبارات SQL هي الخيار المفضل عمومًا مقارنةً باختبارات .sh. يجب ألا تستخدم اختبارات .sh إلا عند الحاجة إلى اختبار ميزة لا يمكن التحقق منها باستخدام SQL فقط، مثل تمرير بعض بيانات الإدخال إلى clickhouse-client عبر pipe أو اختبار clickhouse-local.
من الأخطاء الشائعة عند اختبار أنواع البيانات DateTime و DateTime64 افتراض أن الخادم يستخدم منطقة زمنية محددة (مثل “UTC”). وهذا غير صحيح، إذ تُحدَّد المناطق الزمنية عشوائيًا عمدًا في تشغيلات اختبارات CI. وأسهل حل بديل هو تحديد المنطقة الزمنية لقيم الاختبار صراحةً، مثل toDateTime64(val, 3, 'Europe/Amsterdam').

تشغيل اختبار محليًا

شغّل خادم ClickHouse محليًا، مع الاستماع على المنفذ الافتراضي (9000). ولتشغيل الاختبار 01428_hash_set_nan_key، على سبيل المثال، انتقل إلى مجلد المستودع ونفّذ الأمر التالي:
PATH=<path to clickhouse-client>:$PATH tests/clickhouse-test 01428_hash_set_nan_key
تُكتب نتائج الاختبار (stderr وstdout) في الملفين 01428_hash_set_nan_key.[stderr|stdout] الموجودين بجوار ملف الاختبار نفسه (بالنسبة إلى queries/0_stateless/foo.sql، سيكون الإخراج في queries/0_stateless/foo.stdout). راجع tests/clickhouse-test --help للاطّلاع على جميع خيارات clickhouse-test. يمكنك تشغيل جميع الاختبارات أو تشغيل مجموعة فرعية منها بتمرير عامل تصفية لأسماء الاختبارات: ./clickhouse-test substring. توجد أيضًا خيارات لتشغيل الاختبارات بالتوازي أو بترتيب عشوائي.

تشغيل الاختبارات السريعة

قد تحتاج إلى جهاز بمواصفات قوية نسبيًا لتشغيل مجموعة فرعية من الاختبارات (تُسمى “Fast test”). تنجح الخطوات التالية على مثيل AWS من النوع t3.2xlarge يعمل بنظام Ubuntu amd64 مع سعة تخزين تبلغ 100 GB.
  1. ثبّت المتطلبات الأساسية ثم سجّل الدخول مرة أخرى.
sudo apt-get update
sudo apt-get install docker.io
sudo usermod -aG docker "$USER"
  1. نزّل الشيفرة المصدرية.
git clone --single-branch https://github.com/ClickHouse/ClickHouse
cd ClickHouse
  1. ابنِ الشيفرة البرمجية وشغّل “الاختبارات السريعة”.
python -m ci.praktika run fast
من المفترض أن تحصل على
Failed: 0, Passed: 7394, Skipped: 1795
إذا تركت العملية تعمل دون مراقبة، فيمكنك استخدام nohup أو disown لإبقائها قيد التشغيل بعد انقطاع اتصال ssh.

تشغيل الاختبارات عديمة الحالة

قد تحتاج إلى جهاز قوي إلى حدٍّ ما لتشغيل الاختبارات عديمة الحالة. نجحت الخطوات التالية على مثيل m7i.8xlarge من AWS بمعمارية amd64 ونظام Ubuntu، مع سعة تخزين تبلغ 200 جيجابايت.
  1. ثبّت المتطلبات اللازمة ثم سجّل الدخول مجددًا.
sudo apt-get update
sudo apt-get install docker.io
sudo usermod -aG docker "$USER"
sudo tee /etc/docker/daemon.json <<'EOF'
{
  "ipv6": true,
  "ip6tables": true
}
EOF
sudo systemctl restart docker
  1. نزّل الشيفرة المصدرية.
git clone --single-branch https://github.com/ClickHouse/ClickHouse
cd ClickHouse
  1. قم ببناء الشيفرة.
python -m ci.praktika run build_debug
cp ci/tmp/build/programs/clickhouse ci/tmp
  1. شغّل الاختبارات عديمة الحالة التي يمكن تنفيذها بالتوازي.
python -m ci.praktika run functional
من المفترض أن تحصل على
Failed: 0, Passed: 8497, Skipped: 103
ملاحظة: تُشغِّل استدعاءات python -m ci.praktika run مهمةً محددة للتكامل المستمر، ويمكنك قراءة المزيد عن ClickHouse CI هنا.

إضافة اختبار جديد

لإضافة اختبار جديد، أنشئ أولًا ملفًا بامتداد .sql أو .sh في الدليل queries/0_stateless. ثم أنشئ ملف .reference المقابل باستخدام clickhouse-client < 12345_test.sql > 12345_test.reference أو ./12345_test.sh > ./12345_test.reference. يجب أن تقتصر الاختبارات على إنشاء الجداول أو حذفها أو تنفيذ عمليات select عليها، وما إلى ذلك، ضمن قاعدة البيانات test فقط، وهي قاعدة بيانات تُنشأ تلقائيًا مسبقًا. ولا بأس باستخدام الجداول المؤقتة. ولإعداد البيئة نفسها المستخدمة في CI على جهازك المحلي، ثبّت إعدادات الاختبار (إذ ستستخدم تنفيذًا وهميًا لـ ZooKeeper وستعدّل بعض الإعدادات)
cd <repository>/tests/config
sudo ./install.sh
يجب أن تكون الاختبارات
  • في أضيق حد ممكن: لا تُنشئ إلا الجداول والأعمدة وأدنى قدر لازم من التعقيد،
  • سريعة: ألّا تستغرق أكثر من بضع ثوانٍ (ويُفضَّل: أقل من ثانية)،
  • صحيحة وحتمية: أن تفشل إذا، وفقط إذا، كانت الميزة قيد الاختبار لا تعمل،
  • معزولة/عديمة الحالة: ألا تعتمد على البيئة أو التوقيت
  • شاملة: أن تغطي الحالات الطرفية مثل الأصفار، والقيم NULL، والمجموعات الفارغة، والاستثناءات (الاختبارات السلبية؛ استخدم الصياغة -- { serverError xyz } و -- { clientError xyz } لذلك)،
  • أن تنظّف الجداول في نهاية الاختبار (تحسّبًا لوجود بقايا)،
  • والتأكّد من أن الاختبارات الأخرى لا تختبر الشيء نفسه (أي استخدم grep أولًا).

اختبارات القوالب باستخدام Jinja

يمكن كتابة اختبار بصيغة .sql على شكل قالب Jinja2 بإضافة اللاحقة .j2 إلى اسم الملف، بحيث يصبح foo.sql هو foo.sql.j2. قبل تشغيل الاختبار، يعرض clickhouse-test القالب كنص برمجي عادي بصيغة .sql ثم ينفّذ الناتج. يفيد ذلك عندما يكرّر الاختبار الاستعلام نفسه مع اختلافات بسيطة: إذ تُنشئ الحلقة الاستعلامات انطلاقًا من قالب موجز بدلًا من كتابة كل استعلام يدويًا. وأكثر البنى شيوعًا هي:
  • {% for ... %} ... {% endfor %} لتكرار كتلة،
  • {{ expression }} لإدراج قيمة في المخرجات،
  • -%} و{%- لحذف المسافات البيضاء المجاورة حتى يبقى النص البرمجي المُولَّد نظيفًا.
على سبيل المثال، هذا القالب:
{% for type in ['UInt8', 'UInt16', 'UInt32'] -%}
SELECT toTypeName(0::{{ type }});
{% endfor -%}
يظهر بالشكل التالي:
SELECT toTypeName(0::UInt8);
SELECT toTypeName(0::UInt16);
SELECT toTypeName(0::UInt32);
يمكن تقديم المخرجات المتوقعة إما كملف <name>.reference عادي يحتوي على النتائج الموسَّعة بالكامل، أو كقالب <name>.reference.j2، والذي يعالجه clickhouse-test بالطريقة نفسها قبل المقارنة. استخدم الصيغة المعتمدة على القوالب عندما تتبع المخرجات المتوقعة أيضًا نمطًا متكررًا. لمزيد من الأمثلة، راجع ملفات *.sql.j2 الموجودة في tests/queries/0_stateless/.

تقييد تشغيل الاختبارات

يمكن أن يتضمن الاختبار صفرًا أو أكثر من الوسوم التي تحدد القيود على السياقات التي يُشغَّل فيها الاختبار ضمن بيئة CI. بالنسبة لاختبارات .sql، تُوضَع الوسوم في السطر الأول على شكل تعليق SQL:
-- Tags: no-fasttest, no-replicated-database
-- no-fasttest: <provide_a_reason_for_the_tag_here>
-- no-replicated-database: <provide_a_reason_here>

SELECT 1
بالنسبة لاختبارات .sh، تُكتب الوسوم على شكل تعليق في السطر الثاني:
#!/usr/bin/env bash
# Tags: no-fasttest, no-replicated-database
# - no-fasttest: <provide_a_reason_for_the_tag_here>
# - no-replicated-database: <provide_a_reason_here>
قائمة الوسوم المتاحة:
اسم الوسمما يفعلهمثال على الاستخدام
disabledلا يتم تشغيل الاختبار
longتمتد مدة تنفيذ الاختبار من دقيقة واحدة إلى 10 دقائق
deadlockيتم تشغيل الاختبار في حلقة لمدة طويلة
raceمثل deadlock. يُفضَّل استخدام deadlock
shardيجب أن يستمع الخادم إلى 127.0.0.*
distributedمثل shard. يُفضَّل استخدام shard
globalمثل shard. يُفضَّل استخدام shard
zookeeperيتطلب الاختبار تشغيل Zookeeper أو ClickHouse Keeperيستخدم الاختبار ReplicatedMergeTree
replicaمثل zookeeper. يُفضَّل استخدام zookeeper
no-fasttestلا يتم تشغيل الاختبار ضمن Fast testيستخدم الاختبار محرك الجداول MySQL المعطَّل في Fast test
fasttest-onlyلا يتم تشغيل الاختبار إلا ضمن Fast test
no-[asan, tsan, msan, ubsan]يعطّل الاختبارات في إصدارات البناء التي تستخدم sanitizersيتم تشغيل الاختبار تحت QEMU، الذي لا يعمل مع sanitizers
no-replicated-databaseيعطّل الاختبار عندما تستخدم قاعدة البيانات الافتراضية ReplicatedDatabaseEngine
no-ordinary-databaseيعطّل الاختبار عندما يكون محرك قاعدة البيانات الافتراضية هو Ordinary
no-parallelيعطّل تشغيل اختبارات أخرى بالتوازي مع هذا الاختباريقرأ الاختبار من جداول system وقد تنكسر الثوابت
no-parallel-replicasيعطّل الاختبار عندما تكون parallel replicas مفعّلة
no-debugيعطّل الاختبارات في إصدارات Debug
no-releaseيعطّل الاختبارات في إصدارات Release
no-darwinيعطّل الاختبار على macOS (Darwin)يعتمد الاختبار على ميزات خاصة بـ Linux، مثل distributed queries أو procfs أو خادم HTTP
تُدعَم أيضًا الخيارات التالية: no-polymorphic-parts, no-random-settings, no-random-merge-tree-settings, no-backward-compatibility-check, no-cpu-x86_64, no-cpu-aarch64, no-cpu-ppc64le, no-s3-storage. بالإضافة إلى الإعدادات المذكورة أعلاه، يمكنك استخدام العلامات USE_* من system.build_options لتحديد استخدام ميزات معيّنة في ClickHouse. على سبيل المثال، إذا كان اختبارك يستخدم جدول MySQL، فيجب إضافة الوسم use-mysql.

تحديد حدود الإعدادات العشوائية

يمكن للاختبار تحديد الحدين الأدنى والأقصى المسموح بهما للإعدادات التي يمكن تعيينها عشوائيًا أثناء تشغيل الاختبار. بالنسبة إلى اختبارات .sh، تُكتب الحدود على هيئة تعليق في السطر المجاور للوسوم، أو في السطر الثاني إذا لم يتم تحديد أي وسوم:
#!/usr/bin/env bash
# Tags: no-fasttest
# Random settings limits: max_block_size=(1000, 10000); index_granularity=(100, None)
بالنسبة إلى اختبارات .sql، توضع الوسوم على شكل تعليق SQL في السطر المجاور لها أو في السطر الأول:
-- Tags: no-fasttest
-- Random settings limits: max_block_size=(1000, 10000); index_granularity=(100, None)
SELECT 1
إذا كنت تحتاج إلى تحديد حدّ واحد فقط، يمكنك استخدام None للحدّ الآخر.

اختيار اسم الاختبار

يبدأ اسم الاختبار ببادئة من خمسة أرقام، يليها اسم وصفي، مثل 00422_hash_function_constexpr.sql. ولاختيار البادئة، ابحث عن أكبر بادئة موجودة بالفعل في المجلد، ثم زِدها بمقدار واحد.
ls tests/queries/0_stateless/[0-9]*.reference | tail -n 1
في هذه الأثناء، قد تُضاف اختبارات أخرى بالبادئة الرقمية نفسها، وهذا لا يسبب أي مشكلة، ولن تحتاج إلى تغييره لاحقًا.

التحقق من حدوث خطأ متوقّع

أحيانًا قد ترغب في اختبار حدوث خطأ من الخادم عند تنفيذ استعلام غير صحيح. ندعم لهذا الغرض وسومًا خاصة في اختبارات SQL، وذلك بالشكل التالي:
SELECT x; -- { serverError 49 }
يضمن هذا الاختبار أن الخادم يُرجِع خطأً بالرمز 49 يفيد بوجود العمود غير المعروف x. إذا لم يحدث أي خطأ، أو كان الخطأ مختلفًا، فسيفشل الاختبار. إذا كنت تريد التأكد من حدوث خطأ على جانب العميل، فاستخدم الوسم clientError بدلًا من ذلك. لا تتحقق من صياغة معيّنة لرسالة الخطأ، لأنها قد تتغير في المستقبل، وقد يؤدي ذلك إلى فشل الاختبار دون داعٍ. تحقق فقط من رمز الخطأ. إذا لم يكن رمز الخطأ الحالي دقيقًا بما يكفي لاحتياجاتك، ففكّر في إضافة رمز جديد.

اختبار استعلام موزّع

إذا كنت تريد استخدام الاستعلامات الموزّعة في الاختبارات الوظيفية، فيمكنك الاستفادة من دالة الجدول remote مع العناوين 127.0.0.{1..2} لكي يجري الخادم استعلامًا على نفسه؛ أو يمكنك استخدام عناقيد الاختبار المعرّفة مسبقًا في ملف إعدادات الخادم، مثل test_shard_localhost. تذكّر إضافة الكلمتين shard أو distributed إلى اسم الاختبار، بحيث يُشغَّل في CI ضمن الإعدادات الصحيحة التي يكون فيها الخادم مهيّأً لدعم الاستعلامات الموزّعة.

العمل مع الملفات المؤقتة

قد تحتاج أحيانًا، في اختبار shell، إلى إنشاء ملف أثناء التنفيذ للعمل عليه. ضع في اعتبارك أن بعض عمليات التحقق في CI تُشغِّل الاختبارات بالتوازي، لذا إذا كنت تُنشئ ملفًا مؤقتًا أو تحذفه في البرنامج النصي من دون اسم فريد، فقد يتسبب ذلك في فشل بعض عمليات التحقق في CI، مثل Flaky. ولتفادي ذلك، ينبغي استخدام متغير البيئة $CLICKHOUSE_TEST_UNIQUE_NAME لمنح الملفات المؤقتة اسمًا فريدًا للاختبار الجاري تشغيله. وبذلك يمكنك التأكد من أن الملف الذي تُنشئه أثناء الإعداد أو تحذفه أثناء التنظيف هو الملف المستخدم فقط من قِبل ذلك الاختبار، وليس من قِبل اختبار آخر يعمل بالتوازي.

الأخطاء المعروفة

إذا كنا نعرف بعض الأخطاء التي يمكن إعادة إنتاجها بسهولة من خلال الاختبارات الوظيفية، فنضع اختبارات وظيفية مُعَدّة مسبقًا في الدليل tests/queries/bugs. وتُنقل هذه الاختبارات إلى tests/queries/0_stateless عند إصلاح الأخطاء.

اختبارات التكامل

تتيح اختبارات التكامل اختبار ClickHouse في إعداد عنقودي، وكذلك اختبار تفاعل ClickHouse مع خوادم أخرى مثل MySQL وPostgres وMongoDB. وهي مفيدة لمحاكاة انقسامات الشبكة وفقدان الحزم وما إلى ذلك. تُشغَّل هذه الاختبارات ضمن Docker، وتُنشئ عدة حاويات تضم برمجيات متنوعة. راجع tests/integration/README.md لمعرفة كيفية تشغيل هذه الاختبارات. لاحظ أن تكامل ClickHouse مع برامج تشغيل الجهات الخارجية لا يُختبَر. كما أننا لا نملك حاليًا اختبارات تكامل لبرنامجي تشغيل JDBC وODBC الخاصين بنا.

اختبارات الوحدة

تكون اختبارات الوحدة مفيدة عندما تريد اختبار مكتبة أو فئة معزولة واحدة، لا ClickHouse بالكامل. يمكنك تمكين بناء الاختبارات أو تعطيله باستخدام خيار CMake ‏ENABLE_TESTS. توجد اختبارات الوحدة (وبرامج الاختبار الأخرى) في الأدلة الفرعية tests في أنحاء الشيفرة البرمجية. لتشغيل اختبارات الوحدة، اكتب ninja test. تستخدم بعض الاختبارات gtest، بينما يكون بعضها الآخر مجرد برامج تُرجع رمز خروج غير صفري عند فشل الاختبار. ليس من الضروري وجود اختبارات وحدة إذا كانت الشيفرة البرمجية مشمولة بالفعل بالاختبارات الوظيفية (وعادةً ما تكون الاختبارات الوظيفية أبسط بكثير في الاستخدام). يمكنك تشغيل فحوصات gtest الفردية باستدعاء الملف التنفيذي مباشرةً، على سبيل المثال:
$ ./src/unit_tests_dbms --gtest_filter=LocalAddress*

اختبارات الأداء

تتيح اختبارات الأداء قياس ومقارنة أداء جزء معيّن ومعزول من ClickHouse باستخدام استعلامات اصطناعية. توجد اختبارات الأداء في tests/performance/. ويُمثَّل كل اختبار بملف .xml يصف حالة الاختبار. تُشغَّل الاختبارات باستخدام الأداة docker/test/performance-comparison. راجع ملف readme لمعرفة طريقة التشغيل. يُنفِّذ كل اختبار استعلامًا واحدًا أو عدة استعلامات (وقد تكون مع توليفات من المعلمات) ضمن حلقة. إذا كنت تريد تحسين أداء ClickHouse في سيناريو معيّن، وكان بالإمكان ملاحظة التحسينات من خلال استعلامات بسيطة، فمن الموصى به بشدة كتابة اختبار أداء. ويُوصى أيضًا بكتابة اختبارات أداء عند إضافة دوال SQL أو تعديلها إذا كانت معزولة نسبيًا وليست غامضة جدًا. ومن المفيد دائمًا استخدام perf top أو أدوات perf الأخرى أثناء اختباراتك.

أدوات الاختبار والبرامج النصية

بعض البرامج الموجودة في الدليل tests ليست اختبارات جاهزة، بل أدوات للاختبار. فعلى سبيل المثال، بالنسبة إلى Lexer توجد أداة src/Parsers/tests/lexer لا تقوم إلا بتقسيم stdin إلى رموز، ثم تكتب النتيجة الملوّنة إلى stdout. يمكنك استخدام هذا النوع من الأدوات كأمثلة على الشيفرة، وللاستكشاف والاختبار اليدوي.

اختبارات متفرقة

توجد اختبارات لنماذج التعلّم الآلي في tests/external_models. هذه الاختبارات غير محدَّثة ويجب نقلها إلى اختبارات التكامل. يوجد اختبار منفصل لعمليات الإدخال وفق النصاب. يشغّل هذا الاختبار عنقود ClickHouse على خوادم منفصلة ويحاكي حالات فشل متنوعة: انقسام الشبكة، وفقدان الحزم (بين عُقد ClickHouse، وبين ClickHouse وZooKeeper، وبين خادم ClickHouse والعميل، وما إلى ذلك)، وkill -9 وkill -STOP وkill -CONT، على غرار Jepsen. ثم يتحقق الاختبار من أن جميع عمليات الإدخال التي تم الإقرار بها قد كُتبت، وأن جميع عمليات الإدخال المرفوضة لم تُكتب.

الاختبار اليدوي

عند تطوير ميزة جديدة، فمن المعقول أيضًا اختبارها يدويًا. يمكنك القيام بذلك باتباع الخطوات التالية: قم ببناء ClickHouse. شغّل ClickHouse من الطرفية: انتقل إلى الدليل programs/clickhouse-server ثم شغّله باستخدام ./clickhouse-server. سيستخدم الإعدادات (config.xml وusers.xml والملفات الموجودة داخل الدليلين config.d وusers.d) من الدليل الحالي افتراضيًا. للاتصال بخادم ClickHouse، شغّل programs/clickhouse-client/clickhouse-client. لاحظ أن جميع أدوات clickhouse (الخادم، والعميل، وما إلى ذلك) ليست سوى روابط رمزية لملف binary واحد باسم clickhouse. يمكنك العثور على ملف binary هذا في programs/clickhouse. يمكن أيضًا استدعاء جميع الأدوات بصيغة clickhouse tool بدلًا من clickhouse-tool. بدلًا من ذلك، يمكنك تثبيت حزمة ClickHouse: إما إصدار stable من ClickHouse repository، أو بناء الحزمة بنفسك باستخدام ./release في جذر ClickHouse sources. ثم ابدأ الخادم باستخدام sudo clickhouse start (أو stop لإيقاف الخادم). ابحث عن logs في /etc/clickhouse-server/clickhouse-server.log. عندما يكون ClickHouse مثبتًا بالفعل على نظامك، يمكنك بناء ملف binary جديد باسم clickhouse واستبدال ملف binary الحالي:
$ sudo clickhouse stop
$ sudo cp ./clickhouse /usr/bin/
$ sudo clickhouse start
يمكنك أيضًا إيقاف خدمة clickhouse-server الخاصة بالنظام وتشغيل نسختك الخاصة باستخدام الإعدادات نفسها، ولكن مع إخراج السجلات إلى الطرفية:
$ sudo clickhouse stop
$ sudo -u clickhouse /usr/bin/clickhouse server --config-file /etc/clickhouse-server/config.xml
مثال باستخدام gdb:
$ sudo -u clickhouse gdb --args /usr/bin/clickhouse server --config-file /etc/clickhouse-server/config.xml
إذا كانت خدمة clickhouse-server تعمل بالفعل ولا تريد إيقافها، فيمكنك تغيير أرقام المنافذ في ملف config.xml (أو إعادة تعريفها في ملف داخل دليل config.d)، وتحديد مسار البيانات المناسب، ثم تشغيله. يكاد الملف التنفيذي clickhouse لا يعتمد على أي تبعيات، ويعمل على مجموعة واسعة من توزيعات Linux. ولإجراء اختبار سريع ومبدئي لتغييراتك على خادم، يمكنك ببساطة استخدام scp لنسخ الملف التنفيذي clickhouse الذي بنيته حديثًا إلى خادمك، ثم تشغيله كما في الأمثلة أعلاه.

اختبارات البناء

تتيح اختبارات البناء التحقق من أن عملية البناء لا تتعطل عند استخدام تهيئات بديلة متنوعة وعلى بعض الأنظمة الأخرى. وهذه الاختبارات مؤتمتة أيضًا. أمثلة:
  • الترجمة العابرة إلى Darwin x86_64 ‏(macOS)
  • الترجمة العابرة إلى FreeBSD x86_64
  • الترجمة العابرة إلى Linux AArch64
  • البناء على Ubuntu باستخدام مكتبات من حزم النظام (غير مستحسن)
  • البناء مع الربط الديناميكي للمكتبات (غير مستحسن)
فعلى سبيل المثال، يُعدّ البناء باستخدام حزم النظام ممارسة غير جيدة، لأننا لا نستطيع ضمان الإصدار الدقيق للحزم المتوفر على أي نظام. لكن هذا مطلوب فعلًا من القائمين على صيانة Debian. ولهذا السبب، علينا على الأقل دعم هذا النمط من البناء. ومثال آخر: الربط الديناميكي مصدر شائع للمشكلات، لكنه مطلوب لبعض المهتمين. ومع أننا لا نستطيع تشغيل جميع الاختبارات على جميع أنواع البناء، فإننا نريد على الأقل التحقق من أن تنويعات البناء المختلفة لا تتعطل. ولهذا الغرض نستخدم اختبارات البناء. ونختبر أيضًا عدم وجود وحدات ترجمة طويلة أكثر من اللازم بحيث يصعب تجميعها أو تتطلب مقدارًا كبيرًا جدًا من RAM. ونختبر أيضًا عدم وجود إطارات مكدس كبيرة أكثر من اللازم.

اختبار توافق البروتوكول

عندما نوسّع بروتوكول شبكة ClickHouse، نختبر يدويًا أن clickhouse-client القديم يعمل مع clickhouse-server الجديد، وأن clickhouse-client الجديد يعمل مع clickhouse-server القديم (وذلك ببساطة عبر تشغيل الملفات التنفيذية من الحزم المقابلة). كما نختبر بعض الحالات تلقائيًا باستخدام اختبارات التكامل:
  • ما إذا كان يمكن للنسخة الجديدة قراءة البيانات التي كتبتها نسخة قديمة من ClickHouse بنجاح؛
  • وما إذا كانت الاستعلامات الموزعة تعمل في عنقود يضم إصدارات مختلفة من ClickHouse.

مساعدة من المترجم البرمجي

يُبنى كود ClickHouse الأساسي (الموجود في الدليل src) باستخدام -Wall -Wextra -Werror، إلى جانب بعض التحذيرات الإضافية المفعّلة. لكن هذه الخيارات ليست مفعّلة لمكتبات الطرف الثالث. يوفّر Clang تحذيرات مفيدة أكثر — يمكنك استعراضها باستخدام -Weverything واختيار ما يلائم البناء الافتراضي. نحن نستخدم دائمًا clang لبناء ClickHouse، سواء للتطوير أو للإنتاج. يمكنك إجراء البناء على جهازك باستخدام وضع debug (للحفاظ على بطارية حاسوبك المحمول)، لكن يُرجى ملاحظة أن المترجم البرمجي يمكنه توليد المزيد من التحذيرات مع -O3 بفضل تحسين تحليل تدفّق التحكم والتحليل بين الإجراءات. وعند البناء باستخدام clang في وضع debug، يُستخدم إصدار debug من libc++، ما يتيح اكتشاف المزيد من الأخطاء في وقت التشغيل.

أدوات اكتشاف الأخطاء

إذا تعطلت العملية (خادم ClickHouse أو العميل) عند بدء التشغيل أثناء تشغيلها محليًا، فقد تحتاج إلى تعطيل العشوائية في تخطيط مساحة العناوين: sudo sysctl kernel.randomize_va_space=0

AddressSanitizer

نُجري الاختبارات الوظيفية، واختبارات التكامل، واختبارات الإجهاد، واختبارات الوحدة باستخدام ASan مع كل عملية commit.

Thread sanitizer

نشغّل الاختبارات الوظيفية، واختبارات التكامل، واختبارات الإجهاد، واختبارات الوحدة باستخدام TSan مع كل commit.

أداة فحص الذاكرة

نُشغِّل الاختبارات الوظيفية، واختبارات التكامل، واختبارات إجهاد، واختبارات الوحدة باستخدام MSan مع كل عملية commit.

أداة كشف السلوك غير المعرّف

نجري الاختبارات الوظيفية، واختبارات التكامل، واختبارات إجهاد، واختبارات الوحدة باستخدام UBSan مع كل commit. لا تخضع شيفرة بعض مكتبات الطرف الثالث لفحص UB.

Valgrind (memcheck)

كنا نُشغّل الاختبارات الوظيفية باستخدام Valgrind ليلًا، لكننا لم نعد نفعل ذلك. يستغرق ذلك عدة ساعات. يوجد حاليًا إنذار إيجابي كاذب واحد معروف في مكتبة re2، راجع هذه المقالة.

التشويش

يُنفَّذ التشويش في ClickHouse باستخدام كلٍّ من libFuzzer واستعلامات SQL العشوائية. ويجب إجراء جميع اختبارات Fuzz باستخدام sanitizers ‏(Address وUndefined). يُستخدم LibFuzzer لإجراء اختبارات Fuzz معزولة لكود المكتبات. تُنفَّذ Fuzzers كجزء من كود الاختبار، وتحمل اللاحقة “_fuzzer” في أسمائها. يمكن العثور على Example لأحد Fuzzers في src/Parsers/fuzzers/lexer_fuzzer.cpp. تُخزَّن إعدادات LibFuzzer الخاصة وDictionaries وcorpus في tests/fuzz. ونشجّعك على كتابة اختبارات Fuzz لكل وظيفة تتعامل مع مدخلات المستخدم. لا يتم بناء Fuzzers افتراضيًا. ولبناء Fuzzers، يجب ضبط الخيارين -DENABLE_FUZZING=1 و-DENABLE_TESTS=1. ونوصي بتعطيل Jemalloc أثناء بناء Fuzzers. يمكن العثور على configuration المستخدمة لدمج التشويش في ClickHouse مع Google OSS-Fuzz في docker/fuzz. ونستخدم أيضًا اختبار Fuzz بسيطًا لتوليد استعلامات SQL عشوائية والتحقق من أن server لا يتعطل أثناء تنفيذها. يمكنك العثور عليه في 00746_sql_fuzzy.pl. ويجب تشغيل هذا الاختبار باستمرار (طوال الليل ولمدد أطول). ونستخدم أيضًا Query Fuzzer متقدمًا قائمًا على AST، وقادرًا على اكتشاف عدد هائل من الحالات الطرفية. فهو يُجري تبديلات وتبديلات substitution عشوائية في AST الخاصة بالاستعلامات. كما يتذكر عُقد AST من الاختبارات السابقة لاستخدامها في التشويش للاختبارات اللاحقة، مع معالجتها بترتيب عشوائي. يمكنك معرفة المزيد عن هذا fuzzer في هذه المقالة على المدونة.

اختبار الإجهاد

اختبارات الإجهاد هي حالة أخرى من التشويش. ويشغّل هذا الاختبار جميع الاختبارات الوظيفية بالتوازي وبترتيب عشوائي على خادم واحد. ولا يتم التحقق من نتائج الاختبارات. ويُتحقَّق مما يلي:
  • ألّا يتعطل الخادم، وألّا يتم تفعيل أي مصائد Debug أو sanitizer؛
  • عدم وجود حالات توقف متبادل؛
  • أن تكون بنية قاعدة البيانات متسقة؛
  • أن يتمكن الخادم من التوقف بنجاح بعد الاختبار ثم البدء مجددًا دون استثناءات.
هناك خمسة أنواع (Debug, ASan, TSan, MSan, UBSan).

Thread fuzzer

يُعد Thread Fuzzer (يرجى عدم الخلط بينه وبين Thread Sanitizer) نوعًا آخر من الاختبار بالتشويش يتيح جعل ترتيب تنفيذ الخيوط عشوائيًا. ويساعد ذلك في اكتشاف المزيد من الحالات الخاصة.

تدقيق أمني

أجرى فريق الأمن لدينا مراجعة أولية لإمكانات ClickHouse من منظور أمني.

أدوات التحليل الساكن

نشغّل clang-tidy مع كل commit. كما أن فحوصات clang-static-analyzer مفعّلة أيضًا. ويُستخدم clang-tidy كذلك لبعض فحوصات الأسلوب. لقد قيّمنا clang-tidy وCoverity وcppcheck وPVS-Studio وtscancode وCodeQL. ستجد تعليمات الاستخدام في الدليل tests/instructions/. إذا كنت تستخدم CLion بوصفه بيئة تطوير متكاملة، فيمكنك الاستفادة مباشرةً من بعض فحوصات clang-tidy الجاهزة. كما نستخدم shellcheck للتحليل الساكن لبرامج shell النصية.

التحصين

في بنية debug، نستخدم allocator مخصصًا يطبّق ASLR على تخصيصات مستوى المستخدم. ونحمي أيضًا يدويًا مناطق الذاكرة التي يُفترض أن تصبح readonly بعد التخصيص. في بنية debug، نستخدم أيضًا تخصيصًا لـ libc يضمن عدم استدعاء أي دوال “ضارة” (مهجورة، غير آمنة، غير thread-safe). تُستخدم تأكيدات Debug على نطاق واسع. في بنية debug، إذا تم إطلاق استثناء بالرمز “logical error” (مما يشير إلى وجود خلل برمجي)، فسيُنهي البرنامج عمله مبكرًا. وهذا يتيح استخدام الاستثناءات في بنية release، مع التعامل معها كتأكيد في بنية debug. تُستخدم نسخة Debug من jemalloc في بنيات debug. وتُستخدم نسخة Debug من libc++ في بنيات debug.

فحوصات سلامة البيانات أثناء التشغيل

تُحمى البيانات المخزنة على القرص باستخدام checksums. وتُحمى البيانات في جداول MergeTree باستخدام checksums بثلاث طرق متزامنة* (كتل البيانات المضغوطة، وكتل البيانات غير المضغوطة، وإجمالي checksum عبر الكتل). كما تُحمى البيانات المنقولة عبر الشبكة بين client وserver أو بين الخوادم باستخدام checksums. ويضمن النسخ المتماثل تطابق البيانات على مستوى البِتّات في replicas. وهذا ضروري للحماية من أعطال العتاد (تلف البِتّات على وسائط التخزين، وانقلاب البِتّات في RAM على server، وانقلاب البِتّات في RAM في وحدة تحكم الشبكة، وانقلاب البِتّات في RAM في مبدّل الشبكة، وانقلاب البِتّات في RAM لدى client، وانقلاب البِتّات على wire). لاحظ أن انقلاب البِتّات شائع، ومن المرجّح أن يحدث حتى مع ECC RAM ومع وجود checksums الخاصة بـ TCP (إذا كنت تدير آلاف الخوادم التي تعالج بيتابايتات من البيانات يوميًا). شاهد الفيديو (بالروسية). يوفّر ClickHouse أدوات تشخيص تساعد مهندسي العمليات على اكتشاف العتاد المعيب.
  • وهو ليس بطيئًا.

نمط الشيفرة

قواعد نمط الشيفرة موضحة هنا. للتحقق من بعض مخالفات النمط الشائعة، يمكنك استخدام السكربت utils/check-style. لفرض نمط الشيفرة الصحيح، يمكنك استخدام clang-format. يوجد الملف .clang-format في جذر الشيفرة المصدرية. وهو يتوافق في الغالب مع نمط الشيفرة المعتمد لدينا. لكن لا يُنصح بتطبيق clang-format على الملفات الموجودة مسبقًا لأنه قد يجعل التنسيق أسوأ. يمكنك استخدام الأداة clang-format-diff التي يمكنك العثور عليها في مستودع clang المصدري. بدلًا من ذلك، يمكنك تجربة الأداة uncrustify لإعادة تنسيق الشيفرة. يوجد ملف الإعدادات uncrustify.cfg في جذر الشيفرة المصدرية. وهي أقل اختبارًا من clang-format. يمتلك CLion منسّق شيفرة خاصًا به، ويجب ضبطه ليتوافق مع نمط الشيفرة لدينا.

تغطية الاختبارات

كما نتتبّع تغطية الاختبارات، لكن للاختبارات الوظيفية فقط، ولـ clickhouse-server فقط. ويتم ذلك يوميًا.

اختبارات خاصة بالاختبارات

يوجد تحقّق آلي من الاختبارات غير المستقرة. ويشغّل جميع الاختبارات الجديدة 100 مرة (بالنسبة إلى الاختبارات الوظيفية) أو 10 مرات (بالنسبة إلى اختبارات التكامل). إذا فشل الاختبار حتى مرة واحدة، فيُعد اختبارًا غير مستقر.

أتمتة الاختبارات

نُجري الاختبارات باستخدام GitHub Actions. تُشغَّل مهام البناء والاختبارات في Sandbox لكل commit. تُنشر الحزم الناتجة ونتائج الاختبارات على GitHub، ويمكن تنزيلها عبر روابط مباشرة. تُحفَظ الملفات الناتجة لعدة أشهر. عندما ترسل طلب سحب على GitHub، نضع عليه الوسم “can be tested”، ثم يبني نظام CI لدينا حزم ClickHouse لك (إصدار release، وإصدار debug، ومع address sanitizer، وغير ذلك).
آخر تعديل في ٢٥ يونيو ٢٠٢٦