Server: MySQL 5.6
Proc Tersimpan:
DELIMITER //
CREATE PROCEDURE slowInsert(IN t int)
BEGIN
SELECT SLEEP(t);
INSERT INTO `table_x` (message) VALUES (UUID());
END //
DELIMITER ;
Buka Kode:
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)
}
}
Kami menghadapi perilaku aneh.
Ketika kita menjalankan slowInsert( 10 )
pada klien mysql (mis. sekuel pro), 10 detik menunggu dihormati sebelum penyisipan. Kami tidak memiliki alasan untuk percaya bahwa proc yang disimpan salah atau tidak valid.
Saat kami menjalankan kode Go di atas, konteksnya dibatalkan setelah 3 detik -- semuanya baik-baik saja.
... Tapi segera penyisipan terjadi. (Tidak menunggu 10 detik).
Sekarang, jika kita mengubah proc yang disimpan menjadi:
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 ;
Kode Go bekerja dengan sempurna dan membatalkan kueri (yaitu tidak ada penyisipan yang terjadi).
Dalam kedua kasus, klien mysql kami (sekuel pro) menghormati masa tunggu sehingga kedua procs yang disimpan setara.
Tetapi dalam kode Go kami, proc tersimpan kedua berfungsi tetapi yang pertama tidak - mengarah pada keyakinan kami bahwa ada beberapa bug aneh pada driver.
@ alessio-palumbo
Ini bukan bug. Kami belum mendukung pembatalan kueri.
Dan SLEEP()
adalah kueri yang sangat istimewa. Ini memeriksa koneksi hidup atau tidak.
Dalam kasus umum, MySQL tidak memeriksa koneksi sampai query selesai.
Readme: 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.
OK, README menyesatkan. Itu hanya membatalkan hasil kueri "menunggu", bukan eksekusi kueri.
Protokol MySQL tidak menyediakan cara yang aman untuk membatalkan eksekusi kueri.
Beberapa CLI mendukung pembatalan, tetapi tidak aman/stabil di beberapa lingkungan.
Terima kasih
Yang harus Anda lakukan adalah menggunakan transaksi. Jika Anda tidak melakukan, itu dibatalkan.
Jadi timeout selama transaksi aman.
@andizzle @alexclifford @edwardhutchison
Mengapa storedProc pertama dimasukkan setelah batas waktu tetapi storedProc kedua tidak dimasukkan. Saya berasumsi bahwa berdasarkan pernyataan Anda, itu dimasukkan atau tidak. Jika driver tidak mendukung pembatalan eksekusi kueri, mengapa sepertinya membatalkan eksekusi?
Ini adalah perilaku MySQL. Saya tidak tahu banyak. Tetapi SLEEP() memiliki perilaku yang sangat aneh ketika koneksi ditutup.
@julienschmidt apa pendapat Anda tentang ide ini: https://github.com/src-d/gitbase-web/issues/241#issuecomment -427740194
Kami mungkin dapat menggunakan
SHOW FULL PROCESSLIST
danKILL
untuk mengirimKILL
ke kueri yang tepat.
Sayangnya itu tidak bisa diimplementasikan dengan mudah di level driver. Ini akan membutuhkan koneksi ke-2 di mana driver menjalankan perintah ini. Itu bisa berupa "koneksi manajemen" bersama atau koneksi yang dibuka sesuai permintaan.
Pertama-tama, ini akan membuat pengemudi jauh lebih kompleks. Saat ini semua penanganan sesi dan koneksi dilakukan oleh paket database/sql
, bukan driver, yang hanya menyediakan fungsi untuk membuka koneksi baru dll.
Tetapi jauh lebih bermasalah adalah, bahwa kita sebenarnya tidak memiliki cara untuk menemukan server yang tepat (kecuali hanya ada satu). Driver sebagian besar digunakan dengan proxy load-balancing mysql. Dalam hal ini, kemungkinan SHOW FULL PROCESSLIST
dijalankan di server yang salah dan kami tidak dapat menemukan kuerinya.
Apakah Anda dapat membuat proposal Go 2 untuk mengubah apa pun yang diperlukan untuk database/sql
agar kueri benar-benar dapat dibatalkan?
Sejujurnya saya berpikir bahwa bukan database/sql
atau driver ini yang menjadi masalah di sini, tetapi MySQL sederhana itu sendiri, yang tidak menyediakan
Awal yang baik adalah jika balasan atas kueri yang dikirim ke server akan berisi id proses.
Dalam kasus penyiapan server tunggal, kita kemudian dapat membatalkan kueri dengan cara yang mirip dengan Postgres : Buka koneksi baru dan segera kirim perintah KILL
.
Kasus dengan beberapa proxy lebih sulit. Saat ini proxy tersebut benar-benar transparan bagi pengemudi. Apa yang diperlukan adalah semacam mekanisme untuk menjamin bahwa koneksi dibuka ke backend yang sama dengan koneksi lain yang ada.
Kami sebenarnya sudah memiliki PR terbuka untuk pendekatan serupa (untuk kasus penyiapan server tunggal): https://github.com/go-sql-driver/mysql/pull/791
Saya akan mengirimkan permintaan fitur dengan MySQL. Jika saya meminta kueri untuk memuat id proses, apakah itu akan membuat perubahan yang tidak kompatibel ke belakang yang akan mereka tolak?
Ini bukan satu-satunya masalah tentang pembatalan. Ada beberapa masalah lain.
Tidak ada protokol "pembatalan jarak jauh yang andal" di bumi.
Ini harus digunakan hanya untuk menyimpan beberapa CPU.
Lagi pula, apa gunanya masalah ini?
Perilaku aneh tidak dihindari dengan menerapkan pembatalan.
Anda tidak dapat mengetahui permintaan "dibatalkan oleh konteks" dijalankan atau dibatalkan di server MySQL. Ini masalah waktu.
Yang bisa Anda lakukan hanyalah menggunakan transaksi; semua permintaan akan dibatalkan.
Jika kami benar-benar membutuhkan masalah untuk "Terapkan permintaan pembatalan", ajukan masalah baru dan tutup ini.
Inilah solusi saya: https://medium.com/@rocketlaunhr.cloud/canceling -mysql-in-go-827ed8f83b30
Komentar yang paling membantu
Sayangnya itu tidak bisa diimplementasikan dengan mudah di level driver. Ini akan membutuhkan koneksi ke-2 di mana driver menjalankan perintah ini. Itu bisa berupa "koneksi manajemen" bersama atau koneksi yang dibuka sesuai permintaan.
Pertama-tama, ini akan membuat pengemudi jauh lebih kompleks. Saat ini semua penanganan sesi dan koneksi dilakukan oleh paket
database/sql
, bukan driver, yang hanya menyediakan fungsi untuk membuka koneksi baru dll.Tetapi jauh lebih bermasalah adalah, bahwa kita sebenarnya tidak memiliki cara untuk menemukan server yang tepat (kecuali hanya ada satu). Driver sebagian besar digunakan dengan proxy load-balancing mysql. Dalam hal ini, kemungkinan
SHOW FULL PROCESSLIST
dijalankan di server yang salah dan kami tidak dapat menemukan kuerinya.