الدوال المعرّفة من قبل المستخدم في WebAssembly
نظرة عامة
wasm32-unknown-unknown) من دون أي تبعيات على نظام تشغيل أو مكتبة قياسية. كما لا يكون مدعومًا إلا هدف WebAssembly الافتراضي ذو 32 بت (من دون امتداد wasm64).
يجب أن تلتزم الوحدة بأحد بروتوكولات الاتصال (ABIs) المدعومة للتفاعل مع ClickHouse.
بعد التجميع، يُحمَّل الكود الثنائي للوحدة إلى ClickHouse عبر إدراجه في جدول system.webassembly_modules.
بعد ذلك، يمكنك إنشاء UDFs تشير إلى الدوال التي تُصدِّرها الوحدة باستخدام العبارة CREATE FUNCTION ... LANGUAGE WASM.
المتطلبات الأساسية
البدء السريع
wat2wasm من WebAssembly Binary Toolkit (WABT) أو الأمر parse من wasm-tools.
FORMAT RawBlob لإدراجها في جدول system.webassembly_modules.
ثم نعرّف دالة UDF تشير إلى الدالة steps التي تصدّرها هذه الوحدة:
::، لأنه يختلف عن اسم UDF.
يمكننا الآن استخدام الدالة collatz_steps في استعلاماتنا:
number صراحةً إلى UInt32، لأن دوال WebAssembly تتطلب تطابقًا تامًا مع أنواع البيانات المحددة في التوقيع ضمن تعليمة CREATE FUNCTION.
في النتيجة، حصلنا على متتالية خطوات Collatz للأعداد من 1 إلى 100، وهي تقابل المتتالية A006577 from the OEIS.
إدارة وحدات WASM عبر جدول النظام
system.webassembly_modules بالبنية التالية:
- الأعمدة
nameString — اسم الوحدة. يجب ألا يكون فارغًا، وأن يقتصر على محارف الكلمات فقط.codeString — شيفرة WASM الثنائية الخام. للكتابة فقط؛ وتُرجع عمليات القراءة سلسلة فارغة.hashUInt256 — قيمة SHA256 للملف الثنائي للوحدة (تكون صفرًا إذا كانت موجودة على القرص ولكن لم تُحمَّل بعد).
إضافة وحدة
توزيع وحدة عبر عنقود
system.webassembly_modules هو جدول خاص بكل مثيل — لا تصل عملية INSERT إلا إلى النسخة المتماثلة التي تتولى الاتصال. ولا توجد صيغة ON CLUSTER لتعليمة INSERT، لذا سيفشل CREATE FUNCTION ... ON CLUSTER لاحقًا على النسخ المتماثلة التي لا تحتوي على الوحدة:
insert على جميع العقد، اكتب إلى دالة الجدول cluster بدلًا من الجدول المحلي system.webassembly_modules:
يعتمد هذا النمط على أن يمرّ مسار الكتابة الموزعة الأساسي على كل replica داخل كل shard، وهذا لا يحدث إلا عندما يكون العنقود مضبوطًا على
internal_replication=false. عند استخدام internal_replication=true (وهو الإعداد default في العناقيد التي تستخدم ReplicatedMergeTree لتتولى النسخ المتماثل بنفسها)، تُرسَل عملية insert إلى replica سليمة واحدة فقط لكل shard، ولا يجري نسخ system.webassembly_modules عبر هذا المسار — لذا ستبقى بعض الـ replicas من دون الوحدة. في هذا الإعداد، تحتاج إلى تنفيذ insert على كل replica على حدة، على سبيل المثال بالتكرار على system.clusters والكتابة باستخدام remote(...) لكل host، أو بنسخ الملف binary إلى user_scripts/wasm/ على كل host.يمكنك التحقق من internal_replication لعنقود معيّن باستخدام SELECT cluster, shard_num, internal_replication FROM system.clusters.insert المتشعب، تصبح الوحدة موجودة على كل replica وينجح CREATE FUNCTION ... ON CLUSTER:
clusterAllReplicas:
system.webassembly_modules غير مؤثرة عند تكرارها بالنسبة إلى زوج (name, hash) نفسه، لذا فإن إعادة تنفيذ عملية الإدراج الموزعة آمنة وتُعد وسيلة مناسبة لإصلاح الحالة بعد استبدال نسخة متماثلة. لاحظ أن الخوادم المُضافة حديثًا لا تتلقى الوحدات الموجودة بأثر رجعي — يجب عليك إعادة تنفيذ عملية الإدراج على العنقود المُحدَّث، أو وضع الملف التنفيذي في الدليل user_scripts/wasm/ على المضيف الجديد.
عرض الوحدات
حذف وحدة
DELETE FROM system.webassembly_modules WHERE name = '...'.
يجب أن يكون شرط التصفية إما name = 'literal' لإجراء تطابق تام، أو name LIKE 'pattern' لحذف كل وحدة يطابق اسمها النمط؛ ولا تُقبل أي صيغ أخرى.
إنشاء دالة UDF باستخدام WebAssembly
function_name: اسم الدالة في ClickHouse. قد يختلف عن اسم الدالة المُصدَّرة في الوحدة.FROM 'module_name' :: 'source_function_name': اسم وحدة WASM المُحمَّلة واسم الدالة في وحدة WASM المراد استخدامها (تكون القيمة الافتراضيةfunction_name)ARGUMENTS: قائمة بأسماء الوسائط وأنواعها (الأسماء اختيارية وتُستخدم مع تنسيقات التسلسل التي تدعم الحقول المُسمّاة)ABI: إصدار واجهة الثنائية للتطبيقاتROW_DIRECT: مواءمة مباشرة للأنواع، مع معالجة صفًا بصفBUFFERED_V1: معالجة قائمة على الكتل مع التسلسلASSEMBLYSCRIPT: معالجة صفًا بصف للوحدات الناتجة عن مصرّف AssemblyScript. تُطابَق الأنواع الرقمية مع الأنواع البدائية في AssemblyScript؛ ويُطابَقStringفي ClickHouse معstringفي AssemblyScript.
DETERMINISTIC: يصرّح بأن الدالة حتمية — أي إنها تعيد دائمًا الناتج نفسه للمدخلات نفسها. عند تحديد ذلك، قد يطوي ClickHouse الاستدعاءات الثابتة التي تكون فيها جميع الوسائط ثوابت: تُقيَّم الدالة مرة واحدة في مرحلة تحليل الاستعلام، ثم يُعاد استخدام النتيجة لكل صف.SHA256_HASH: قيمة التجزئة المتوقعة للوحدة للتحقق منها (تُملأ تلقائيًا إذا أُهملت)، ويمكن استخدامها لضمان تحميل وحدة WASM الصحيحة عبر النسخ المتماثلة المختلفة.SETTINGS: إعدادات خاصة بكل دالةserialization_formatString — تنسيق التسلسل الذي يتطلبه ABI. القيم المدعومة:MsgPackوJSONEachRowوCSVوTSVوTSVRawوRowBinaryوBuffers. القيمة الافتراضية:MsgPack. يجب أن تُرجع التنسيقات المعتمدة على الكتل مثلBuffersعمودًا واحدًا يطابق نوعه توقيع الدالة المُصرَّح به.webassembly_udf_enable_fuelBool — يفعّل حصة fuel محدودة للدالة. القيمة الافتراضية:true. عند ضبطه علىfalse، يُتجاهل إعداد الاستعلامwebassembly_udf_max_fuelلهذه الدالة. قد يؤدي تعطيل حدود fuel إلى تحسين الأداء عند استخدام المحركwasmtime. ومع ذلك، بالنسبة إلى شيفرة الضيف غير الموثوق بها أو التي تحتوي على أخطاء، فقد يزيد ذلك من خطر استمرار التنفيذ بلا قيود.
إصدارات ABI
ROW_DIRECT: تعيين مباشر للأنواع (الأنواع البدائيةInt32وUInt32وInt64وUInt64وFloat32وFloat64فقط)BUFFERED_V1: أنواع معقدة مع دعم التسلسلASSEMBLYSCRIPT: تكامل صفًا بصف مع وحدات AssemblyScript؛ يدعم الأنواع الرقمية وString.
ABI ROW_DIRECT
- يجب أن تكون الوسائط وأنواع الإرجاع من الأنواع الرقمية
Int32/UInt32/Int64/UInt64/Float32/Float64/Int128/UInt128. - النوع Strings غير مدعوم في واجهة ABI هذه.
- يجب أن تتطابق التواقيع مع ما هو مُصدَّر من WASM (
i32/i64/f32/f64/v128). - لا يلزم أن يصدّر الوحدة أي دوال دعم.
ABI BUFFERED_V1
واجهة ABI هذه تجريبية وقابلة للتغيير في الإصدارات المستقبلية.
i32 وتُرجع قيمة واحدة من النوع i32.
تعالج شيفرة الضيف البيانات وتُرجع مؤشرًا إلى مخزن النتيجة المؤقت الذي يحتوي على بيانات النتيجة المُسلسلة.
يجب أن توفّر شيفرة الضيف دالتين لإنشاء هذه المخازن المؤقتة وتدميرها.
ABI ASSEMBLYSCRIPT
-
رقمية:
Int8/UInt8،Int16/UInt16(تُوسَّع إلىi32عند الحد الفاصل)،Int32/UInt32،Int64/UInt64،Float32،Float64 -
String— تُحوَّل إلىstringفي AssemblyScript (UTF-16 في ذاكرة WASM). ويتولى ClickHouse التحويل بين UTF-8 وUTF-16 تلقائيًا. - لا تُدعَم فئات AssemblyScript المخصّصة كأنواع للوسائط أو الإرجاع، لأن معرّفات الفئات في بيئة التشغيل الخاصة بها ليست مستقرة عبر عمليات الترجمة البرمجية (راجع AssemblyScript#2982).
__new و__pin و__unpin. وتعتمد عليها الآلية القياسية للتعامل مع السلاسل النصية الواردة والصادرة. الاستدعاء الموصى به:
env.abort لأخطاء وقت التشغيل (مثل نفاد الذاكرة وتجاوز الحدود وما إلى ذلك). ويوفّر ClickHouse هذا الاستيراد تلقائيًا: فعند استدعاء abort، يفشل الاستعلام الجاري مع استثناء WASM_ERROR يتضمّن رسالة AssemblyScript بعد فك ترميزها وموضع المصدر.
مثال:
asc وتحميل ملف .wasm الناتج إلى system.webassembly_modules، عرّف UDFs كما يلي:
ملاحظة حول تطوير UDFs في Rust
clickhouse_create_buffer وclickhouse_destroy_buffer يدويًا، وإنما يكفي إضافة الـ crate كاعتمادية. كما تتوفر macro #[clickhouse_wasm_udf] لتغليف دوال Rust العادية بالصيغة المطلوبة لـ ABI.
وباستخدام هذه الـ crate، يمكنك كتابة UDFs على النحو التالي:
serde.
واجهة برمجة التطبيقات المضيفة المتاحة للوحدات
clickhouse_server_version() -> i64— تُرجع إصدار ClickHouse server كعدد صحيح (على سبيل المثال 25011001 للإصدار v25.11.1.1).clickhouse_throw(ptr: i32, size: i32)— يُطلق خطأً بالرسالة المحددة. يقبل مؤشراً إلى موقع الذاكرة الذي يحتوي على سلسلة رسالة الخطأ، بالإضافة إلى حجم السلسلة.clickhouse_log(ptr: i32, size: i32)— يسجل رسالة في السجل النصي لـ ClickHouse server.clickhouse_random(ptr: i32, size: i32)— يملأ الذاكرة ببايتات عشوائية.env.abort(message: i32, fileName: i32, line: i32, column: i32)— متاح للوحدات المتوافقة مع AssemblyScript. يؤدي استدعاؤه (أو التسبب في trap لوقت تشغيل AssemblyScript يستدعيه) إلى إنهاء UDF مع استثناءWASM_ERRORيتضمن الرسالة المفككة وموقع المصدر. ولا تتأثر الوحدات التي لا تستوردenv.abort.
الإعدادات
-
webassembly_udf_max_fuel— حد الوقود لكل عملية تنفيذ لمثيل WebAssembly UDF. تستهلك كل تعليمة في WebAssembly مقدارًا من الوقود. تُضرب القيمة في 1024 قبل تمريرها إلى بيئة التشغيل، لذا فإنwebassembly_udf_max_fuel = 1يعادل تقريبًا 1024 وحدة وقود. عيّنه إلى 0 لإلغاء أي حد فعلي. ينطبق ذلك فقط على الدوال التي تكون قيمة الإعداد الخاص بكل دالةwebassembly_udf_enable_fuelفيها هيtrue، وهذا هو الإعداد الافتراضي. -
webassembly_udf_max_memory— حد الذاكرة بالبايت لكل مثيل WebAssembly UDF. -
webassembly_udf_max_input_block_size— الحد الأقصى لعدد الصفوف التي تُمرَّر إلى WebAssembly UDF في كتلة واحدة. عيّنه إلى 0 لمعالجة جميع الصفوف دفعة واحدة. -
webassembly_udf_max_instances— الحد الأقصى لعدد مثيلات WebAssembly UDF التي يمكن تشغيلها بالتوازي لكل دالة.