Keterbatasan itu perlu dilanggar atau bagaimana kami mempercepat tes fungsional tiga kali

gambar

Tes fungsional adalah hal yang bermanfaat. Pada awalnya, mereka tidak membutuhkan banyak waktu, tetapi proyek ini terus berkembang, dan semakin banyak tes yang diperlukan. Kami tidak bermaksud mentolerir perlambatan dalam kecepatan pengiriman, dan, mengumpulkan kekuatan kami, kami mempercepat tes fungsional tiga kali. Dalam artikel ini Anda akan menemukan tips universal, namun, Anda akan melihat efek khusus pada proyek-proyek besar.

Secara singkat tentang aplikasi


Tim saya sedang mengembangkan API publik yang menyediakan data untuk pengguna 2GIS. Ketika Anda pergi ke 2gis.ru dan mencari "Supermarket", Anda mendapatkan daftar organisasi - ini adalah data dari API kami. Pada 2000+ RPS kami, hampir setiap masalah menjadi kritis jika beberapa jenis fungsi rusak.

Aplikasi ini ditulis dalam Scala, tes ditulis dalam PHP, database-nya adalah PostgreSQL-9.4. Kami memiliki sekitar 25.000 tes fungsional, mereka membutuhkan waktu 30 menit untuk menyelesaikan pada mesin virtual khusus untuk regresi umum. Durasi tes tidak terlalu mengganggu kami - kami terbiasa dengan fakta bahwa tes bisa memakan waktu 60 menit pada kerangka lama.

Bagaimana kami mempercepat apa yang disebut tes "cepat"


Semuanya berawal dari kecelakaan. Seperti biasa terjadi. Kami mendukung satu fitur demi satu, lulus tes pada saat yang sama. Jumlah mereka bertambah, dan waktu yang diperlukan untuk menyelesaikannya juga. Begitu tes mulai merangkak keluar dari batas waktu yang diberikan kepada mereka, dan karena itu proses eksekusi mereka diselesaikan dengan paksa. Tes yang tidak lengkap penuh dengan masalah yang terlewat dalam kode.

Kami menganalisis kecepatan tes dan tugas mempercepatnya menjadi relevan. Maka dimulailah studi yang disebut "Tes bekerja lambat - perbaiki."

Berikut adalah tiga masalah besar yang kami temukan dalam tes.

Masalah 1: jsQuery disalahgunakan



Semua data yang kita miliki disimpan dalam database PostgreSQL. Sebagian besar dalam bentuk json, jadi kami aktif menggunakan jsQuery.

Berikut adalah contoh kueri yang kami buat di database untuk mendapatkan data yang diperlukan:

SELECT * FROM firm WHERE json_data @@ 'rubrics.@# > 0' AND json_data @@ 'address_name = *' AND json_data @@ 'contact_groups.#.contacts.#.type = β€œwebsite”' ORDER BY RANDOM() LIMIT 1 

Sangat mudah untuk memperhatikan bahwa contoh menggunakan json_data beberapa kali berturut-turut, meskipun akan benar untuk menulis ini:

 SELECT * FROM firm WHERE json_data @@ 'rubrics.@# > 0 AND address_name = * AND contact_groups.#.contacts.#.type = β€œwebsite”' ORDER BY RANDOM() LIMIT 1 

Kekurangan seperti itu tidak terlalu mencolok, karena dalam tes kami tidak menulis semua pertanyaan dengan tangan kami, tetapi kami menggunakan QueryBuilders, yang mereka buat sendiri setelah menentukan fungsi yang diperlukan. Kami tidak berpikir bahwa ini dapat mempengaruhi kecepatan eksekusi permintaan. Dengan demikian, dalam kode itu terlihat seperti ini:

 $qb = $this>createQueryBulder() ->selectAllBranchFields() ->fromBranchPartition() ->hasRubric() ->hasAddressName() ->hasWebsite() ->orderByRandom() ->setMaxResults(1); 

Jangan ulangi kesalahan kami : jika ada beberapa kondisi dalam satu bidang JSONB, jelaskan semuanya dalam kerangka operator tunggal '@@'. Setelah kami redid, kami mempercepat waktu eksekusi dari setiap permintaan dua kali. Sebelumnya, permintaan yang diuraikan membutuhkan 7500 ms, tetapi sekarang dibutuhkan 3500 ms.

Masalah 2: Data Uji Ekstra



Akses ke API kami disediakan oleh kunci, setiap pengguna memiliki API sendiri. Sebelumnya, dalam pengujian, seringkali perlu untuk mengubah pengaturan kunci. Karena itu, tes jatuh.

