Dari Praktik Terbaik Panduan untuk menggunakan Sidekiq, saya mengerti bahwa yang terbaik adalah meneruskan "string, integer, float, boolean, null(nil), array, dan hash" sebagai argumen untuk pekerjaan.

Saya sering hanya meneruskan id objek yang bertahan ke pekerjaan saya, tetapi karena kendala latensi saya perlu menyimpan objek setelah menjalankan pekerjaan.

Objek non-persisten yang saya kerjakan berisi campuran tipe data:

#MyObject<00x000>{
id: nil
start_time: Fri, 11 Dec 2020 08:45:00 PST -08:00 (*this is a TimeWithZone object)
rate: 18.0 (*this is a BigDecimal object)
...
}

Saya berencana untuk meneruskan objek ini ke pekerjaan saya dengan mengubahnya menjadi hash terlebih dahulu:

MyJob.perform_async(my_object.attributes)

Dan kemudian bertahan objek seperti ini:

MyObject.new(my_object_hash).save

Pertanyaan saya, apakah ini aman? Meskipun saya meneruskan tipe data 'sederhana' ke Sidekiq, itu sebenarnya berisi objek yang kompleks. Apakah saya akan kehilangan presisi?

Terima kasih!

0
LumberManFromOregon 11 Desember 2020, 16:51

3 jawaban

Jawaban Terbaik

Ini terdengar seperti solusi "potayto, potahto". Anda tidak menggunakan serialisasi Sidekiq, tetapi membuat serial itu sendiri.

Mari kita lihat mengapa sidekiq memiliki aturan ini:

Bahkan jika mereka membuat serial dengan benar, apa yang terjadi jika antrian Anda dicadangkan dan objek kutipan itu berubah sementara itu? [...] Jangan memberikan simbol, parameter bernama, argumen kata kunci, atau objek Ruby yang kompleks (seperti Tanggal atau Waktu!) karena itu tidak akan bertahan dari dump/load round trip dengan benar.

Saya ingin menambahkan yang ketiga:

Status serialisasi membuat tidak mungkin untuk membedakan antara data yang bertahan dan halus (dalam memori, memo, pemuatan lambat, dll.). Misalnya. a def sent_mails; @sent_mails ||= Mail.for(user_id: id); end sekarang menjadi serial: apakah Anda menginginkannya?

Solusinya juga disediakan oleh sidekiq:

Jangan simpan status ke Sidekiq, simpan pengidentifikasi sederhana. Cari objek setelah Anda benar-benar membutuhkannya dalam metode perform Anda.

Masalah XY di sini

Masalah nyata Anda bukanlah di mana atau bagaimana membuat cerita bersambung. Karena sidekiq memperingatkan agar tidak membuat serialisasi di mana pun dan bagaimana Anda melakukan ini.

Masalah yang perlu Anda pecahkan adalah bagaimana menyimpan status di suatu tempat di mana ia dapat disimpan dengan benar. Atau untuk menghindari penyimpanan status sama sekali: tidak di redis/sidekiq, atau di penyimpanan yang memberi Anda masalah.

Latensi

Apakah penyimpanan Anda lambat? Apakah ini bukan validasi, serialisasi, beberapa efek samping dari penyimpanan yang lambat?

Bisakah Anda meningkatkan ini dengan menjadikannya dua langkah: masukkan status dan perbarui/perkaya/validasi asinkron nanti? Jika Anda menggunakan Rails, itu tidak akan membantu Anda di sini, atau bahkan mungkin merugikan Anda, tetapi model yang umum adalah menyimpan objek dalam tabel "antrian" khusus atau antrian acara; misalnya kafka terkenal untuk ini.

Ketika misalnya penyimpanan terjadi melalui jaringan yang lambat ke API yang lambat, ini mungkin tidak dapat dipecahkan, tetapi ketika penyimpanan terjadi di database lokal, ada beberapa dekade solusi untuk meningkatkan kinerja penulisan di sini yang dapat Anda gunakan. Baik di dalam database Anda, atau dengan beberapa antrian khusus untuk penyimpanan negara (sidekiq bukan antrian penyimpanan khusus) tergantung pada teknologi yang digunakan untuk menyimpan. Misalnya. Linux akan memungkinkan Anda untuk menyimpan melalui memori, membuat penulisan ke disk sangat cepat, tetapi menghilangkan jaminan bahwa itu benar-benar ditulis ke disk.

Misalnya. Dalam api pembukuan, kami akan menyimpan objek yang divalidasi di PostgreSQL dan kemudian meminta pekerjaan async menambahkan atribut mahal ke ini nanti (mis. status yang harus diambil dari API lama atau melalui perhitungan yang rumit).

Misalnya. dalam sistem GIS yang banyak menulis, kami akan menyimpan objek ke dalam tabel "to_process_places", yang dipantau oleh perkakas yang memproses Places. Itu semua sangat tergantung pada domain Anda, dan persyaratan.

Tidak menggunakan status.

Solusi umum adalah tidak membuat objek, tetapi menggunakan muatan aktual oleh pelanggan. Kirim saja payload HTTP (dalam rails, params) dan biarkan saja. Mungkin bergabung dalam header (seperti Tanggal Permintaan) atau menyaring beberapa data (token header atau cookie).

Jika pengontrol Anda dapat beroperasi dengan data ini, demikian juga pekerjaan yang tertunda. Alih-alih membangun objek di pengontrol, serahkan itu pada pekerjaan yang tertunda. Ini bahkan dapat menghasilkan pengontrol yang benar-benar rapi dan ramping: yang mereka lakukan hanyalah (beberapa otentikasi dan otorisasi lalu) memanggil pekerjaan yang tepat dan meneruskannya params yang telah disanitasi.

