Prompt dan Konteks yang Berlaku
Rancang sistem pemesanan tiket untuk konser, acara olahraga, atau pameran permintaan tinggi. Satu acara memiliki 50.000 kursi yang ditetapkan. Dua juta pengguna tiba dalam lima menit pertama penjualan, dan traffic edge mencapai puncak 50.000 permintaan per detik. Sistem mengizinkan tidak lebih dari 10.000 pembeli peta kursi secara bersamaan dan menangani hingga 2.000 percobaan hold serta 1.000 otorisasi pembayaran per detik. Sebuah hold berlangsung selama lima menit; kursi yang di-hold tetapi tidak dibeli harus dapat dijual kembali.
Asumsikan penyedia pembayaran mendukung otorisasi dan penangkapan yang terpisah. Target p99 peta kursi adalah di bawah 500 milidetik, pembuatan hold di bawah 300 milidetik, dan pembacaan status pesanan di bawah 200 milidetik. Volume kedatangan, target latensi, durasi hold, dan tingkat pembayaran adalah asumsi wawancara, bukan tolok ukur yang dipublikasikan untuk perusahaan tiket mana pun. Aturan kebenaran inti adalah bahwa satu kursi pada satu acara dimiliki oleh paling banyak satu pesanan terjual yang valid. Ketika ketersediaan menurun, layanan dapat menghentikan penerimaan atau menolak hold; layanan tidak boleh menebak inventaris dan terus menjual.
Cakupannya meliputi penelusuran acara, ruang tunggu virtual, peta kursi, hold multi-kursi atomik, checkout, konfirmasi pra-penerbitan, kedaluwarsa, dan pemulihan. Pembuatan acara, penetapan harga dinamis, penjualan kembali sekunder, akuntansi pengembalian dana, dan sistem deteksi bot yang lengkap berada di luar cakupan, tetapi jawaban harus mendefinisikan bagaimana hal-hal tersebut memenuhi batas inventaris dan pembayaran. Materi desain sistem publik tahun 2026 masih menyajikan pemesanan tiket sebagai masalah konkurensi dan kerumunan mendadak yang tergabung. Materi publik Ticketmaster juga menggambarkan alur nyata di mana pembeli memasuki ruang tunggu virtual dan mencapai pemilihan kursi serta checkout pada tingkat yang terkontrol.
Apa yang Dievaluasi oleh Pewawancara
Sinyal pertama adalah apakah kandidat memisahkan keadilan antrian dari kebenaran inventaris. Ruang tunggu virtual melindungi origin dan mengontrol masuk ke dalam penjualan. Ia tidak dapat menghentikan dua pembeli yang sudah diterima dari meminta kursi yang sama. Transaksi inventaris yang otoritatif harus memberikan pengecualian final.
Sinyal kedua adalah perbedaan antara kunci baris database dan hold bisnis. Kunci baris PostgreSQL berada di dalam transaksi pendek dan dilepaskan ketika transaksi berakhir. Mempertahankan koneksi dan transaksi yang terbuka selama lima menit sementara pembeli memasukkan detail dan membayar adalah hal yang tidak aman. Makna bisnis yang lebih panjang harus berupa status yang tahan lama dengan hold_id dan expires_at.
Sinyal ketiga adalah transisi status yang sadar waktu dan percobaan ulang. Pekerja kedaluwarsa yang tertunda tidak boleh melepaskan kursi yang kini di-hold oleh pembeli baru atau yang telah terjual. Batas waktu klien tidak boleh membuat hold atau pembayaran kedua. Setiap transisi memeriksa status saat ini, pemilik, versi, dan tenggat waktu, sementara kunci idempoten mengambil hasil asli.
Sinyal keempat adalah batas pembayaran yang benar. Otorisasi mungkin berhasil sementara konfirmasi lokal gagal; transaksi terjual mungkin di-commit sementara responsnya hilang; penangkapan mungkin berhasil sebelum webhook-nya tiba. Jawaban yang kuat menyimpan catatan pesanan dan operasi pembayaran serta berkonvergensi melalui pencarian, webhook, dan rekonsiliasi. Mereka tidak memperlakukan respons yang hilang sebagai kegagalan.
Terakhir, kapasitas dan verifikasi harus mengatasi bottleneck yang sebenarnya. CDN, caching, dan replika baca dapat menyerap penelusuran. Penulisan kursi untuk satu acara panas sebaiknya tetap berada pada satu shard otoritatif transaksional. Memisahkan kursi dari satu acara ke beberapa shard tulis terlalu dini mengubah hold tiga kursi biasa menjadi transaksi terdistribusi.
Pertanyaan untuk Diklarifikasi Sebelum Menjawab
- Apakah kursi ditetapkan atau penerimaan umum? Tempat duduk yang ditetapkan membutuhkan status eksklusif per kursi. Penerimaan umum lebih baik dimodelkan sebagai kuantitas yang tersedia per tingkatan tiket dengan dekrement kondisional yang tidak bisa menjadi negatif.
- Apakah permintaan multi-kursi harus semua-atau-tidak-sama-sekali? Prompt ini mengharuskan hold semua-atau-tidak-sama-sekali dari satu hingga enam kursi. Keberhasilan sebagian mengubah penetapan harga, perilaku pelepasan, dan pengalaman pembeli.
- Apa kebijakan keadilan antrian? FIFO memberi penghargaan kepada kedatangan lebih awal; pelepasan acak mengurangi efek perbedaan jaringan skala milidetik. Kebijakan harus diungkapkan dan tetap stabil daripada berubah di tengah penjualan sambil masih mengklaim keadilan terurut.
- Dapatkah otorisasi dan penangkapan pembayaran dipisahkan? Jika ya, otorisasi, konfirmasi kursi, lalu tangkap. Penangkapan segera memerlukan kompensasi ketika uang diambil tetapi konfirmasi kursi gagal.
- Berapa batas tiket per akun? Terapkan di hold maupun konfirmasi pesanan. Pembatasan hanya di browser dapat dilewati.
- Bolehkah beberapa wilayah menulis acara yang sama? Desain dasar menetapkan satu rumah tulis untuk setiap acara dan melayani penelusuran serta antrean di tempat lain. Penulisan multi-wilayah bersamaan memerlukan konsensus lintas wilayah atau inventaris yang dialokasikan sebelumnya dan mengubah perilaku latensi dan kegagalan.
- Seberapa segar peta kursi yang harus ada? Jika beberapa detik keterlambatan dapat diterima, gunakan snapshot ditambah delta. Setiap status
AVAILABLEyang ditampilkan tetap bersifat penasehat hingga transaksi hold berhasil. - Bagaimana jika hold kedaluwarsa sementara autentikasi pembayaran masih berjalan? Tentukan satu periode tenggang
PAYMENT_PENDINGyang terbatas. Jangan memperbarui selamanya atau menjual kembali sementara hasil pembayaran tidak diketahui.
Kerangka Jawaban 30 Detik
"Saya akan menyimpan dua juta kedatangan di ruang tunggu virtual yang bercakupan acara dan menerbitkan token penerimaan bertanda yang berumur pendek hanya pada tingkat yang didukung oleh shard inventaris, penyedia pembayaran, dan penerbit tiket. Penelusuran dan peta kursi menggunakan CDN, caching, dan delta berversi; penulisan hold masuk ke shard database otoritatif tunggal acara. Transaksi pendek mengunci kursi yang dipilih dalam urutan terurut dan menulis satu hold_id dengan kedaluwarsa lima menit dari waktu database hanya jika setiap kursi tersedia atau kedaluwarsa. Tidak ada kunci database yang tetap terbuka selama checkout. Antrian tertunda mempercepat pembersihan kedaluwarsa, tetapi pelepasan tetap secara kondisional mencocokkan hold, status, dan tenggat waktu. Pembayaran menggunakan ID operasi yang stabil untuk mengotorisasi terlebih dahulu. Setelah otorisasi, sebuah transaksi memvalidasi ulang hold dan meng-commit kursi, pesanan, dan outbox sebagai terjual sebelum penangkapan. Setiap batas waktu menjadi tidak diketahui dan berkonvergensi melalui webhook, pencarian, dan rekonsiliasi. Saya akan membuktikan desain dengan pertikaian kursi yang sama, perlombaan kedaluwarsa/pembayaran, respons yang hilang, pesan duplikat, dan kegagalan shard."
Penyelaman Mendalam Langkah-demi-Langkah
Langkah 1: Ubah volume menjadi target kontrol penerimaan
Dua juta kedatangan dalam 300 detik rata-rata sekitar 6.667 per detik. Puncak edge 50.000 permintaan per detik menunjukkan bahwa momen pembukaan dan pembaruan browser jauh lebih meledak-ledak daripada rata-rata. Inventaris hanya dianggarkan untuk 2.000 percobaan hold dan 1.000 otorisasi pembayaran per detik, sehingga percobaan ulang edge tidak dapat langsung mencapai layanan inventaris.
Setiap hold dapat berisi hingga enam kursi, sehingga 2.000 permintaan per detik dapat menyebabkan sebanyak 12.000 keputusan baris kursi per detik sebelum konflik dan rollback. Uji kapasitas memerlukan distribusi bagian panas yang miring; menyebarkan 12.000 keputusan tersebut secara merata ke 50.000 kursi akan menyembunyikan pertikaian.
Ruang tunggu virtual diisolasi oleh event_id dan dapat menerima pengunjung sebelum penjualan. Ia mencatat kelompok kedatangan, waktu masuk, akun, dan sinyal risiko. Ketika diterima, pengunjung menerima admission_token bertanda yang berumur pendek yang terikat pada acara dan akun. Entri penjualan memverifikasi token, kedaluwarsa, dan satu sesi aktif; traffic tidak valid ditolak di edge. Tingkat penerimaan merespons terhadap waktu tunggu kunci inventaris, p99 hold, kesalahan pembayaran, pembeli aktif, dan tingkat penyelesaian. Ketika database melambat, kurangi penerimaan alih-alih menyembunyikan pertikaian tulis di balik lebih banyak replika baca.
Dokumentasi ruang tunggu publik Cloudflare menggambarkan pemesanan FIFO dari stempel waktu saat pengunjung pertama kali mencapai antrian aktif dan mode acak yang memilih pengunjung yang menunggu. Ini adalah kebijakan produk, bukan mekanisme konsistensi database. Bahkan FIFO membutuhkan aturan eksplisit untuk granularitas waktu, koneksi ulang, beberapa perangkat, otoritas jam, dan bot. FIFO tidak dapat menjanjikan urutan permintaan demi permintaan yang mutlak di seluruh jaringan global.
Langkah 2: Pisahkan penelusuran, tampilan kursi, dan inventaris otoritatif
Detail acara, peta venue statis, dan penjelasan harga dapat berada di CDN atau cache. Tampilan kursi menggabungkan snapshot berversi dengan delta status. Klien pertama kali menerima snapshot_version, kemudian menerapkan perubahan yang berisi seat_id, status baru, dan versi yang lebih tinggi. Ia memuat ulang snapshot setelah pemutusan koneksi atau celah versi. Jika 10.000 pembeli aktif melakukan polling setiap lima detik, mereka menghasilkan sekitar 2.000 pembacaan per detik. Pengiriman delta mengurangi pembacaan peta penuh yang berulang, tetapi tidak menjadi sumber kebenaran inventaris.
Peta kursi mungkin sedikit usang. Pembeli dapat melihat AVAILABLE dan tetap kalah dalam perlombaan hold. Cache tidak dapat mengonfirmasi penjualan atau menerima hold dari data usang sementara database tidak tersedia. Ini memisahkan jalur penelusuran yang sangat tersedia dari jalur tulis inventaris yang sangat konsisten.
Tetapkan setiap acara ke satu shard tulis utama dan gunakan transaksi relasional di dalamnya. Acara yang berbeda diskalakan secara horizontal oleh event_id, sementara acara yang sangat panas dapat menerima shard atau kumpulan sumber daya yang didedikasikan. Lima puluh ribu baris kursi bukanlah masalah kapasitas. Waktu tunggu kunci, kelelahan koneksi, dan badai percobaan ulang yang disebabkan oleh banyak permintaan yang menargetkan beberapa kursi yang samalah yang menjadi masalah.
Langkah 3: Definisikan API, status, dan invarian
API inti dapat tetap kecil:
POST /events/{eventId}/holds
{ seatIds, idempotencyKey, admissionToken }
-> { holdId, status, expiresAt, seats }
POST /holds/{holdId}/checkout
{ paymentMethodToken, idempotencyKey }
-> { orderId, paymentStatus, nextAction }
GET /orders/{orderId}
-> { orderStatus, paymentStatus, seats }Model data minimal adalah:
SeatInventory(event_id, seat_id, price_version, status,
hold_id, hold_expires_at, order_id, version)
Hold(hold_id, event_id, account_id, status, expires_at,
idempotency_key, request_digest, created_at)
Order(order_id, hold_id, account_id, amount_minor, currency,
status, payment_operation_id, created_at)
PaymentOperation(operation_id, order_id, provider_reference,
kind, status, idempotency_key, updated_at)
Outbox(event_id, aggregate_id, event_type, payload, published_at)Invarian kritis adalah:
one (event_id, seat_id) has at most one valid SOLD order
all seats in one hold share the event, account, and expiry
only the current hold_id may move HELD to SOLD or release it
order confirmation revalidates price version, quantity, limit, and total
one idempotency key describes one immutable request; changed parameters are rejectedUang adalah bilangan bulat dalam unit minor mata uang. Total yang dikirimkan klien tidak otoritatif; server menghitungnya kembali dari versi harga yang dikunci. admission_token memberikan masuk ke penjualan, bukan kepemilikan kursi mana pun.
Langkah 4: Buat hold multi-kursi atomik dengan transaksi pendek
Satu permintaan memilih paling banyak enam kursi. Transaksi mengurutkan berdasarkan seat_id, kemudian mengunci baris dengan SELECT ... FOR UPDATE dalam urutan tetap tersebut, mengurangi deadlock yang disebabkan oleh urutan kunci yang berlawanan. Jika setiap baris adalah AVAILABLE atau HELD yang kedaluwarsa, transaksi menulis hold_id dan expires_at = database_now + 5 minutes yang sama ke semua baris dan menyisipkan Hold beserta event outbox. Jika satu baris terjual atau dimiliki oleh hold lain yang belum kedaluwarsa, seluruh transaksi di-rollback.
UPDATE kondisional dengan predikat status dan tenggat waktu adalah implementasi valid lainnya, asalkan layanan memeriksa bahwa jumlah baris yang diperbarui sama dengan jumlah yang diminta. Dalam kedua pendekatan, keputusan dan penulisan harus berbagi satu transaksi otoritatif. Mengakuisisi kunci Redis dan menulis database secara asinkron menciptakan dua sumber kebenaran ketika salah satu berhasil dan yang lain gagal. Membaca "tersedia" dan kemudian menulis tanpa syarat memiliki perlombaan asli.
PostgreSQL mendokumentasikan bahwa kunci baris memblokir penulis dan pengunci pada baris yang sama dan dilepaskan pada akhir transaksi. Checkout lima menit pembeli oleh karena itu adalah status data yang tahan lama, bukan transaksi database lima menit. Commit dan lepaskan koneksi segera, kemudian buka transaksi pendek baru untuk setiap transisi selanjutnya.
Commit catatan idempoten dengan hasil hold. Jika layanan meng-commit hold dan crash sebelum membalas, percobaan ulang dengan idempotencyKey yang sama mengembalikan holdId asli. Kunci baru memasuki perlombaan baru. Simpan ringkasan permintaan agar kunci yang sama tidak dapat digunakan kembali untuk kelompok kursi yang berbeda.
Langkah 5: Jadikan kedaluwarsa benar meskipun pekerjaan terlambat
Hasilkan hold_expires_at dari waktu database. Pembacaan dapat menampilkan hold yang kedaluwarsa sebagai tersedia, tetapi mengklaimnya kembali tetap memeriksa tenggat waktu dalam transaksi tulis. Membuat hold menjadwalkan pesan tertunda, dan pemindaian berkala memberikan kompensasi. Keduanya mempercepat transisi eksplisit kembali ke AVAILABLE; keduanya bukan satu-satunya sumber kebenaran kedaluwarsa.
Operasi pelepasan harus menyerupai:
UPDATE seat_inventory
SET status = 'AVAILABLE', hold_id = NULL, hold_expires_at = NULL
WHERE event_id = :eventId
AND hold_id = :holdId
AND status = 'HELD'
AND hold_expires_at <= database_now;Pesan tertunda dapat diduplikasi, terlambat, atau tidak berurutan. Jika hold_id baru sekarang memiliki kursi, predikat lama tidak lagi cocok. Jika kursi adalah SOLD, statusnya tidak lagi cocok. Melepaskan berdasarkan seat_id saja dapat menghapus hold valid pembeli lain.
Autentikasi pembayaran tambahan dapat mendekati tenggat waktu hold. Sebelum otorisasi dimulai, desain ini dapat secara atomik memindahkan Hold.status hold yang valid ke PAYMENT_PENDING dengan satu ekstensi tenggang yang terbatas. Kepemilikan tenggang tetap dihitung terhadap inventaris aktif dan batas akun. Hasil yang tetap tidak diketahui pada tenggat waktu baru masuk ke rekonsiliasi; klien tidak dapat memperpanjang tanpa batas untuk menimbun kursi.
Langkah 6: Masukkan hasil pembayaran yang tidak diketahui ke dalam mesin status
Checkout membuat payment_operation_id yang stabil dan menggunakan kembali kunci idempotennya dengan penyedia. Dokumentasi API publik Stripe menjelaskan bahwa mengulang permintaan dengan kunci idempoten yang sama mengembalikan hasil tersimpan dari permintaan pertama. Objek bergaya PaymentIntent juga mengekspos status siklus hidup untuk pembayaran yang mungkin memerlukan autentikasi tambahan atau penyelesaian asinkron.
Gunakan urutan ini:
- Dalam transaksi pendek, validasi hold, batas tiket, harga, dan tenggat waktu, kemudian buat
Orderdan operasi otorisasi. Jika periode tenggang diperlukan, pindahkanHold.statuskePAYMENT_PENDINGdalam transaksi yang sama. - Panggil otorisasi pembayaran di luar transaksi. Batas waktu menjadi
UNKNOWN; itu tidak membuat operasi kedua. - Setelah otorisasi dikonfirmasi, kunci kursi lagi, cocokkan
hold_id, atur semua kursi keSOLD, atur pesanan keCONFIRMED, dan tulis event outbox dalam satu transaksi. - Tangkap setelah commit. Penangkapan berulang menggunakan ID operasi yang sama. Penerbitan tiket hanya mengonsumsi event konfirmasi yang di-commit.
- Jika otorisasi berhasil tetapi hold tidak lagi dapat dikonfirmasi, batalkan otorisasi. Jika transaksi terjual di-commit dan penangkapan tidak diketahui, pertahankan kursi dan pesanan sementara webhook, pencarian aktif, dan rekonsiliasi berkonvergensi. Jangan lepaskan dan jual kembali. Jika penangkapan definitif gagal dan tidak dapat dicoba ulang, batalkan pesanan dan lepaskan kursinya dalam transaksi kompensasi yang diaudit sebelum penerbitan tiket.
Urutan ini mengizinkan status "terjual, penangkapan tertunda" sementara tetapi tidak pernah memberikan kursi yang sama kepada dua pembeli. Jika bisnis hanya dapat menangkap segera, biaya yang berhasil diikuti oleh transaksi terjual yang gagal memerlukan pembatalan atau pengembalian dana yang dapat diaudit. Kemampuan penyedia tersebut meningkatkan risiko pelanggan dan operasional dan harus dinyatakan secara eksplisit.
Status kursi dan pesanan bergerak secara monoton. Pengalihan browser ke halaman sukses tidak dapat mengotorisasi penerbitan tiket. Hanya respons penyedia yang terverifikasi, webhook, atau pencarian aktif yang memajukan status pembayaran. Deduplikasi webhook berdasarkan ID event penyedia dan hanya terima transisi yang sah.
Langkah 7: Rancang kegagalan, penskalaan, dan degradasi
- Shard acara tidak tersedia: Hentikan penerimaan dan hold baru untuk acara tersebut dan pertahankan pengguna di ruang tunggu. Penelusuran dapat mengatakan pemesanan sementara tidak tersedia; replika usang tidak dapat terus menjual.
- Pengiriman cache atau delta gagal: Kembali ke snapshot berversi dengan frekuensi lebih rendah. Database tetap memutuskan hold.
- Pesan kedaluwarsa hilang: Klaim kondisional dan pemindaian kompensasi tetap memulihkan kursi. Pantau backlog yang kedaluwarsa dan hold yang paling lama lewat waktu.
- Penyedia pembayaran melambat: Kurangi penerimaan dan konkurensi checkout serta pertahankan operasi yang tidak diketahui. Jangan beralih penyedia secara otomatis dan berisiko terjadi biaya kedua.
- Respons hilang setelah commit: Kunci idempoten mengambil hold, pesanan, atau operasi pembayaran asli.
- Satu acara menjadi sangat panas: Berikan sumber daya database khusus, batasi tingkat berdasarkan acara, dan kurangi pembacaan yang tidak penting. Jangan membagi satu transaksi multi-kursi di beberapa shard.
- Sebuah wilayah gagal: Setiap acara memiliki satu rumah tulis dan failover yang terverifikasi. Sebelum primary baru mengambil alih, buktikan bahwa primary lama tidak lagi dapat menulis, menghindari kelebihan penjualan aktif-aktif.
Ruang tunggu juga memerlukan ketahanan terhadap penyalinan token, pemutaran ulang, dan bypass. Token ditandatangani, berumur pendek, dan terikat pada acara dan akun; layanan membatasi sesi bersamaan dan jumlah tiket, sementara traffic berisiko tinggi menerima verifikasi tambahan. Kontrol ini mengurangi keuntungan bot. Kontrol ini tidak secara independen membuktikan bahwa pembeli adalah manusia atau bahwa alokasi benar-benar adil.
Langkah 8: Verifikasi invarian dengan injeksi kesalahan
Selain uji fitur, tegaskan properti yang dapat diperiksa oleh mesin:
count(valid SOLD orders for one seat) <= 1
every CONFIRMED order owns exactly its recorded seats
every SOLD seat points to one CONFIRMED or payment-reconciling order
an expiry action changes only its own hold_id
replaying one idempotent request does not create new business stateKirim ribuan klien bersamaan setelah satu kursi dan harapkan satu hold valid. Kemudian biarkan klien meminta set tiga kursi yang sama dalam urutan yang berbeda untuk memverifikasi perilaku semua-atau-tidak-sama-sekali, urutan kunci tetap, dan percobaan ulang deadlock. Gerakkan waktu melewati batas kedaluwarsa sambil menyelingi hold baru, pesan kedaluwarsa, otorisasi pembayaran, dan konfirmasi. Tugas pembersihan lama tidak boleh pernah mengubah status baru.
Matikan proses atau hilangkan respons segera setelah commit hold, sebelum dan sesudah otorisasi kembali, setelah commit terjual, setelah pengiriman penangkapan, dan sebelum dan sesudah publikasi outbox. Duplikasi webhook dan pesan tertunda; putuskan cache, penyedia pembayaran, dan primary database. Nilai hasilnya menggunakan jumlah terjual tingkat acara, waktu tunggu kunci, tingkat konflik kondisional, tingkat kedaluwarsa hold, usia pembayaran yang tidak diketahui, waktu tunggu antrian dan tingkat penerimaan, serta perbedaan rekonsiliasi penerbitan tiket. HTTP 200 saja tidak banyak membuktikan.
Contoh Jawaban Berkualitas Tinggi
"Saya akan membagi masalah menjadi perlindungan traffic dan kebenaran inventaris. Dua juta kedatangan dalam lima menit rata-rata sekitar 6.667 per detik, dengan puncak edge 50.000 permintaan per detik, sementara inventaris hanya menerima 2.000 percobaan hold per detik. Saya akan membuat ruang tunggu virtual yang bercakupan acara. Ia menyerap traffic pembaruan di edge dan menerbitkan token penerimaan berumur pendek yang terikat akun sesuai dengan kesehatan shard acara dan jalur pembayaran. Pemesanan antrian adalah kebijakan produk eksplisit; ia tidak memberikan pengecualian kursi.
Halaman acara dan peta venue statis menggunakan CDN. Peta kursi menggunakan snapshot berversi ditambah delta dan mungkin beberapa detik usang; hanya hold yang memutuskan. Setiap acara memiliki satu shard tulis database relasional, sehingga 50.000 kursinya tidak dibagi ke beberapa shard. API hold menerima satu hingga enam ID kursi yang diurutkan dan kunci idempoten. Transaksi pendek mengunci baris tersebut dan menulis satu hold_id dengan kedaluwarsa lima menit dari waktu database hanya ketika setiap kursi tersedia atau kedaluwarsa. Jika tidak, semua gagal. Transaksi melepaskan kuncinya saat commit; checkout mempertahankan status hold yang tahan lama, bukan kunci database.
Antrian tertunda dan pemindai membersihkan hold yang kedaluwarsa, tetapi setiap pelepasan mencocokkan hold_id, HELD, dan tenggat waktu. Pesan lama yang terlambat oleh karena itu tidak dapat melepaskan hold baru atau kursi yang terjual. Kunci idempoten dan ringkasan permintaan di-commit dengan hold, sehingga respons yang hilang setelah commit hanya mengambil hasil asli.
Untuk pembayaran, prompt mengasumsikan otorisasi dan penangkapan yang terpisah. Saya membuat operasi pembayaran yang stabil dan mengotorisasi terlebih dahulu. Batas waktu tidak diketahui dan menggunakan ID operasi yang sama untuk pencarian atau percobaan ulang. Setelah otorisasi, sebuah transaksi memvalidasi ulang hold, harga, batas, dan tenggat waktu, kemudian memindahkan kursi ke SOLD, pesanan ke CONFIRMED, dan menulis outbox. Penangkapan dan penerbitan tiket mengikuti. Jika otorisasi berhasil tetapi konfirmasi kursi gagal, saya membatalkan otorisasi. Jika kursi dikonfirmasi dan penangkapan tidak diketahui, saya mempertahankan pesanan dan berkonvergensi melalui webhook, pencarian, dan rekonsiliasi; saya tidak pernah melepaskan dan menjual kembali.
Penskalaan bercakupan acara: acara biasa berbagi shard, acara panas menerima sumber daya khusus, dan satu acara memiliki satu rumah tulis. Jika database, penyedia pembayaran, atau penerbit melambat, ruang tunggu mengurangi penerimaan dan dapat menghentikan hold. Untuk verifikasi, ribuan klien bersaing untuk satu kursi dan set kursi yang tumpang tindih sementara saya menyuntikkan kedaluwarsa terlambat, crash setelah commit, respons pembayaran yang hilang, webhook duplikat, kehilangan cache, dan kegagalan primary. Desain lulus hanya selama membuktikan paling banyak satu pesanan terjual valid per kursi, tidak ada status baru dari pemutaran ulang, dan tidak ada hold baru yang diubah oleh tindakan kedaluwarsa lama."
Kesalahan Umum
- Mengunci di Redis dan menulis inventaris secara asinkron → Kunci dan penulisan database dapat tidak selaras, menghasilkan dua otoritas → Lakukan kondisi, hold yang tahan lama, dan hasil idempoten dalam satu transaksi database otoritatif.
- Mempertahankan kunci baris selama checkout lima menit → Transaksi panjang mengonsumsi koneksi, memblokir penulis, dan pulih dengan buruk setelah ditinggalkan → Gunakan kunci hanya untuk transisi dan representasikan checkout dengan status hold yang tahan lama.
- Mengasumsikan ruang tunggu virtual mencegah kelebihan penjualan → Ia mengontrol penerimaan sementara pembeli yang sudah diterima masih bersaing pada baris yang sama → Terapkan pengecualian secara terpisah dalam transaksi inventaris.
- Melepaskan kedaluwarsa hanya berdasarkan ID kursi → Pesan yang terlambat dapat menghapus hold baru atau status terjual → Cocokkan
hold_id, status, dan tenggat waktu bersama-sama. - Membebankan biaya karena peta kursi menunjukkan tersedia → Model baca mungkin usang dan pembeli lain dapat menang lebih dulu → Dapatkan hold otoritatif sebelum pembayaran.
- Melepaskan dan mencoba ulang dengan kunci baru setelah batas waktu pembayaran → Operasi pertama mungkin telah berhasil, menyebabkan penjualan kembali atau biaya duplikat → Pertahankan status yang tidak diketahui, gunakan kembali ID operasi, dan berkonvergensi melalui webhook, pencarian, dan rekonsiliasi.
- Melakukan sharding acak setiap kursi dari acara panas → Satu hold multi-kursi melintasi shard dan kehilangan atomisitas sederhana → Simpan satu acara pada satu shard tulis hingga kapasitas terukur membuktikan partisi inventaris yang lebih kompleks diperlukan.
- Menjanjikan FIFO global yang ketat → Latensi jaringan, koneksi ulang, pergantian perangkat, dan bot mengubah urutan yang dapat diamati → Tentukan granularitas antrian, identitas, koneksi ulang, dan kebijakan risiko sebagai kontrak keadilan yang dapat diverifikasi.
- Uji beban hanya pada throughput rata-rata → Permintaan pembukaan terkonsentrasi pada beberapa kursi, menyembunyikan waktu tunggu kunci dan badai percobaan ulang di balik distribusi seragam → Uji kursi panas, grup yang tumpang tindih, ledakan instan, dan dependensi yang melambat.
Pertanyaan Lanjutan dan Respons
Lanjutan 1: Apa yang berubah untuk penerimaan umum ketika hanya kuantitas tingkatan tiket yang tidak boleh kelebihan penjualan?
Ganti status per kursi dengan InventoryBucket(event_id, tier_id, available, held, sold, version). Transaksi hold secara kondisional mengharuskan available >= quantity, menurunkan satu kali, dan membuat hold. Kedaluwarsa dan konfirmasi tetap memindahkan jumlah secara idempoten berdasarkan hold_id. Tingkatan yang sangat diperebutkan dapat menggunakan beberapa bucket untuk menyebarkan penulisan, tetapi control plane kuota harus mengalokasikan kapasitas bucket dan menjaga jumlahnya dalam inventaris nyata. Menghapus pemilihan kursi atomik menyederhanakan penyimpanan; perlombaan kedaluwarsa dan pembayaran yang tidak diketahui tetap ada.
Lanjutan 2: Otorisasi rata-rata 90 detik dan autentikasi tambahan dapat melebihi lima menit. Bagaimana Anda menghentikan hold berumur panjang?
Periksa waktu yang tersisa sebelum pembayaran dimulai dan tolak alur autentikasi baru ketika terlalu sedikit waktu yang tersisa. Setelah dimulai, pindahkan hold ke status tenggang PAYMENT_PENDING yang terbatas satu kali, dengan batas per akun dan seluruh acara pada inventaris tersebut. Pada kedaluwarsa tenggang, hentikan memulai pekerjaan dan tanyakan penyedia. Lepaskan hanya setelah mengonfirmasi tidak ada otorisasi; selesaikan atau batalkan otorisasi yang dikonfirmasi. Gunakan data konversi dan perputaran inventaris untuk menyetel hold lima menit dan tenggang daripada membiarkan klien memperbarui berulang kali.
Lanjutan 3: Wilayah tulis utama menghilang selama penjualan. Apakah wilayah lain harus segera mengambil alih?
Jangan memulai penulis kedua hanya berdasarkan batas waktu. Hentikan penerimaan dan hold baru untuk acara tersebut. Gunakan lease konsensus, failover database, atau isolasi primary lama untuk membuktikan ada satu penulis, kemudian biarkan primary baru melanjutkan dari posisi log yang dikonfirmasi. Antrean dan degradasi hanya-baca dapat berlanjut selama celah; hold yang tidak terbukti unik tidak bisa. Rekonsiliasi pesanan, kursi, dan operasi pembayaran sebelum menaikkan penerimaan lagi.
Lanjutan 4: Bagaimana Anda meningkatkan keadilan dan membatasi bot tanpa membuat kontrol penipuan menjadi dependensi kebenaran?
Gunakan akun yang terverifikasi, batas pembelian, batas tingkat, sinyal perangkat dan perilaku, serta tantangan tambahan untuk sesi berisiko tinggi. Token antrian ditandatangani, berumur pendek, dan terikat pada acara dan akun; sesi duplikat digabungkan atau ditolak berdasarkan kebijakan. Penjualan dapat menggunakan FIFO waktu kedatangan kasar atau secara acak menetapkan posisi kepada pengguna yang sudah dalam antrian. Apakah deteksi risiko melewatkan bot atau tidak, database menerapkan invarian hold, batas, dan pesanan yang sama. Kesalahan kontrol risiko memengaruhi keadilan alokasi; itu tidak boleh menyebabkan kelebihan penjualan.
Lanjutan 5: Mengapa tidak melindungi setiap kursi dengan layanan kunci terdistribusi yang ada?
Ketika inventaris otoritatif sudah ada dalam database relasional dengan penulisan kondisional dan transaksi pendek, layanan kunci terpisah menciptakan status kedua yang harus selaras dengan inventaris, ditambah batas kedaluwarsa lease dan pagar. Kunci baris atau pembaruan kondisional menempatkan pengecualian dan penulisan tahan lama dalam satu transaksi dan lebih sederhana. Pertimbangkan kunci terdistribusi hanya ketika inventaris mencakup penyimpanan yang tidak dapat bertransaksi bersama atau ketika bagian kritis melindungi sumber daya eksternal. Bahkan dalam kasus itu, penyimpanan tahan lama tetap memerlukan versi atau token pagar untuk menolak pemegang yang terlambat.