Bagian integral dari situs kencan OkCupid adalah rekomendasi dari calon mitra. Mereka didasarkan pada tumpang tindih banyak preferensi yang Anda dan calon mitra tunjukkan. Seperti yang dapat Anda bayangkan, ada banyak cara untuk mengoptimalkan tugas ini.
Namun, preferensi Anda bukan satu-satunya faktor yang memengaruhi siapa yang kami rekomendasikan sebagai calon mitra (atau merekomendasikan diri Anda sendiri sebagai calon mitra untuk orang lain). Jika kami hanya menampilkan semua pengguna yang sesuai dengan kriteria Anda, tanpa peringkat apa pun, daftar sama sekali tidak akan optimal. Misalnya, jika Anda mengabaikan aktivitas pengguna terkini, Anda dapat menghabiskan lebih banyak waktu untuk berbicara dengan orang yang tidak mengunjungi situs tersebut. Selain preferensi yang Anda tentukan, kami menggunakan banyak algoritme dan faktor untuk merekomendasikan kepada Anda orang-orang yang menurut kami harus Anda lihat.
Kita harus memberikan hasil terbaik dan daftar rekomendasi yang hampir tidak ada habisnya. Di aplikasi lain, di mana konten lebih jarang berubah, Anda dapat melakukannya dengan memperbarui rekomendasi secara berkala. Misalnya, saat menggunakan fitur "Temukan Mingguan" Spotify, Anda menikmati serangkaian lagu yang direkomendasikan, set ini tidak akan berubah hingga minggu depan. Di OkCupid, pengguna tanpa henti melihat rekomendasi mereka secara real time. "Konten" yang direkomendasikan bersifat sangat dinamis (misalnya, pengguna dapat mengubah preferensi, data profil, lokasi, menonaktifkannya kapan saja, dll.). Pengguna dapat mengubah siapa dan bagaimana dia dapat merekomendasikannya, jadi kami ingin memastikan bahwa pertandingan potensial adalah yang terbaik pada waktu tertentu.
Untuk memanfaatkan algoritme peringkat yang berbeda dan membuat rekomendasi waktu nyata, Anda perlu menggunakan mesin telusur yang terus diperbarui dengan data pengguna dan menyediakan kemampuan untuk memfilter dan memberi peringkat calon potensial.
Apa masalah dengan sistem pencarian pertandingan yang ada
OkCupid telah menggunakan mesin pencari internalnya sendiri selama bertahun-tahun. Kami tidak akan membahas detailnya, tetapi pada tingkat abstraksi yang tinggi, ini adalah kerangka kerja pengurangan peta atas pecahan ruang pengguna, di mana setiap pecahan berisi beberapa data pengguna yang relevan dalam memori, yang digunakan saat mengaktifkan berbagai filter dan jenis dengan cepat. Istilah penelusuran berbeda di semua pecahan, dan pada akhirnya hasilnya digabungkan untuk mengembalikan kandidat k teratas. Sistem penyandingan yang kami tulis bekerja dengan baik, jadi mengapa kami memutuskan untuk mengubahnya sekarang?
Kami tahu kami perlu memperbarui sistem untuk mendukung berbagai proyek berbasis rekomendasi di tahun-tahun mendatang. Kami tahu bahwa tim kami akan berkembang, dan begitu pula jumlah proyek. Salah satu tantangan terbesar adalah memperbarui skema. Misalnya, menambahkan bagian baru dari data pengguna (misalnya, tag jenis kelamin dalam preferensi) memerlukan ratusan atau ribuan baris kode dalam templat, dan penerapan membutuhkan koordinasi yang cermat untuk memastikan bahwa semua bagian sistem diterapkan dalam urutan yang benar. Hanya mencoba menambahkan cara baru untuk memfilter kumpulan data khusus atau memeringkat hasil membutuhkan setengah hari waktu teknisi. Dia harus menerapkan setiap segmen secara manual dalam produksi dan memantau potensi masalah. Lebih penting lagi, menjadi sulit untuk mengelola dan menskalakan sistem,karena pecahan dan replika didistribusikan secara manual ke seluruh armada mesin yang tidak memasang perangkat lunak apa pun.
Di awal tahun 2019, beban pada sistem penyandingan meningkat, jadi kami menambahkan satu set replika dengan menempatkan instance layanan secara manual di beberapa mesin. Pekerjaan itu memakan waktu berminggu-minggu di bagian belakang dan untuk pengembang. Selama waktu ini, kami juga mulai melihat hambatan kinerja dalam penemuan layanan tertanam, antrian pesan, dan sebagainya. Meskipun komponen ini sebelumnya bekerja dengan baik, kami telah mencapai titik di mana kami mulai mempertanyakan skalabilitas sistem ini. Tugas kami adalah memindahkan sebagian besar beban kerja kami ke cloud. Mem-porting sistem pasangan ini sendiri merupakan tugas yang membosankan, tetapi juga melibatkan subsistem lain.
Saat ini di OkCupid, banyak dari subsistem ini dilayani oleh opsi OSS yang lebih kuat dan ramah cloud, dan tim telah mengadopsi berbagai teknologi dengan sukses besar selama dua tahun terakhir. Kami tidak akan membahas proyek ini di sini, tetapi berfokus pada tindakan yang kami ambil untuk mengatasi masalah di atas, beralih ke mesin telusur yang lebih ramah pengembang dan dapat diskalakan untuk rekomendasi kami: Vespa .
Itu kebetulan! Mengapa OkCupid berteman dengan Vespa
Tim kami secara historis kecil. Kami tahu sejak awal bahwa memilih mesin pencari akan sangat sulit, jadi kami melihat opsi open source yang berhasil untuk kami. Dua pesaing utama adalah Elasticsearch dan Vespa.
Elasticsearch
Ini adalah teknologi populer dengan komunitas besar, dokumentasi bagus, dan dukungan. Ada banyak fitur dan bahkan digunakan oleh Tinder . Bidang skema baru dapat ditambahkan menggunakan pemetaan PUT, kueri dapat dibuat menggunakan panggilan REST terstruktur, ada beberapa dukungan untuk pemeringkatan berdasarkan waktu kueri, kemampuan untuk menulis plugin khusus, dll. Ketika menyangkut penskalaan dan pemeliharaan, Anda hanya perlu menentukan jumlah pecahan , dan sistem itu sendiri menangani distribusi replika. Penskalaan membutuhkan pembangunan kembali indeks lain dengan lebih banyak pecahan.
Salah satu alasan utama kami membuang Elasticsearch adalah kurangnya pembaruan parsial yang sebenarnya dalam memori. Ini sangat penting untuk kasus penggunaan kami, karena dokumen yang akan kami indeks harus sangat sering diperbarui karena suka, pesan, dll. Dokumen ini bersifat sangat dinamis, dibandingkan dengan konten seperti iklan atau gambar, yang sebagian besar merupakan objek statis dengan atribut konstan. Oleh karena itu, siklus baca-tulis yang tidak efisien pada pembaruan adalah masalah kinerja utama kami.
Vespa
Kode sumber dibuka hanya beberapa tahun yang lalu. Pengembang mengumumkan dukungan untuk menyimpan, mencari, memberi peringkat, dan mengatur Big Data secara real time. Fitur yang didukung Vespa:
Dalam hal penskalaan dan pemeliharaan, Anda tidak perlu lagi memikirkan tentang shard - Anda menyiapkan tata letak untuk node konten, dan Vespa secara otomatis menangani cara memecah dokumen, mereplikasi, dan mendistribusikan data. Selain itu, data secara otomatis dipulihkan dan didistribusikan kembali dari replika setiap kali Anda menambah atau menghapus node. Penskalaan berarti memperbarui konfigurasi untuk menambahkan node dan memungkinkan Vespa mendistribusikan ulang data ini secara otomatis dalam waktu nyata.
Secara keseluruhan, Vespa tampaknya paling cocok untuk kasus penggunaan kami. OkCupid menyertakan banyak informasi berbeda tentang pengguna untuk membantu mereka menemukan kecocokan terbaik - dalam hal filter dan sortir saja, ada lebih dari seratus parameter! Kami akan selalu menambahkan filter dan sortir, jadi sangat penting untuk mempertahankan alur kerja ini. Dalam hal entri dan pertanyaan, Vespa paling mirip dengan sistem kami yang ada; artinya, sistem kami juga memerlukan pemrosesan pembaruan parsial yang cepat dalam memori dan pemrosesan waktu nyata selama permintaan kecocokan. Vespa juga memiliki struktur rangking yang jauh lebih fleksibel dan sederhana. Bonus bagus lainnya adalah kemampuan untuk mengekspresikan kueri di YQL, berbeda dengan struktur kueri yang tidak nyaman di Elasticsearch. Dalam hal penskalaan dan pemeliharaan,kemudian kemampuan distribusi data otomatis Vespa sangat menarik bagi tim kami yang relatif kecil. Secara keseluruhan, Vespa terbukti mendukung kasus penggunaan dan persyaratan kinerja kami dengan lebih baik sekaligus lebih mudah perawatannya daripada Elasticsearch.
Elasticsearch adalah mesin yang lebih terkenal dan kami dapat memanfaatkan pengalaman Tinder dengannya, tetapi opsi apa pun akan membutuhkan banyak penelitian pendahuluan. Pada saat yang sama, Vespa melayani banyak sistem dalam produksi seperti Zedge , Flickr dengan miliaran gambar, platform iklan Yahoo Gemini Ads dengan lebih dari 100 ribu kueri per detik untuk menayangkan iklan kepada 1 miliar pengguna aktif bulanan. Ini memberi kami keyakinan bahwa itu adalah opsi yang teruji dalam pertempuran, efisien dan dapat diandalkan - pada kenyataannya, Vespa sudah ada bahkan sebelum Elasticsearch.
Selain itu, para pengembang Vespa telah terbukti sangat ramah dan membantu. Vespa pada awalnya dibuat untuk iklan dan konten. Sejauh yang kami tahu, itu belum digunakan di situs kencan. Awalnya sulit untuk mengintegrasikan mesin karena kami memiliki kasus penggunaan yang unik, tetapi tim Vespa terbukti sangat responsif dan dengan cepat mengoptimalkan sistem untuk membantu kami menangani beberapa masalah yang muncul.
Bagaimana Vespa bekerja dan seperti apa pencarian di OkCupid
Sebelum mempelajari contoh Vespa kami, berikut adalah ikhtisar singkat tentang cara kerjanya. Vespa adalah kumpulan dari banyak layanan, tetapi setiap kontainer Docker dapat dikonfigurasi menjadi admin / config host, host kontainer Java stateless, dan / atau host konten C ++ stateful. Paket aplikasi dengan konfigurasi, komponen, model ML, dll. Dapat diterapkan melalui API Negaradalam cluster konfigurasi yang menangani penerapan perubahan pada container dan cluster konten. Permintaan umpan dan permintaan lainnya melalui penampung Java tanpa status (yang memungkinkan penyesuaian pemrosesan) melalui HTTP sebelum pembaruan umpan tiba di cluster konten atau permintaan bercabang ke lapisan konten tempat eksekusi permintaan terdistribusi terjadi. Sebagian besar, menerapkan paket aplikasi baru hanya membutuhkan beberapa detik, dan Vespa memproses perubahan ini secara real time di container dan cluster konten, sehingga Anda jarang harus memulai ulang apa pun.
Seperti apa pencarian itu?
Dokumen cluster Vespa berisi berbagai atribut khusus pengguna. Definisi skema mendefinisikan bidang tipe dokumen serta profil peringkat yang berisi kumpulan ekspresi peringkat yang berlaku. Misalkan kita memiliki definisi skema yang mewakili pengguna seperti ini:
search user {
document user {
field userId type long {
indexing: summary | attribute
attribute: fast-search
rank: filter
}
field latLong type position {
indexing: attribute
}
# UNIX timestamp
field lastOnline type long {
indexing: attribute
attribute: fast-search
}
# Contains the users that this user document has liked
# and the corresponding weights are UNIX timestamps when that like happened
field likedUserSet type weightedset<long> {
indexing: attribute
attribute: fast-search
}
}
rank-profile myRankProfile inherits default {
rank-properties {
query(lastOnlineWeight): 0
query(incomingLikeWeight): 0
}
function lastOnlineScore() {
expression: query(lastOnlineWeight) * freshness(lastOnline)
}
function incomingLikeTimestamp() {
expression: rawScore(likedUserSet)
}
function hasLikedMe() {
expression: if (incomingLikeTimestamp > 0, 1, 0)
}
function incomingLikeScore() {
expression: query(incomingLikeWeight) * hasLikedMe
}
first-phase {
expression {
lastOnlineScore + incomingLikeScore
}
}
summary-features {
lastOnlineScore incomingLikeScore
}
}
}
Notasi
indexing: attribute
menunjukkan bahwa kolom ini harus disimpan dalam memori untuk performa baca dan tulis terbaik kolom ini.
Katakanlah kita mengisi cluster dengan dokumen kustom ini. Kemudian kami dapat memfilter dan memberi peringkat pada salah satu bidang di atas. Misalnya, membuat permintaan POST ke mesin telusur default
http://localhost:8080/search/
untuk menemukan pengguna selain pengguna kami sendiri 777
, dalam jarak 50 mil dari lokasi kami, yang telah online sejak stempel waktu 1592486978
, diberi peringkat berdasarkan aktivitas terakhir dan mempertahankan dua kandidat teratas. Mari juga pilih fitur ringkasan untuk melihat kontribusi setiap ekspresi peringkat di profil peringkat kami:
{
"yql": "select userId, summaryfeatures from user where lastOnline > 1592486978 and !(userId contains \"777\") limit 2;",
"ranking": {
"profile": "myRankProfile",
"features": {
"query(lastOnlineWeight)": "50"
}
},
"pos": {
"radius": "50mi",
"ll": "N40o44'22;W74o0'2",
"attribute": "latLong"
},
"presentation": {
"summary": "default"
}
}
Kita bisa mendapatkan hasil seperti ini:
{
"root": {
"id": "toplevel",
"relevance": 1.0,
"fields": {
"totalCount": 317
},
"coverage": {
"coverage": 100,
"documents": 958,
"full": true,
"nodes": 1,
"results": 1,
"resultsFull": 1
},
"children": [
{
"id": "index:user/0/bde9bd654f1d5ae17fd9abc3",
"relevance": 48.99315843621399,
"source": "user",
"fields": {
"userId": -5800469520557156329,
"summaryfeatures": {
"rankingExpression(incomingLikeScore)": 0.0,
"rankingExpression(lastOnlineScore)": 48.99315843621399,
"vespa.summaryFeatures.cached": 0.0
}
}
},
{
"id": "index:user/0/e8aa37df0832905c3fa1dbbd",
"relevance": 48.99041280864198,
"source": "user",
"fields": {
"userId": 6888497210242094612,
"summaryfeatures": {
"rankingExpression(incomingLikeScore)": 0.0,
"rankingExpression(lastOnlineScore)": 48.99041280864198,
"vespa.summaryFeatures.cached": 0.0
}
}
}
]
}
}
Setelah memfilter dengan mencocokkan peringkat hasil yang dihitung ekspresi dari fase pertama ( fase pertama) untuk memberi peringkat hasil. Relevansi yang dikembalikan (relevansi) adalah skor keseluruhan sebagai hasil dari melakukan semua fungsi peringkat fase pertama dalam profil peringkat ( profil peringkat) yang kami tentukan dalam kueri kami, yaitu
ranking.profile
myRankProfile
. ranking.features
Kami mendefinisikan query(lastOnlineWeight)
50 dalam daftar , yang kemudian direferensikan oleh satu-satunya ekspresi peringkat yang kami gunakan lastOnlineScore
. Ini menggunakan fungsi peringkat bawaan freshness
, yaitu angka yang mendekati 1 jika stempel waktu di atribut lebih baru daripada stempel waktu saat ini. Selama semuanya berjalan dengan baik, tidak ada yang rumit di sini.
Tidak seperti konten statis, konten ini dapat memengaruhi apakah ditampilkan kepada pengguna atau tidak. Misalnya, mereka mungkin menyukai Anda! Kita bisa mengindeks bidang berbobot
likedUserSet
untuk setiap dokumen pengguna yang berisi sebagai kunci ID pengguna yang mereka sukai dan sebagai nilai stempel waktu kapan itu terjadi. Maka akan mudah untuk menyaring orang-orang yang menyukai Anda (misalnya, menambahkan ekspresi likedUserSet contains \”777\”
di YQL), tetapi bagaimana cara memasukkan informasi ini selama pemeringkatan? Bagaimana cara meningkatkan togr pengguna yang menyukai orang kita di hasil?
Di hasil sebelumnya, ekspresi peringkat
incomingLikeScore
adalah 0 untuk kedua klik ini. Pengguna tersebut 6888497210242094612
sebenarnya menyukai pengguna tersebut777
tetapi saat ini tidak tersedia di peringkat meskipun kami telah menempatkannya "query(incomingLikeWeight)": 50
. Kita dapat menggunakan fungsi peringkat di YQL (argumen pertama dan satu-satunya untuk fungsi rank()
menentukan apakah dokumen tersebut cocok, tetapi semua argumen digunakan untuk menghitung skor peringkat) dan kemudian menggunakan dotProduct dalam ekspresi peringkat YQL untuk menyimpan dan mengambil skor mentah (dalam hal ini stempel waktu saat pengguna menyukai kami), misalnya, dengan cara ini:
{
"yql": "select userId,summaryfeatures from user where !(userId contains \"777\") and rank(lastOnline > 1592486978, dotProduct(likedUserSet, {\"777\":1})) limit 2;",
"ranking": {
"profile": "myRankProfile",
"features": {
"query(lastOnlineWeight)": "50",
"query(incomingLikeWeight)": "50"
}
},
"pos": {
"radius": "50mi",
"ll": "N40o44'22;W74o0'2",
"attribute": "latLong"
},
"presentation": {
"summary": "default"
}
}
{
"root": {
"id": "toplevel",
"relevance": 1.0,
"fields": {
"totalCount": 317
},
"coverage": {
"coverage": 100,
"documents": 958,
"full": true,
"nodes": 1,
"results": 1,
"resultsFull": 1
},
"children": [
{
"id": "index:user/0/e8aa37df0832905c3fa1dbbd",
"relevance": 98.97595807613169,
"source": "user",
"fields": {
"userId": 6888497210242094612,
"summaryfeatures": {
"rankingExpression(incomingLikeScore)": 50.0,
"rankingExpression(lastOnlineScore)": 48.97595807613169,
"vespa.summaryFeatures.cached": 0.0
}
}
},
{
"id": "index:user/0/bde9bd654f1d5ae17fd9abc3",
"relevance": 48.9787037037037,
"source": "user",
"fields": {
"userId": -5800469520557156329,
"summaryfeatures": {
"rankingExpression(incomingLikeScore)": 0.0,
"rankingExpression(lastOnlineScore)": 48.9787037037037,
"vespa.summaryFeatures.cached": 0.0
}
}
}
]
}
}
Sekarang pengguna
68888497210242094612
diangkat ke atas, karena dia menyukai pengguna kita dan itu incomingLikeScore
memiliki arti penuh. Tentu saja, kita sebenarnya memiliki stempel waktu ketika dia menyukai kita sehingga kita dapat menggunakannya dalam ekspresi yang lebih kompleks, tetapi untuk saat ini, biarkan saja.
Ini menunjukkan mekanisme pemfilteran dan pemeringkatan hasil menggunakan sistem pemeringkatan. Kerangka kerja peringkat menyediakan cara yang fleksibel untuk menerapkan ekspresi (yang sebagian besar hanya matematis) untuk dicocokkan selama kueri.
Menyiapkan middleware di Java
Bagaimana jika kita ingin mengambil rute yang berbeda dan menjadikan ekspresi dotProduct ini secara implisit menjadi bagian dari setiap permintaan? Di sinilah lapisan penampung Java khusus masuk - kita dapat menulis komponen Penelusuran khusus . Ini memungkinkan Anda memproses parameter arbitrer, menulis ulang kueri, dan memproses hasil dengan cara tertentu. Berikut contoh di Kotlin:
@After(PhaseNames.TRANSFORMED_QUERY)
class MatchSearcher : Searcher() {
companion object {
// HTTP query parameter
val USERID_QUERY_PARAM = "userid"
val ATTRIBUTE_FIELD_LIKED_USER_SET = “likedUserSet”
}
override fun search(query: Query, execution: Execution): Result {
val userId = query.properties().getString(USERID_QUERY_PARAM)?.toLong()
// Add the dotProduct clause
If (userId != null) {
val rankItem = query.model.queryTree.getRankItem()
val likedUserSetClause = DotProductItem(ATTRIBUTE_FIELD_LIKED_USER_SET)
likedUserSetClause.addToken(userId, 1)
rankItem.addItem(likedUserSetClause)
}
// Execute the query
query.trace("YQL after is: ${query.yqlRepresentation()}", 2)
return execution.search(query)
}
}
Kemudian, di file services.xml kita , kita dapat mengkonfigurasi komponen ini sebagai berikut:
...
<search>
<chain id="default" inherits="vespa">
<searcher id="com.okcupid.match.MatchSearcher" bundle="match-searcher"/>
</chain>
</search>
<handler id="default" bundle="match-searcher">
<binding>http://*:8080/match</binding>
</handler>
...
Kemudian kami hanya membuat dan menerapkan paket aplikasi dan membuat permintaan ke penangan khusus
http://localhost:8080/match-?userid=777
:
{
"yql": "select userId,summaryfeatures from user where !(userId contains \"777\") and rank(lastOnline > 1592486978) limit 2;",
"ranking": {
"profile": "myRankProfile",
"features": {
"query(lastOnlineWeight)": "50",
"query(incomingLikeWeight)": "50"
}
},
"pos": {
"radius": "50mi",
"ll": "N40o44'22;W74o0'2",
"attribute": "latLong"
},
"presentation": {
"summary": "default"
}
}
Kami mendapatkan hasil yang sama seperti sebelumnya! Perhatikan bahwa dalam kode Kotlin, kami menambahkan traceback untuk menampilkan tampilan YQL setelah perubahan, jadi jika disetel
tracelevel=2
dalam parameter URL, responsnya juga akan ditampilkan:
...
{
"message": "YQL after is: select userId, summaryfeatures from user where ((rank(lastOnline > 1592486978, dotProduct(likedUserSet, {\"777\": 1})) AND !(userId contains \"777\") limit 2;"
},
...
Wadah middleware Java adalah alat yang ampuh untuk menambahkan logika pemrosesan kustom melalui Searcher atau pembuatan asli hasil menggunakan Renderer . Kami menyesuaikan komponen Searcher kamiuntuk menangani kasus seperti di atas dan aspek lain yang ingin kami buat tersirat dalam penelusuran kami. Misalnya, salah satu konsep produk yang kami dukung adalah gagasan "timbal balik" - Anda dapat mencari pengguna dengan kriteria tertentu (seperti rentang usia dan jarak), tetapi Anda juga harus memenuhi kriteria pencarian untuk kandidat. Untuk mendukung ini di komponen Searcher kami, kami dapat mengambil dokumen pengguna yang mencari untuk memberikan beberapa atributnya dalam kueri bercabang berikutnya untuk pemfilteran dan pemeringkatan. Kerangka kerja peringkat dan middleware khusus bersama-sama memberikan cara yang fleksibel untuk mendukung banyak kasus penggunaan. Kami hanya membahas beberapa aspek dalam contoh ini, tetapi di sini Anda dapat menemukan dokumentasi terperinci.
Bagaimana kami membangun klaster Vespa dan memasukkannya ke dalam produksi
Pada musim semi 2019, kami mulai merencanakan sistem baru. Selama ini, kami juga menghubungi tim Vespa dan berkonsultasi secara rutin tentang kasus penggunaan kami. Tim operasional kami mengevaluasi dan membangun penyiapan kluster awal, dan tim backend mulai mendokumentasikan, merancang, dan membuat prototipe berbagai kasus penggunaan Vespa.
Tahap pertama pembuatan prototipe
Sistem backend OkCupid ditulis dalam Golang dan C ++. Untuk menulis komponen logika Vespa khusus, serta memberikan kecepatan umpan yang tinggi menggunakan API klien umpan HTTP Vespa Java , kami harus sedikit memahami lingkungan JVM - kami akhirnya menggunakan Kotlin saat mengonfigurasi komponen Vespa dan di saluran pipa umpan kami.
Butuh beberapa tahun untuk mengubah logika aplikasi dan mengungkap fungsi Vespa, berkonsultasi dengan tim Vespa sesuai kebutuhan. Sebagian besar logika sistem mesin pencocokan ditulis dalam C ++, jadi kami juga menambahkan logika untuk menerjemahkan filter kami saat ini dan mengurutkan model data ke dalam kueri YQL yang setara yang kami terbitkan ke klaster Vespa melalui REST. Sejak awal, kami juga berhati-hati dalam membuat pipeline yang baik untuk mengisi kembali kluster dengan basis dokumen pengguna penuh; pembuatan prototipe harus melibatkan banyak perubahan untuk menentukan jenis bidang yang benar untuk digunakan, dan secara tidak sengaja memerlukan pengiriman ulang umpan dokumen.
Pemantauan dan pengujian stres
Saat kami membuat kluster pencarian Vespa, kami harus memastikan dua hal: bahwa kluster tersebut dapat menangani volume kueri dan rekaman pencarian yang diharapkan, dan bahwa rekomendasi yang diberikan sistem sebanding dalam kualitasnya dengan sistem pemasangan yang ada.
Sebelum uji beban, kami menambahkan metrik Prometheus di mana-mana. Eksportir Vespa menyediakan banyak statistik, dan Vespa sendiri juga menyediakan sejumlah kecil metrik tambahan . Berdasarkan ini, kami membuat berbagai dasbor Grafana untuk permintaan per detik, latensi, pemanfaatan sumber daya oleh proses Vespa, dll. Kami juga menjalankan vespa-fbench untuk menguji kinerja kueri. Dengan bantuan pengembang Vespa, kami telah menentukan hal itu karena harganya yang relatif tinggibiaya permintaan statis, tata letak siap pakai yang dikelompokkan kami akan memberikan hasil yang lebih cepat. Dalam tata letak datar, menambahkan lebih banyak node pada dasarnya hanya mengurangi biaya kueri dinamis (yaitu, bagian kueri yang bergantung pada jumlah dokumen yang diindeks). Tata letak yang dikelompokkan berarti bahwa setiap grup situs yang dikonfigurasi akan berisi sekumpulan dokumen lengkap, dan oleh karena itu satu grup dapat melayani permintaan tersebut. Karena tingginya biaya permintaan statis, sambil menjaga jumlah node tetap sama, kami meningkatkan throughput secara signifikan, meningkatkan jumlah dari satu grup datar menjadi tiga. Terakhir, kami juga menguji "lalu lintas bayangan" yang tidak dilaporkan secara real time, saat kami yakin dengan keandalan tolok ukur statis.
Mengoptimalkan kinerja
Performa checkout adalah salah satu rintangan terbesar yang kami hadapi sejak awal. Pada awalnya, kami mengalami masalah saat memproses pembaruan bahkan pada 1000 QPS (permintaan per detik). Kami menggunakan bidang set berbobot secara ekstensif, tetapi pada awalnya tidak efektif. Untungnya, pengembang Vespa dengan cepat membantu menyelesaikan masalah ini, serta masalah lain yang terkait dengan penyebaran data. Mereka kemudian juga menambahkan dokumentasi ekstensif tentang ukuran feed , yang kami gunakan sampai batas tertentu: bidang integer dalam set berbobot besar, jika memungkinkan, memungkinkan pengelompokan dengan menyetel
visibility-delay
dengan menggunakan beberapa pembaruan bersyarat dan mengandalkan bidang atribut (yaitu, dalam memori), serta mengurangi jumlah paket perjalanan pulang pergi dari klien dengan memadatkan dan menggabungkan operasi di pipeline fmdov kami. Sekarang pipeline menangani 3000 QPS secara diam-diam pada kondisi stabil, dan kluster sederhana kami memproses pembaruan 11K QPS saat lonjakan seperti itu terjadi karena alasan apa pun.
Kualitas rekomendasi
Setelah kami yakin bahwa cluster dapat menangani beban tersebut, perlu dilakukan verifikasi bahwa kualitas rekomendasi tidak lebih buruk dari pada sistem yang ada. Setiap penyimpangan kecil dalam penerapan peringkat memiliki dampak yang sangat besar pada kualitas rekomendasi secara keseluruhan dan ekosistem secara keseluruhan. Kami menerapkan sistem eksperimentalVespa di beberapa kelompok uji, sedangkan kelompok kontrol tetap menggunakan sistem yang ada. Beberapa metrik bisnis kemudian dianalisis, mengulangi dan mendokumentasikan masalah sampai kelompok Vespa berkinerja baik, jika tidak lebih baik, daripada kelompok kontrol. Setelah kami yakin dengan hasil Vespa, mudah untuk meneruskan permintaan yang sesuai ke klaster Vespa. Kami dapat meluncurkan semua lalu lintas pencarian ke dalam klaster Vespa tanpa hambatan!
Diagram sistem
Dalam bentuk yang disederhanakan, diagram arsitektur akhir dari sistem baru terlihat seperti ini:
Bagaimana Vespa bekerja sekarang dan apa selanjutnya
Mari kita bandingkan keadaan pencari pasangan Vespa dengan sistem sebelumnya:
- Pembaruan skema
- Sebelumnya: seminggu dengan ratusan baris kode baru, penerapan yang terkoordinasi dengan cermat dengan beberapa subsistem
- :
- Sebelumnya: seminggu dengan ratusan baris kode baru, penerapan yang terkoordinasi dengan cermat dengan beberapa subsistem
- /
- :
- : . , !
- :
-
- : ,
- : , Vespa . -
- : ,
Secara keseluruhan, aspek desain dan pemeliharaan cluster Vespa telah membantu pengembangan semua produk OkCupid. Pada akhir Januari 2020, kami meluncurkan klaster Vespa ke dalam produksi dan melayani semua rekomendasi dalam pencarian pasangan. Kami juga telah menambahkan lusinan bidang baru, ekspresi peringkat, dan kasus penggunaan dengan dukungan untuk semua fitur baru tahun ini, seperti Stacks . Dan tidak seperti sistem perjodohan kami sebelumnya, kami sekarang menggunakan model pembelajaran mesin waktu nyata pada waktu kueri.
Apa berikutnya?
Bagi kami, salah satu keunggulan utama Vespa adalah dukungan langsung untuk pemberian peringkat menggunakan tensor dan integrasi dengan model yang dilatih menggunakan framework seperti TensorFlow . Ini adalah salah satu fitur utama yang akan kami kembangkan dalam beberapa bulan mendatang. Kami sudah menggunakan tensor untuk beberapa kasus penggunaan, dan akan segera mengintegrasikan model pembelajaran mesin yang berbeda yang kami harap dapat memprediksi hasil dan kecocokan dengan lebih baik untuk pengguna kami.
Selain itu, Vespa baru-baru ini mengumumkan dukungan untuk indeks tetangga terdekat multidimensi, yang sepenuhnya waktu nyata, dapat dicari secara bersamaan dan diperbarui secara dinamis. Kami sangat tertarik untuk mempelajari kasus penggunaan lain untuk pencarian indeks tetangga terdekat secara waktu nyata.
OkCupid dan Vespa. Pergilah!
Banyak orang telah mendengar atau bekerja dengan Elasticsearch, tetapi komunitas di sekitar Vespa tidak begitu besar. Kami yakin bahwa banyak aplikasi Elasticsearch lainnya akan bekerja lebih baik di Vespa. Ini bagus untuk OkCupid, dan kami senang kami beralih ke sana. Arsitektur baru ini memungkinkan kami untuk berevolusi dan mengembangkan fitur baru dengan lebih cepat. Kami adalah perusahaan yang relatif kecil, jadi sebaiknya Anda tidak terlalu mengkhawatirkan kerumitan layanan. Kami sekarang jauh lebih siap untuk meningkatkan mesin pencari kami. Tanpa Vespa, kami pasti tidak bisa mencapai kemajuan yang kami capai selama setahun terakhir. Untuk informasi lebih lanjut tentang kemampuan teknis Vespa, pastikan untuk memeriksa AI Vespa di Pedoman E-niaga dari @jobergum .
Kami mengambil langkah pertama dan menyukai para pengembang Vespa. Mereka mengirimi kami pesan kembali dan itu ternyata kebetulan! Kami tidak dapat melakukan ini tanpa bantuan tim Vespa. Terima kasih khusus kepada @jobergum dan @geirst untuk rekomendasi tentang peringkat dan penanganan kueri, serta @kkraune dan @vekterli atas dukungannya. Tingkat dukungan dan upaya yang telah diberikan tim Vespa kepada kami benar-benar luar biasa - mulai dari pemahaman mendalam tentang kasus penggunaan kami, hingga mendiagnosis masalah kinerja dan segera melakukan perbaikan pada mesin Vespa. Kamerad @vekterli bahkan terbang ke kantor kami di New York dan bekerja langsung dengan kami selama seminggu untuk membantu mengintegrasikan mesin. Terima kasih banyak untuk tim Vespa!
Kesimpulannya, kami hanya menyentuh beberapa aspek penggunaan Vespa, tetapi semua ini tidak akan mungkin terjadi tanpa kerja keras dari tim backend dan operasi kami selama setahun terakhir. Kami menghadapi banyak tantangan unik untuk menjembatani kesenjangan antara sistem yang ada dan tumpukan teknologi yang lebih modern, tetapi ini adalah topik untuk artikel lainnya.