Kami memutuskan untuk membuat beberapa kunci dengan pengaturan yang diperlukan untuk setiap proses regresi untuk menghindari masalah persimpangan. Dan karena membuat kunci baru tidak memengaruhi fungsionalitas seluruh aplikasi, pendekatan ini dalam pengujian tidak akan memengaruhi apa pun. Mereka hidup dalam kondisi seperti itu selama sekitar satu tahun, sampai mereka mulai berurusan dengan produktivitas.

Tidak banyak kunci - 1000 buah. Untuk mempercepat aplikasi, kami menyimpannya di memori dan memperbaruinya setiap beberapa menit atau sesuai permintaan. Dengan demikian, setelah menyimpan kunci berikutnya, tes memulai proses sinkronisasi, yang akhirnya tidak kami tunggu - kami terima dalam respons "504", yang ditulis dalam log. Pada saat yang sama, aplikasi tidak menandakan masalah dengan cara apa pun, dan kami pikir semuanya bekerja dengan baik untuk kami. Proses pengujian regresi itu sendiri berlanjut. Dan pada akhirnya ternyata kami selalu beruntung dan kunci kami diselamatkan.

Kami hidup dalam ketidaktahuan sampai kami memeriksa log. Ternyata kami membuat kunci, tetapi tidak menghapusnya setelah menjalankan tes. Dengan demikian, kami telah mengakumulasikan 500.000 di antaranya.

Jangan ulangi kesalahan kami: jika Anda mengubah database dalam pengujian, pastikan untuk memastikan bahwa database tersebut dikembalikan ke kondisi semula. Setelah kami membersihkan database, proses pembaruan utama dipercepat 500 kali.

Masalah 3: Pengambilan Sampel Acak



Kami senang menguji aplikasi pada data yang berbeda. Kami memiliki banyak data, dan masalah ditemukan secara berkala. Misalnya, ada kasus ketika kami tidak mengunggah data tentang iklan, tetapi pengujian menangkap masalah ini tepat waktu. Itu sebabnya dalam setiap permintaan pengujian kami, Anda dapat melihat ORDER BY RANDOM ()

Ketika kami melihat hasil kueri, dengan dan tanpa keacakan, dengan EXPLAIN, kami melihat peningkatan kinerja 20 kali. Jika kita berbicara tentang contoh di atas, maka tanpa pengacakan itu berhasil selama 160 ms. Kami serius memikirkan apa yang harus dilakukan, karena kami tidak benar-benar ingin meninggalkan rumah acak.

Misalnya, di Novosibirsk ada sekitar 150 ribu perusahaan, dan ketika kami mencoba menemukan perusahaan yang memiliki alamat, situs web, dan tajuk, kami menerima catatan acak dari hampir seluruh database. Kami memutuskan untuk mengurangi seleksi menjadi 100 perusahaan pertama yang sesuai dengan kondisi kami. Hasil dari pikiran adalah kompromi antara pemilihan konstan berbagai data dan kecepatan:

 SELECT * FROM (SELECT * FROM firm_1 WHERE json_data @@ 'rubrics.@# > 0 AND address_name = * AND contact_groups.#.contacts.#.type = "website"' LIMIT 100) random_hack ORDER BY RANDOM() LIMIT 1; 

Dengan cara sederhana ini, kami hampir tidak kehilangan apa pun pada akselerasi 20x. Waktu pelaksanaan permintaan seperti itu adalah 180ms.

Jangan ulangi kesalahan kita: momen ini, tentu saja, hampir tidak bisa disebut kesalahan. Jika Anda benar-benar memiliki banyak tes, selalu pikirkan seberapa banyak Anda membutuhkan keacakan dalam data. Trade-off antara kecepatan eksekusi query ke dalam database dan keunikan pilihan membantu kami mempercepat query SQL sebanyak 20 kali.

Sekali lagi daftar tindakan singkat:


  1. Jika kami menetapkan beberapa kondisi untuk memilih data di bidang JSONB, maka mereka harus terdaftar dalam satu operator '@@'.
  2. Jika kami membuat data uji, pastikan untuk menghapusnya. Sekalipun kehadiran mereka tampaknya tidak memengaruhi fungsionalitas aplikasi.
  3. Jika Anda membutuhkan data acak untuk setiap proses, kami menemukan kompromi antara keunikan sampel dan kecepatan eksekusi.

Kami mempercepat regresi tiga kali berkat modifikasi sederhana (dan untuk beberapa, bahkan mungkin jelas). Sekarang tes 25K kami lulus dalam 10 menit. Dan ini bukan batasnya - kami mengoptimalkan kode selanjutnya. Tidak diketahui berapa banyak penemuan tak terduga yang menunggu kita di sana.

Source: https://habr.com/ru/post/id419671/


All Articles