Mysql: سلوك غريب مع إلغاء الاستعلام باستخدام السياق

تم إنشاؤها على ٢ أكتوبر ٢٠١٨  ·  19تعليقات  ·  مصدر: go-sql-driver/mysql

الخادم: MySQL 5.6

بروك المخزن:

DELIMITER //
CREATE PROCEDURE slowInsert(IN t int)
BEGIN       
       SELECT SLEEP(t);
       INSERT INTO `table_x` (message) VALUES (UUID());
END //
DELIMITER ;

كود Go:

package main

import (
    "context"
    "database/sql"
    "time"

    _ "github.com/go-sql-driver/mysql"
)

func main() {
    db, err := sql.Open("mysql", "<url>")
    if err != nil {
        panic(err)
    }
    db.SetConnMaxLifetime(9 * time.Second)
    db.SetMaxIdleConns(12)
    db.SetMaxOpenConns(12)

    ctx := context.Background()
    ctx, cancel := context.WithTimeout(ctx, time.Duration(3)*time.Second)
    defer cancel()

    _, err = db.ExecContext(ctx, "call slowInsert( 10 )") // context will cancel before insert occurs
    if err != nil {
        panic(err)
    }
}

التعليق الأكثر فائدة

قد نتمكن من استخدام SHOW FULL PROCESSLIST و KILL لإرسال KILL إلى الاستعلام الصحيح.

للأسف لا يمكن تنفيذ ذلك على مستوى السائق بسهولة. قد يتطلب اتصالًا ثانيًا ينفذ السائق هذه الأوامر من خلاله. يمكن أن يكون إما "اتصال إدارة" مشترك أو اتصالات مفتوحة عند الطلب.

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

ولكن الأمر الأكثر صعوبة هو أنه ليس لدينا طريقة للعثور على الخادم الصحيح (ما لم يكن هناك خادم واحد فقط). يتم نشر برنامج التشغيل بشكل كبير مع وكلاء mysql لموازنة التحميل. في هذه الحالة ، من المحتمل أن يتم تنفيذ قبعة SHOW FULL PROCESSLIST على الخادم الخطأ ولا يمكننا العثور على الاستعلام.

ال 19 كومينتر

نحن نواجه سلوكًا غريبًا.
عندما نقوم بتشغيل slowInsert( 10 ) على عميل mysql (على سبيل المثال ، Sequel pro) ، يتم احترام الانتظار لمدة 10 ثوانٍ قبل الإدراج. ليس لدينا سبب للاعتقاد بأن العملية المخزنة خاطئة أو غير صالحة.

عندما نقوم بتشغيل كود Go أعلاه ، يتم إلغاء السياق بعد 3 ثوان - كل هذا جيد.
... ولكن يحدث الإدخال على الفور . (لا تنتظر 10 ثوان).

الآن ، إذا قمنا بتغيير proc المخزنة بحيث يكون:

DELIMITER //
CREATE PROCEDURE slowInsert(IN t int)
BEGIN       
       SELECT SLEEP(t/2); // <---- The change
       SELECT SLEEP(t/2); // <---- The change
       INSERT INTO `table_x` (message) VALUES (UUID());
END //
DELIMITER ;

يعمل كود Go بشكل مثالي ويلغي الاستعلام (على سبيل المثال ، لا يحدث الإدراج).
في كلتا الحالتين ، يحترم عميل mysql الخاص بنا (تكملة للمحترفين) فترة الانتظار ، وبالتالي فإن كلا العمليتين المخزنتين متساويتان.

ولكن في كود Go الخاص بنا ، يعمل proc المخزن الثاني ولكن الأول لا يعمل - مما يؤدي إلى إيماننا بوجود خطأ غريب في السائق.

@ alessio-palumbo

إنه ليس خطأ. لا ندعم إلغاء الاستعلام حتى الآن.
و SLEEP() هو استعلام خاص جدًا. يتحقق من الاتصال على قيد الحياة أم لا.
بشكل عام ، لا تتحقق MySQL من الاتصال حتى ينتهي الاستعلام.

الملف التمهيدي: https://github.com/go-sql-driver/mysql#contextcontext -support

Go 1.8 added database/sql support for context.Context. This driver supports query timeouts and cancellation via contexts. See context support in the database/sql package for more details.

حسنًا ، README مضلل. إنها فقط تلغي نتيجة الاستعلام "قيد الانتظار" ، وليس تنفيذ الاستعلام.

لا يوفر بروتوكول MySQL طريقة آمنة لإلغاء تنفيذ الاستعلام.
تدعم بعض CLI الإلغاء ، لكنها ليست آمنة / مستقرة في بعض البيئات.

شكرا

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

andizzlealexcliffordedwardhutchison

لماذا يتم إدراج storeProc لأول مرة بعد انتهاء المهلة ولكن لا يتم إدراج storeProc الثاني. أفترض أنه بناءً على بيانك ، إما أنه يُدرج أو لا يُدرج. إذا كان برنامج التشغيل لا يدعم إلغاء تنفيذ الاستعلام ، فلماذا يبدو أنه يلغي التنفيذ؟

إنه سلوك MySQL. لا اعرف الكثير. لكن SLEEP () له سلوك غريب جدًا عند إغلاق الاتصال.

julienschmidt ما رأيك في هذه الفكرة: https://github.com/src-d/gitbase-web/issues/241#issuecomment -427740194

قد نتمكن من استخدام SHOW FULL PROCESSLIST و KILL لإرسال KILL إلى الاستعلام الصحيح.

للأسف لا يمكن تنفيذ ذلك على مستوى السائق بسهولة. قد يتطلب اتصالًا ثانيًا ينفذ السائق هذه الأوامر من خلاله. يمكن أن يكون إما "اتصال إدارة" مشترك أو اتصالات مفتوحة عند الطلب.

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

ولكن الأمر الأكثر صعوبة هو أنه ليس لدينا طريقة للعثور على الخادم الصحيح (ما لم يكن هناك خادم واحد فقط). يتم نشر برنامج التشغيل بشكل كبير مع وكلاء mysql لموازنة التحميل. في هذه الحالة ، من المحتمل أن يتم تنفيذ قبعة SHOW FULL PROCESSLIST على الخادم الخطأ ولا يمكننا العثور على الاستعلام.

هل ستكون قادرًا على تقديم اقتراح Go 2 لتعديل ما هو مطلوب إلى database/sql لجعل الاستعلام قابلاً للإلغاء حقًا؟

أعتقد بصدق أن المشكلة هنا ليست database/sql أو برنامج التشغيل هذا ، ولكن MySQL بسيط نفسه ، والذي لا يوفر

ستكون البداية الجيدة إذا احتوت الرد على الاستعلامات المرسلة إلى الخادم على معرف العملية.

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

الحالة مع بعض الوكيل أكثر صعوبة. الآن هذه الوكلاء شفافة تمامًا للسائق. ما هو مطلوب هو نوع من الآلية لضمان فتح اتصال لنفس الواجهة الخلفية لاتصال آخر موجود.

لدينا بالفعل علاقات عامة مفتوحة لنهج مماثل (لحالة إعداد الخادم الفردي) بالفعل: https://github.com/go-sql-driver/mysql/pull/791

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

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

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

إذا كنا بحاجة فعلاً لمشكلة "تنفيذ استعلام الإلغاء" ، فقم بتقديم مشكلة جديدة وأغلق هذا.

إليكم الحل: https://medium.com/@rocketlaunchr.cloud/canceling -mysql-in-go-827ed8f83b30

هل كانت هذه الصفحة مفيدة؟
0 / 5 - 0 التقييمات