Jelas ini membutuhkan pertukaran seperti tidak dapat memvalidasi dalam sinkronisasi, tetapi untuk memberikan info seperti itu melalui email, pemberitahuan push, atau respons tertunda sebagai gantinya, tergantung pada kebutuhan Anda (misalnya impor CSV besar hanya dapat mengirim email masalah validasi apa pun, tetapi permintaan login mungkin perlu mendapat tanggapan segera jika login tidak valid).

Ini juga memerlukan beberapa pemikiran: Anda mungkin tidak ingin mengirim CSV yang disandikan Base64 ke sidekiq, tetapi alih-alih menulis file ke penyimpanan (temp) dan meneruskan nama file/url sebagai gantinya. Ini mungkin terdengar jelas, karena: unggahan file pada dasarnya merupakan implementasi dari "penyimpanan sementara" yang disebutkan sebelumnya: Anda tidak meneruskan seluruh PDF/gambar header beresolusi tinggi/CSV ke sidekiq, tetapi simpanlah suatu tempat agar sidekiq dapat mengambilnya nanti untuk diproses. Mengapa atribut lain tidak menggunakan pola yang sama jika meneruskannya ke sidekiq bermasalah?

1
berkes 21 Desember 2020, 12:49

Bagian terpenting dari praktik terbaik yang Anda tautkan adalah

Objek Ruby kompleks tidak dikonversi ke JSON

Oleh karena itu Anda tidak seharusnya memberikan contoh model ke pekerja. Jika Anda menggunakan pekerja Sidekiq, Anda harus mematuhi pernyataan ini dan hash yang Anda berikan seharusnya baik-baik saja. Saya tidak begitu yakin tentang objek TimeWithZone, tetapi Anda dapat mencoba mengonversi ini ke JSON atau ke string seperti yang mereka lakukan dalam panduan praktik terbaik.

Namun, jika Anda menggunakan ActiveJob alih-alih pekerja Sidekiq (apakah Pekerjaan Anda mewarisi ApplicationJob atau include Sidekiq::Worker ?), maka Anda tidak memiliki masalah itu karena ActiveJob menggunakan ID Global untuk mengubah objek menjadi String. Dan kemudian sebelum melakukan pekerjaan itu deserializing objek lagi. Artinya Anda dapat memberikan objek ke pekerjaan Anda.

my_object = MyObject.find(1)
my_object.to_global_id #=> #<GlobalID:0x000045432da2344 [...] gid://your_app_name/MyObject/1>>
serialized_my_object = my_object.to_global_id.to_s

my_object = GlobalID.find(serialized_my_object)

Anda dapat menemukan informasi lebih lanjut di sini https://github.com/toptal/ active-job-style-guide#active-record-models-as-arguments

0
Clara 11 Desember 2020, 16:39

Setelah melakukan beberapa eksperimen pada objek Waktu dalam pekerjaan saya, saya menemukan bahwa saya kehilangan presisi nanodetik di ujung pekerjaan yang lain.

my_object.start_time
=> Mon, 21 Dec 2020 11:35:50 PST -08:00
my_object.strftime('%Y-%m-%d %H:%M:%S.%N')
=> "2020-12-21 11:35:50.151893000"

Anda dapat melihat di sini, kami memiliki presisi termasuk 6 digit setelah desimal. (lihat jawaban ini untuk mengetahui lebih lanjut tentang 'strftime')

Setelah kami memanggil metode JSON pada objek:

generated = JSON.generate(my_object.attributes))
=> \"start_time\":\"2020-12-21T11:35:50.151-08:00\"

Anda dapat melihat di sini kita turun ke 3 digit presisi setelah desimal. 3 digit yang tersisa hilang pada saat ini.

parsed = JSON.parse(generated)
parsed[‘start_time’] = "2020-12-21T11:35:50.151-08:00"

Tampaknya pada tingkat paling dasar, pustaka JSON secara rekursif memanggil as_json pada setiap pasangan nilai kunci dalam hash. Jadi sebenarnya itu tergantung pada bagaimana objek khusus Anda mengimplementasikan as_json.

Masalah ini menyebabkan kegagalan pengujian yang melibatkan kueri db kami untuk objek yang bertahan (diinisialisasi dengan sesuatu seperti, start_time = Time.zone.now (!)) yang dimaksudkan untuk tumpang tindih tepat waktu dengan kelas MyObject kami. Setelah cetak biru my_object yang setengah matang berhasil melewati Sidekiq, mereka kehilangan sedikit presisi, menyebabkan sedikit ketidaksejajaran.

Salah satu cara untuk mengatasi masalah ini adalah dengan monyet menambal kelas Time< /a>.

Dalam kasus kami, solusi yang lebih baik adalah pergi ke arah yang berlawanan dan tidak menggunakan begitu banyak presisi dalam pengujian kami. my_object dalam contoh adalah sesuatu yang akan dimiliki pengguna manusia di kalender mereka; dalam produksi kami tidak pernah menerima begitu banyak presisi dari klien. Jadi, alih-alih, kami memperbaiki pengujian kami dengan menginstruksikan beberapa objek pengujian kami untuk menggunakan sesuatu seperti Time.zone.now.beginning_of_minute, daripada Time.zone.now. Kami sengaja menghilangkan presisi untuk memperbaiki masalah, serta lebih mencerminkan realitas.

0
LumberManFromOregon 21 Desember 2020, 12:06