Bagaimana Saya Memindahkan Antarmuka Keluar dari Mesin Pinjaman
On this page
- Pendahuluan
- A. GPU pinjaman, dan halaman yang ikut terbawa
- B. Apa yang berubah ketika tak ada server di tengah
- C. Delapan hambatan yang menghalangi
- Metode
- A. Antarmuka yang hidup di dalam runtime
- B. Ketika susunannya sendiri yang berisiko
- C. Menjadikan runtime pekerja murni
- D. Membiarkan storage yang menyajikan data
- E. Kontrak yang tak ditegakkan siapa pun
- F. Menulis permintaan sebelum mengirim pemberitahuan
- G. Pekerjaan yang bukan milik sisi mana pun
- H. Antarmuka yang tak ada
- Hasil dan Pembahasan
- A. Apa yang dijumlahkan kedelapan keputusan itu
- B. Kesepakatanlah server-nya sekarang
- C. Yang tidak dicakup
- Simpulan
- Istilah
Pendahuluan
A. GPU pinjaman, dan halaman yang ikut terbawa
GPU laptop saya punya video memory 4 GB, dan itu tak cukup untuk model di balik beberapa project saya: pembuatan gambar, konversi dokumen, dan speech-to-text. Jadi bagian berat masing-masing berjalan di runtime Google Colab dengan T4 dan sekitar 15 GB VRAM — mesin yang dipinjam selama satu sesi.
Tiap project itu butuh antarmuka. Harus ada tempat saya bisa mengunggah bahan, memulai pekerjaan, memantau kemajuan, dan melihat hasil. Tempat paling alami untuk antarmuka itu ada tepat di sebelah pekerjaannya, di dalam runtime yang sama: web server kecil di dalam notebook yang menyajikan satu halaman, dengan proxy port milik Colab sendiri mengubahnya jadi URL yang bisa saya buka di browser. Ketiga project dimulai dengan cara itu. Tak ada yang salah dengan pilihan itu sebagai permulaan; ia pilihan yang lazim dan cepat, dan ia membuktikan bahwa pekerjaannya bisa dikerjakan sama sekali.
Cara itu bekerja, dan untuk versi pertama ia sulit dikalahkan. Tak ada yang perlu dipasang maupun di-hosting, dan antarmukanya bisa memanggil model langsung karena hidup di proses yang sama. Tapi segala sesuatu tentangnya ikut dipinjam bersama GPU-nya. Ketika sesi berakhir, halamannya ikut berakhir. Tiap runtime baru menghasilkan URL baru. Dan susunannya sendiri — menyajikan aplikasi web dari dalam runtime Colab lewat proxy-nya — ternyata tak disambut baik oleh Colab. Bahkan untuk melihat hasil kemarin, saya harus menyalakan sesi GPU lebih dulu, menghabiskan kuota cuma untuk membuka sebuah halaman.
Tantangannya: memindahkan antarmuka keluar dari mesin pinjaman — supaya ia tak lagi mati bersama runtime maupun bergantung pada apa yang diizinkan Colab — sementara runtime tetap mengerjakan pekerjaan GPU.
Gampang dirumuskan, canggung dibangun. Memindahkan halaman itu sederhana. Yang tidak sederhana adalah segala yang selama ini dikerjakan web server di tengah tanpa ada yang menyadarinya: menjawab request, memegang state pekerjaan, menerjemahkan error jadi respons, dan menjadi satu-satunya tempat yang disepakati kedua pihak sebagai yang benar. Singkirkan server-nya, dan tiap pekerjaan itu harus pindah ke tempat lain. Tak satu pun ikut lenyap bersama server; semuanya cuma kehilangan rumah, dan tiap satunya perlu rumah baru yang dipilih dengan sadar.
B. Apa yang berubah ketika tak ada server di tengah
Susunan yang dituju project-project itu punya tiga bagian, dan tak satu pun web server. Runtime jadi pekerja murni: memuat model, menunggu pekerjaan, mengerjakannya, lalu menulis hasilnya keluar. Antarmuka berjalan di laptop, di browser. Di antara keduanya ada dua layanan yang sama-sama dihubungi keluar oleh kedua pihak: object storage, yang memegang tiap file dan tiap record, dan message broker, yang mengantar pemberitahuan singkat — "mulai yang ini", "yang itu sudah selesai" — di antara keduanya.
Itu menyingkirkan server-nya, dan bersamanya ikut hilang yang dulu menjamin kedua pihak sepakat. Ketika server menjawab satu request, dialah yang menentukan arti jawaban itu. Tanpa server, antarmuka dan worker adalah dua codebase terpisah, dalam dua bahasa, yang harus sepakat secara mandiri soal ke mana file ditaruh, seperti apa rupa item yang selesai, topic mana membawa pesan apa, dan siapa sumber kebenaran ketika keduanya berselisih. Tak ada yang menegakkan kesepakatan itu selain kesepakatan itu sendiri. Worker-nya Python di dalam notebook; antarmukanya TypeScript di dalam browser. Keduanya tak berbagi code, tak berbagi proses, dan tak punya compiler bersama yang bisa menyadari ketidakcocokan di antara mereka. Kalau keduanya berselisih, tak ada yang berteriak; yang ada cuma pekerjaan yang tak kunjung tampak selesai.
Jadi pekerjaan memindahkan antarmuka keluar sebagian besar adalah pekerjaan membuat kesepakatan itu tertulis, bisa diuji, dan sulit dipatahkan — serta menyadari pekerjaan mana yang lebih baik tak dikerjakan di sisi mana pun. Imbalannya adalah antarmuka yang selalu ada, entah ada runtime yang sedang hidup di belakangnya atau tidak. Membuka hasil kemarin cukup dengan membuka browser; menyalakan GPU jadi keputusan tersendiri, bukan syarat untuk melihat apa pun.
C. Delapan hambatan yang menghalangi
Antarmuka pertama hidup di dalam runtime. Itulah cara paling sederhana yang bekerja, dan ia mengikat umur halaman pada umur sesi pinjaman.
Proxy itu sendiri sumber risikonya. Menyajikan aplikasi web lewat proxy port runtime mengundang peringatan dan pembatasan dari Colab, dan bisa membahayakan akunnya. Risiko semacam itu tak bisa ditawar dengan kode yang lebih rapi, sebab yang dipersoalkan bukan kodenya melainkan tempat ia berjalan.
Runtime harus jadi pekerja murni. Mengeluarkan antarmuka berarti membuang satu web framework, satu halaman, dan delapan belas endpoint dari notebook, tanpa mengusik code yang benar-benar membuat gambar, dokumen, atau transkrip.
Tanpa server, harus ada yang lain yang menyajikan data. Antarmuka tetap butuh tiap gambar dan record, dan saluran pemberitahuan adalah tempat yang keliru untuk mengangkutnya.
Tanpa server, kontrak cuma kesepakatan. Dua codebase dalam dua bahasa harus menghitung nama folder dan topic yang sama, dan ketidakcocokan gagal tanpa suara. Yang tampak dari luar cuma worker yang seolah tak pernah menjawab.
Perintah wajib selamat dari pesan yang hilang. Pemberitahuan bisa jatuh di jalan. Permintaan yang cuma ada di dalam pemberitahuan itu ikut jatuh bersamanya.
Pekerjaan yang dipindah ke browser bisa mendarat di tempat yang salah. Menyiapkan media di browser tampak seperti cara meringankan runtime. Ia menggantung, dan perbaikannya justru berhenti mengerjakannya sama sekali.
Kadang antarmuka terbaik adalah tak ada. Untuk pekerjaan singkat, seluruh susunan antarmuka, storage, dan broker adalah mesin yang lebih besar dari yang pantas bagi pekerjaan itu.
Urutannya mengikuti perpindahan itu sendiri: kenapa antarmukanya harus pergi, jadi apa runtime-nya begitu ia pergi, apa yang menggantikan pekerjaan server, lalu dua pelajaran soal tempat yang tepat bagi sebuah pekerjaan — termasuk kasus ketika jawabannya adalah tak memakai antarmuka sama sekali. Dua pelajaran terakhir itu sengaja ditaruh di ujung, sebab keduanya membatasi seberapa jauh pemindahan ini layak dikejar.
Metode
A. Antarmuka yang hidup di dalam runtime
Inilah jawaban atas hambatan pertama, dimulai dari rupanya sebelum pindah. Versi pertama tiap project menjalankan web server di dalam notebook lalu meminta URL-nya kepada Colab:
=
...
=
Halamannya dan tiap aksi di baliknya hidup dalam satu proses.
Ongkosnya struktural, bukan bug. Antarmuka cuma bisa ada selama runtime ada, jadi meninjau hasil berarti menyalakan sesi GPU. URL-nya baru setiap kali, jadi tak ada alamat tetap untuk kembali. Dan bagian terberat notebook-nya bukan code model melainkan antarmukanya: di project slide show, halamannya saja sudah 48 KB HTML, CSS, dan JavaScript yang tertanam di dalam string Python.
B. Ketika susunannya sendiri yang berisiko
Ini menjawab hambatan kedua, dan inilah yang memaksa perubahannya. Colab memperingatkan serta membatasi penyajian aplikasi web dari dalam runtime lewat proxy-nya. Penilaian yang tercatat saat itu gamblang: susunan itu berisiko melanggar kebijakan Google, dengan akunnya sendiri berpotensi di-banned.
Itu mengubah bingkai keputusannya. Memindahkan antarmuka keluar bukan refactor demi kerapian yang bisa ditunda. Mesin pinjaman menetapkan syarat soal apa yang boleh berjalan di atasnya, dan antarmuka yang di-hosting di dalamnya berada di sisi yang keliru dari syarat itu. Selebihnya dari artikel ini adalah pekerjaan hidup tanpa bagian yang memang harus pergi.
Penggantinya tidak dirancang dari nol. Project pengubah PDF sudah lebih dulu pindah dan sudah dipakai sungguhan, jadi project slide show dan speech meniru bentuknya dan menyalin beberapa file-nya apa adanya, termasuk client storage-nya. Susunan yang sudah terbukti adalah titik berangkat yang lebih baik ketimbang susunan baru.
C. Menjadikan runtime pekerja murni
Ini menjawab hambatan ketiga. Membuang antarmuka dari notebook slide show dikerjakan sebagai commit tersendiri yang isinya cuma penghapusan, supaya bisa ditinjau persis sebagai itu:
# satu commit, isinya cuma penghapusan
Yang bertahan adalah tiap fungsi yang mengerjakan pekerjaan sungguhan — memuat model, membuat gambar, mencatat status. Satu-satunya perubahan di lapisan itu ada pada cara ia melaporkan kegagalan. Error-nya dulu berupa HTTP exception, karena pemanggilnya endpoint HTTP. Tanpa HTTP lagi, status code tak berarti apa-apa:
# error domain: antarmuka butuh pesan yang terbaca, bukan status code
pass
# mis. "project tidak ditemukan"Empat belas raise diubah. Masing-masing menjelaskan sesuatu tentang pekerjaannya — project yang belum dibuka, item yang sedang dibuat — dan sampai ke antarmuka sebagai pesan yang terbaca pada status item itu. Worker yang tersisa tak punya web server sama sekali. Ia memuat model, menunggu, bekerja, lalu menulis.
D. Membiarkan storage yang menyajikan data
Ini menjawab hambatan keempat. Server dulu menyerahkan tiap gambar dan record kepada antarmuka. Tanpa server, antarmuka membacanya langsung dari object storage, dan broker cuma mengangkut pemberitahuan:
| |
Aturan di balik gambar itu: broker tak pernah membawa byte sebuah file. Pesan menyatakan bahwa sesuatu terjadi; bendanya sendiri diambil dari storage. Itu menjaga pesan tetap kecil dan cepat, dan artinya pesan yang hilang cuma mengorbankan pemberitahuan, tak pernah data.
Membaca storage langsung dari browser punya syaratnya sendiri. Tiap gambar diambil lewat presigned URL — tautan yang memberi akses sementara ke satu objek — dan umur tautan itu dipilih dari cara antarmukanya dipakai:
// lebih panjang dari default storage (900 dtk): sesi berlangsung berjam-jam,
// dan tautan yang kedaluwarsa di tengah scroll membuat gambar gagal dimuat
presignTtl: 3600,Akses lintas origin diperiksa langsung ketimbang diandaikan: request preflight ke storage memperlihatkan ia memantulkan origin apa pun, jadi antarmuka baru dan yang lama bisa berjalan berdampingan tanpa mengubah konfigurasi server-nya.
E. Kontrak yang tak ditegakkan siapa pun
Ini menjawab hambatan kelima. Di project speech, aturan yang wajib dimiliki bersama oleh kedua pihak ditulis sebagai tabel, sebab tak ada hal lain yang akan menyatukannya:
| kontrak | worker Python | antarmuka TypeScript |
|---|---|---|
| nama folder | slugify_name() | slugifyName() |
| prefix topic | topic_prefix() | topicHash() |
| format durasi | fmt_duration() | fmtDuration() |
| ambang selesai | DONE_MIN_BYTES | DONE_MIN_BYTES |
| bentuk data | konstanta cell persiapan | types/project.ts |
| topic pesan | konstanta cell persiapan | types/mqtt.ts |
Alasan ia berupa tabel dan bukan komentar ada pada cara ia gagal. Kalau kedua versi fungsi nama folder berselisih, worker menulis ke satu folder dan antarmuka membaca folder lain. Kalau fungsi topic berselisih, tiap pihak mendengarkan topic yang tak pernah dipakai pihak lain. Tak satu pun menghasilkan error. Keduanya menghasilkan sunyi, yang rupanya persis sama dengan "worker-nya lambat".
Jadi tiap pasangan fungsi diuji terhadap masukan yang sama. Ketiga fungsi murni itu menghasilkan keluaran identik di 21 kasus uji sebelum antarmuka dibangun di atasnya. Tabel menyatakan apa yang harus berubah bersamaan; uji membuktikan perubahan itu memang terjadi.
F. Menulis permintaan sebelum mengirim pemberitahuan
Ini menjawab hambatan keenam. Ketika saya meminta worker speech mentranskrip sesuatu, dua hal terjadi: permintaannya dicatat, dan worker diberi tahu. Urutan itulah seluruh rancangannya:
await store.requestConvert(props.name, model.value, language.value, force)
await job
.notifyConvert(props.name, cur.stem, model.value, language.value, force)
.catch(() => {
// broker tak terjangkau: permintaan sudah ada di manifest,
// jadi worker tetap menemukannya saat memindai — cuma lebih lambat
})
startPoll()Permintaan masuk ke manifest project di storage lebih dulu. Baru sesudahnya pemberitahuan dikirim. Kalau pemberitahuannya hilang, tak ada yang ikut hilang: worker menemukan permintaannya saat memindai berikutnya, dan worker yang menerima pemberitahuan dijamin menemukan permintaan yang sudah lengkap menunggunya.
Antarmuka pun tak bergantung pada pemberitahuan untuk menampilkan hasil. Ia memeriksa storage tiap lima detik selama pekerjaan berlangsung. Itu diuji dari ujung ke ujung dengan broker yang tak menghasilkan satu pesan pun, dan transkripnya tetap muncul begitu ditulis. Pemberitahuan membuat segalanya lebih cepat. Storage-lah yang membuatnya benar.
G. Pekerjaan yang bukan milik sisi mana pun
Ini menjawab hambatan ketujuh. Antarmuka speech semula mengekstrak audio dari video di browser sebelum mengunggahnya, memakai build WebAssembly dari ffmpeg — program command-line yang lazim untuk mengonversi audio dan video, dikompilasi ke bentuk yang bisa dijalankan browser. Gagasannya masuk akal: unggahan lebih kecil, runtime lebih ringan.
Library itu bisa menggantung selamanya di "mengekstrak audio". Ia menyalakan worker-nya sebagai module worker, tempat pemanggilan yang dipakainya untuk memuat core-nya sendiri tidak diizinkan. Worker itu gagal selagi masih disiapkan — sebelum ia sempat memasang handler yang akan melaporkan kegagalannya — jadi tak ada error yang pernah kembali, dan promise-nya menunggu tanpa batas, sama saja di beberapa versi library itu.
Ketimbang ditambal, langkah itu dibuang, sebab ternyata ia memang tak pernah dibutuhkan. Library transkripsinya mendekode video secara langsung:
# rekaman yang sama, 10 menit, 40 MB
Transkrip dari keduanya identik kata per kata, di tiap format container yang dicoba. Satu-satunya ongkos mengunggah video utuh adalah waktu unggah — sekitar tujuh detik lebih lama untuk file 31 MB — dan ongkos itu membeli kembali 32 MB WebAssembly, ketergantungan pada content delivery network, dan satu kelas bug yang gagal tanpa berkata apa-apa.
Memindahkan pekerjaan ke antarmuka cuma jadi perbaikan kalau antarmuka memang tempat yang baik bagi pekerjaan itu. Di sini tempat terbaiknya bukan browser dan bukan pula langkah baru di worker, melainkan langkah yang tak perlu ada.
H. Antarmuka yang tak ada
Ini menjawab hambatan kedelapan. Project speech juga punya varian headless: tanpa antarmuka, tanpa storage, tanpa broker. Ia berjalan sepenuhnya di dalam runtime Colab. Media diseret ke satu folder lewat panel file milik Colab, notebook-nya mendaftar apa yang belum ditranskrip, dan saya memilih lewat nomor:
# "kosongkan lalu Enter untuk batal"
=
# tak ada yang dimulaiJawaban kosong berarti batal, bukan "semua" — menekan Enter tanpa sengaja tak boleh memulai berjam-jam transkripsi. Status tak disimpan di mana pun. Ia diturunkan dari ada tidaknya file subtitle di sebelah tiap rekaman, sama seperti worker penuh menurunkannya dari apa yang ada di storage, jadi file yang gagal sekadar ditawarkan lagi di lain waktu.
Saya memakainya dalam dua situasi: satu-dua rekaman yang dibutuhkan cepat, ketika seluruh rantai antarmuka, storage, dan broker adalah mesin yang lebih besar ketimbang yang pantas bagi pekerjaannya; dan ketika antarmuka atau storage sedang tak bisa dipakai, sebagai cara tetap bekerja.
Keberadaannya menyatakan sesuatu tentang rancangan selebihnya. Memindahkan antarmuka keluar adalah jawaban yang tepat untuk satu kelompok kebutuhan — pekerjaan panjang, banyak item, peninjauan dari waktu ke waktu. Jawaban itu bukan satu-satunya, dan untuk pekerjaan singkat dengan satu file, antarmuka yang tak ada tak mungkin gagal.
Hasil dan Pembahasan
A. Apa yang dijumlahkan kedelapan keputusan itu
Project slide show dan speech kini berjalan dengan antarmuka di laptop dan worker di Colab yang tak punya web server. Antarmukanya tetap terbuka sementara runtime datang dan pergi, bisa dipakai meninjau hasil tanpa sesi GPU, dan tak lagi bergantung pada apa pun yang mungkin dibatasi Colab. Project pengubah PDF pindah lebih dulu.
| Keputusan | Yang dibeli | Alternatif yang ditolak |
|---|---|---|
| antarmuka di laptop | selamat dari runtime; alamat tetap | web server di dalam Colab, lewat proxy-nya |
| tiru susunan yang sudah terbukti | perilaku yang dikenal sejak hari pertama | merancang pemisahan dari nol |
| penghapusan dalam commit tersendiri | penghapusan bisa ditinjau sebagai penghapusan | menghapus dan menyambung ulang dalam satu perubahan |
| error domain ketimbang error HTTP | pesan terbaca tanpa status code palsu | mempertahankan HTTP exception tanpa HTTP |
| storage menyajikan data, broker cuma pemicu | pesan hilang tak pernah menghilangkan data | mengirim file lewat broker |
| tabel kontrak plus uji berpasangan | penyimpangan tertangkap sebelum jadi sunyi | memercayai dua codebase tetap selaras |
| permintaan dicatat sebelum pemberitahuan | perintah selamat dari pesan yang hilang | permintaan cuma dibawa pesannya |
| video didekode di worker | transkrip identik, 32 MB code lebih sedikit | mengekstrak audio di browser |
| varian headless untuk pekerjaan singkat | tanpa mesin bila memang tak perlu | selalu memakai rantai penuh |
B. Kesepakatanlah server-nya sekarang
Web server mengerjakan tugas senyap yang gampang terlewat sampai ia hilang: ia satu-satunya tempat yang dipercaya kedua pihak. Singkirkan dia, dan tugas itu tak ikut lenyap. Ia harus diambil alih sesuatu, dan di rancangan ini ia diambil alih kesepakatan — soal nama folder, topic, urutan tulis, dan salinan kebenaran mana yang menang.
Sebagian besar hambatan adalah proses pengalihan itu. Storage mengambil alih memegang kebenaran, dan karena itulah permintaan ditulis ke sana sebelum ada yang diberi tahu. Broker cuma mengambil alih bagian pekerjaan server yang menyangkut waktu, dan karena itulah ia membawa pemicu dan tak pernah data. Tabel kontrak dan uji-ujinya mengambil alih bagian yang menyangkut makna, sebab tanpa server tak ada hal lain yang akan menyadari dua codebase saling menjauh.
Pelajaran umumnya berlaku jauh melampaui Colab. Ketika satu sistem kehilangan bagian tengahnya, apa pun yang dulu dijamin bagian tengah itu harus dialihkan dengan sengaja — atau ia tak lagi terjamin, dan tanda pertamanya biasanya sunyi.
C. Yang tidak dicakup
Antarmuka yang membaca dan menulis storage secara langsung adalah rancangan untuk tool internal yang dipakai satu orang di mesinnya sendiri, dan hanya di situ. Antarmuka yang dibuka bagi orang lain kembali lewat server — atau paling tidak lewat layanan yang membagikan akses berumur pendek dan berlingkup sempit.
Simpulan
GPU 4 GB menaruh pekerjaan berat beberapa project di runtime Colab pinjaman, dan langkah pertama yang alami adalah menaruh antarmukanya di sana juga. Itu bekerja, tapi mengikat halaman pada umur sesi dan bersandar pada penyajian aplikasi web lewat proxy Colab — susunan yang diperingatkan dan dibatasi oleh Colab.
Memindahkan antarmuka keluar menjadikan runtime pekerja murni, dilucuti dari delapan belas endpoint dan satu halaman 48 KB, melaporkan error domain ketimbang error HTTP. Storage mengambil alih penyajian data, dengan broker dibatasi pada pemicu. Tabel kontrak dan uji berpasangan mengambil alih apa yang dulu dijamin server soal makna. Permintaan dicatat sebelum ada yang diberi tahu, jadi pesan yang hilang cuma mengorbankan waktu, bukan pekerjaan. Pekerjaan yang bukan milik sisi mana pun dibuang, bukan ditambal. Dan untuk pekerjaan singkat, antarmuka yang tepat ternyata tak ada sama sekali.
Yang terbawa ke mana-mana adalah pertanyaan ini, tiap kali bagian tengah satu sistem disingkirkan: apa yang selama ini dijaminnya diam-diam — lalu beri tiap jaminan itu rumah baru dengan sengaja.
Istilah
- proxy port
- Fitur Colab yang membuka port di dalam runtime sebagai URL di browser, dan begitulah web server yang berjalan di notebook bisa dijangkau.
- pekerja murni
- Proses yang cuma mengerjakan pekerjaannya — memuat, menunggu, menghitung, menulis — tanpa web server maupun antarmuka sendiri.
- presigned URL
- Tautan ke satu objek di storage, ditandatangani untuk memberi akses sementara tanpa membagikan kredensial storage kepada siapa pun yang memakai tautannya.
- manifest
- Record di storage yang menjelaskan sebuah project dan item-itemnya, termasuk permintaan yang menunggu diproses. Di sini ia sumber kebenaran untuk apa yang sudah diminta.
- headless
- Berjalan tanpa antarmuka apa pun. Pekerjaan dimulai dan dipilih dari notebook itu sendiri, dan hasilnya dibaca langsung dari tempat ia ditulis.
- module worker
- Worker browser yang dinyalakan sebagai modul JavaScript. Beberapa cara lama memuat code, seperti
importScripts(), tak diizinkan di dalamnya.
Technologies covered
- Python
- TypeScript
- FastAPI
- Vue
- FFmpeg
- Google Colab
- MinIO
- MQTT