Semantik eksekusi, batasan, dan keamanan
Halaman ini merangkum bagaimana Script berperilaku saat runtime (urutan, transaksi, error, penguncian), batasan statis apa yang dikenakan saat disimpan, dan model keamanannya. Untuk sintaksis, lihat Katalog Statement dan Ekspresi nilai; untuk kombinasi praktis, lihat Cookbook.
Urutan eksekusi
statementsdieksekusi secara berurutan dari atas ke bawah. Ketika mencapaiReturn, eksekusi berhenti di titik tersebut.- Eksekusi berlangsung secara inline, pada jalur yang menangani permintaan panggilan. Respons panggilan itu sendiri adalah hasil eksekusi (untuk bentuk responsnya, lihat Permintaan dan respons di Ikhtisar Script), dan waktu yang diberikan untuk satu eksekusi dibahas di Anggaran waktu di bawah.
Semantik eksekusi
Guard (prakondisi)
Tidak ada statement guard khusus. Anda menyatakannya dengan If dan then:[Return]. Ketika kondisi dilanggar, ia mengembalikan hasil dan tidak menjalankan statement selanjutnya (tentu saja Script tanpa guard juga dimungkinkan).
{ "type": "If", "condition": { "<": [ "{ /wallet/fields/balance/en-US }", "{ /payload/fields/cost }" ] },
"then": [ { "type": "Return", "value": { "ok": false, "reason": "insufficient credit" }, "statusCode": 402 } ] }Tanpa transaksi dan kompensasi best-effort
Script bukanlah transaksi. Saat gagal, engine mencoba mengompensasi (compensation) pekerjaan yang telah dilakukan sejauh ini dan mengembalikan penyebab error, tetapi dengan keterbatasan berikut (diterima sebagai konsekuensi desain).
- Membatalkan penghapusan membuat
sys.idbaru, sehingga referensi yang tadinya menunjuk ke sana menjadi rusak. - Efek eksternal (
Http) bersifat ireversibel (panggilan yang sudah keluar beserta biayanya tidak dapat dibatalkan). - Kompensasi bisa sama sekali tidak berjalan, sehingga keadaan yang belum terkompensasi dapat tertinggal.
Jika Anda membutuhkan atomisitas sejati, tulis sendiri kompensasi di dalam Script tersebut, atau tempatkan operasi yang tidak dapat dibatalkan (misalnya panggilan eksternal) di paling akhir. Urutan yang paling berbahaya adalah yang "berhasil dirantai tetapi tidak dapat di-rollback, namun tampak aman."
Penguncian optimis
Persaingan update/patch dipersempit dengan version pada ResourceUpdate dan ResourcePatch. Jika Anda memberikan version (ekspresi nilai, Int), pembaruan dilakukan hanya jika cocok dengan sys.version target saat ini; ketidakcocokan di-abort karena error konflik versi (dapat ditangani secara lokal dengan Try/catch). Jika dihilangkan, berlaku last-write-wins tanpa pemeriksaan. Biasanya Anda membaca terlebih dahulu dengan ResourceRead atau ResourceFind lalu meneruskan sys.version tersebut (lihat CAS penguncian optimis di Cookbook).
Penulisan berbasis origin
Penulisan selalu tercermin di origin (draft), dan apakah diekspos ke delivery (CDA/ACDA) dikendalikan oleh publish (publish pada ResourceCreate/ResourceUpdate/ResourcePatch, atau ResourcePublish/ResourceUnpublish).
Apa yang dianggap sebagai kegagalan
- Kegagalan sejati adalah error runtime statement: status akhir
Http400 atau lebih (4xx·5xx; bukan kegagalan jikaignoreStatusCode: true) atau timeout, body respons melebihi 10MiB, atau operasi resource yang gagal (target tidak ada, konflik versi, operasi yang tidak didukung, dsb.). Untuk kegagalan semacam ini engine akan meng-abort dan mengompensasi, dan Anda dapat menanganinya secara lokal denganTry/catch/finally. Returnbukanlah error, melainkan penghentian dini yang normal. Ia bukan targetcatch(tidak ada konsep user-throw).- Di dalam
catch, Anda merujuk{ message }melalui/error. Statement mana yang gagal tidak disertakan di dalamnya.
Agregasi sisi server hanya untuk jumlah
Jumlahnya dihitung di sisi server oleh ResourceCount. Karena ia tidak membaca itemnya, ia tidak terikat pada batas atas jumlah item yang diproses.
Untuk sum dan group-by tidak ada operasi server khusus. Agregasi semacam itu harus Anda hitung sendiri dengan menelusuri ResourceForEach lalu menggunakan SetVar dan JsonLogic, sehingga terikat pada batas atas jumlah item yang diproses (tidak cocok untuk mengagregasi jutaan record). Jika Anda hanya membutuhkan jumlahnya, gunakan ResourceCount alih-alih menelusuri.
Tanpa penungguan atau penundaan
Script tidak memiliki statement Delay. Script dieksekusi sekali lalu selesai, dan tidak menunggu atau melakukan polling secara internal hingga job eksternal selesai.
Batasan statis (divalidasi saat disimpan)
Berikut ini diperiksa pada saat Script disimpan (pembuatan/perubahan). Jika dilanggar, penyimpanan ditolak (gagal saat penyusunan, bukan saat runtime). Pelanggaran mana yang ditolak dengan kode mana dibahas di Error.
| Batasan | Nilai |
|---|---|
Panggilan eksternal maksimum per definisi (Http·EmailSend) | Per paket (lihat Paket Harga) |
Total item yang diproses ResourceForEach maksimum (jika limit yang dideklarasikan tidak ada, menelusuri hingga nilai ini; gagal jika tercapai sementara masih ada kecocokan) | 10.000 |
SetVar maksimum per definisi (termasuk yang bersarang) | 10 |
Cache maksimum per definisi (termasuk yang bersarang, dijumlahkan tanpa memandang operasinya) | 5. Jika melebihi, penyimpanan ditolak |
Cache di dalam blok Loop·ResourceForEach | Penyimpanan ditolak |
Cache.key | Hanya literal, maksimal 128 karakter. Jika berupa ekspresi nilai, penyimpanan ditolak |
Cache.ttl | Antara 1 dan 30 detik; jika dihilangkan, 5 detik. Jika di luar rentang itu, penyimpanan ditolak |
| Total statement maksimum per definisi (termasuk yang bersarang) | Per paket (lihat Paket Harga) |
Batas atas Http.retry | 2 |
Panjang Regex.pattern | 128 karakter |
| Statement yang mengubah ServiceUser | Penyimpanan ditolak. Hanya tiga statement pembacaan yang menerima resource ini |
Jika anonymousCallEnabled bernilai true, createdBy: ":self" pada where | Penyimpanan ditolak |
Batas tetap pada tabel di atas adalah nilai yang ditetapkan platform, sehingga sama tanpa memandang paket. Sebaliknya, jumlah total statement dan jumlah panggilan eksternal per definisi adalah batas per paket. Karena keduanya bukan error validasi melainkan batas paket, jika terlampaui, penyimpanan/pengubahan ditolak karena melampaui batas paket (definisi yang sama diperbolehkan pada paket yang lebih tinggi) dan dibuka dengan peningkatan paket. Angka per paket ada di Paket Harga.
Berbeda dengan panggilan eksternal seperti
Http·EmailSend, penyerapan file Media tidak dihitung terhadap batas panggilan eksternal per definisi.
ResourceForEachadalah statement komposit yang memiliki anak, sehingga statement itu sendiri tidak dihitung ke jumlah panggilan eksternal. Statement panggilan eksternal (Http·EmailSend) di dalamonEach-lah yang dihitung (secara statis dihitung sebagai 1, tetapi benar-benar dijalankan untuk tiap item selama penelusuran).onEachdapat memuat panggilan eksternal atau penyerapan file Media, dan hal ini juga berlaku untuk bodyLoop. Berapa kali perulangan itu benar-benar berputar tidak masuk ke hitungan ini, melainkan masuk sebagai perkalian pada Anggaran waktu di bawah.
Batas atas panjang nilai (runtime)
Statement tanda tangan dan pemrosesan teks, serta Cache, memiliki batas atas untuk ukuran nilai yang ditanganinya. Yang dibatasi bukan panjang ekspresinya, melainkan panjang nilai hasil resolve dari ekspresi itu (enam belas karakter { /rawPayload } menunjuk ke puluhan KB), sehingga pemeriksaannya terjadi selama eksekusi, bukan saat penyimpanan.
- Keempatnya sama seperti kegagalan runtime lainnya sehingga dapat ditangani secara lokal dengan
Try/catch. - Batas atas
Signaturedisesuaikan dengan ukuran body yang benar-benar dikirim penyedia (peristiwa pembayaran berukuran beberapa KB, dan webhook pesanan mencapai puluhan KB).Hashmenempati posisi yang menyambung beberapa field saja, sehingga jauh lebih sempit. - Angka 128 karakter untuk
Regex.patternadalah pemeriksaan saat penyimpanan pada Batasan statis di atas. Panjang itu bukan perangkat untuk mencegah ledakan komputasi ((a+)+$sudah berbahaya hanya dengan enam karakter). Yang mencegah ledakan adalah aturan yang mengharuskan pattern ditulis sebagai literal serta anggaran waktu di bawah; yang dijanjikan panjang itu hanyalah ukuran yang masih dapat dibaca dan ditinjau manusia.
Anggaran waktu (runtime)
Waktu yang diberikan untuk satu eksekusi ditentukan oleh satu rumus: min(30 detik + jumlah waktu yang dideklarasikan statement, 180 detik).
- Anggarannya dihitung dari Script itu sendiri. Yang ditambahkan ke anggaran dasar hanyalah waktu yang dideklarasikan oleh definisi. Satu-satunya waktu terdeklarasi adalah
timeoutMspadaHttpdanEmailSend.HttpmemakaitimeoutMsmiliknya lagi pada setiap percobaan ulang, sehingga dihitung sebagaitimeoutMs × (1 + retry), sedangkanEmailSendtidak melakukan percobaan ulang sehingga dihitung satu kali. JikatimeoutMstidak ditulis, yang dihitung adalah nilai bawaannya (Http30 detik,EmailSend10 detik). - Pekerjaan tanpa waktu terdeklarasi diambil dari anggaran dasar 30 detik. Pembacaan dan penulisan resource, penyerapan file Media, serta pekerjaan yang dilakukan perulangan di dalamnya termasuk di sini. Karena itu anggaran dasar bukan angka formalitas, melainkan jatah yang sesungguhnya.
- Cara menjumlahkannya mengikuti struktur statement. Statement yang diletakkan berurutan dijumlahkan,
Ifmengambil yang lebih besar di antara dua cabangnya, danParallelmengambil yang terbesar di antara cabang-cabangnya.Loopmengalikan body dengan jumlah iterasi (maxIterations, 10.000 jika tidak dideklarasikan), danResourceForEachmengalikanonEachdengan jumlah item yang diproses (limit, 10.000 jika tidak dideklarasikan). - Perulangan tanpa panggilan eksternal memiliki waktu terdeklarasi 0. Karena itu anggaran dasar 30 detik menjadi batas yang sesungguhnya, dan di titik inilah Script yang memuat perulangan benar-benar terhenti.
- Batas atas 180 detik tidak menghalangi penyimpanan, melainkan memotong eksekusi. Meskipun hasil perhitungannya melampaui batas atas, Script itu tetap tersimpan dan tetap berjalan, lalu berhenti begitu mencapai 180 detik.
Batas jumlah per paket
Untuk Script, jumlah per Organization dibatasi oleh paket.
| Paket | Jumlah Script |
|---|---|
| Free | 10 |
| Basic | 30 |
| Pro | 100 |
| Enterprise | Tak terbatas |
Terpisah dari itu, jumlah statement dan jumlah panggilan eksternal (Http·EmailSend) yang dapat dimuat oleh satu definisi Script juga dibatasi oleh paket. Saat menyimpan/mengubah definisi, jika melampaui batas paket tersebut, permintaan ditolak; untuk angka konkretnya, lihat Paket Harga.
Ketika batas tercapai, pembuatan Script baru ditolak.
Model keamanan
Header secret
Item pada Http.headers yang memiliki secret:true bersifat khusus CMA (administrator): nilainya tidak diekspos ke end-user (ServiceUser) dan hanya didekripsi tepat sebelum dikirim. Simpan rahasia seperti kunci API LLM di sini (bahkan saat dipaketkan menjadi App Bundle, nilai secret tetap disamarkan dan tidak pernah keluar dari Space asalnya).
secret pada Signature tidak mendapat perlakuan ini di dalam Space. Nilainya tidak dienkripsi dan disimpan persis seperti yang Anda tulis di definisi, sehingga role yang dapat membaca Script itu dapat melihat nilainya. Anggota (ServiceUser) tidak dapat membaca definisi Script (pembacaan dan penyusunannya khusus CMA, dan ACMA tidak memiliki Script API). Untuk Script yang menyimpan kunci verifikasi, lebih aman mempersempit role yang dapat membacanya.
Ketika keluar dari Space, ceritanya lain. Saat Script itu dipaketkan menjadi App Bundle, secret pada Signature disamarkan dan tidak keluar dari Space asalnya. Pada Http.headers, yang disamarkan adalah item yang membawa flag secret beserta header Authorization, sedangkan pada Signature, secret disamarkan tanpa syarat apa pun karena field itu sendiri merupakan kunci tanda tangan. Signature yang tersarang di dalam If·Loop·Try juga ikut disamarkan.
Identitas eksekusi dan otorisasi
- Identitas eksekusi: selama eksekusi, setiap operasi resource dilakukan dengan identitas pengguna yang memanggil
/execute.createdBy/updatedBydari resource yang dibuat atau diubah adalah pemanggil, dan cakupancreatedBy: ":self"juga diuraikan berdasarkan pemanggil. Pengecualiannya adalah panggilan anonim. Eksekusi yang masuk melalui/execute/anonymoustidak memiliki pemanggil, sehingga keduanya diuraikan berdasarkan penulis (Panggilan anonim). - Ada dua batas otorisasi, dan saat runtime izin resource tidak diperiksa ulang pada setiap statement.
- Saat penyusunan (penyimpanan): ketika sebuah Script disimpan, sistem memeriksa apakah penulis benar-benar memiliki izin resource dan aksi yang dipakai oleh statement-statement di dalamnya. Jika satu saja tidak ada, penyimpanan ditolak. Dengan kata lain, Script yang memuat operasi tanpa izin memang tidak akan pernah tersimpan sejak awal. Statement yang memilih resource, baik berupa leaf maupun
ResourceForEachyang memiliki blok, semuanya menjalani pemeriksaan ini. Definisi yang sudah tersimpan pun diperiksa ulang ketika diubah, sehingga setelah izinnya dicabut, definisi itu tidak dapat diperbaiki dan disimpan lagi.- Direktori anggota (ServiceUser) diperiksa bukan lewat map izin, melainkan lewat sumbu pengaturan. Untuk memakai
resource: "ServiceUser"pada tiga statement pembacaan,settingspada SpaceRole penulis harus memuatSETTING_SERVICE_LOGIN(atauSETTING_ALL) (settings pada SpaceRole). Alasannya, direktori anggota adalah resource yang dikelola pengaturan Space di semua jalur lainnya juga. - Statement yang mengubah anggota tidak dapat disimpan dengan role apa pun. Karena di Script memang tidak ada jalan untuk membuat, mengubah, atau menghapus anggota, penolakannya bukan karena izin kurang (
403) melainkan karena statement yang salah tulis (400). Artinya, ini bukan celah yang bisa ditutup dengan menambah izin.
- Direktori anggota (ServiceUser) diperiksa bukan lewat map izin, melainkan lewat sumbu pengaturan. Untuk memakai
- Saat pemanggilan (
/execute): hanya izin Execute Script milik pemanggil yang diperiksa. Tanpa izin itu, hasilnya403. Jika lolos, izin resource per statement tidak diperiksa lagi saat runtime; eksekusi langsung berjalan. Cara kerjanya seperti izin eksekusi fungsi dalam pemrograman. Jika Anda memiliki izin untuk menjalankan fungsi tersebut, izin untuk setiap operasi individual di dalamnya tidak ditanyakan lagi. Pada jalur panggilan anonim, pemeriksaan ini tidak ada. Sebab tidak ada pemanggil yang bisa diperiksa, sehingga membuka jalur itu setara dengan mempublikasikan satu Script tanpa autentikasi.
- Saat penyusunan (penyimpanan): ketika sebuah Script disimpan, sistem memeriksa apakah penulis benar-benar memiliki izin resource dan aksi yang dipakai oleh statement-statement di dalamnya. Jika satu saja tidak ada, penyimpanan ditolak. Dengan kata lain, Script yang memuat operasi tanpa izin memang tidak akan pernah tersimpan sejak awal. Statement yang memilih resource, baik berupa leaf maupun
- Pemblokiran pemanggilan langsung (
directCallEnabled): jikadirectCallEnableddari Script bernilaifalse, pemanggilan langsung/executeitu sendiri ditolak. Gerbang ini terkena setelah pemeriksaan izin Execute dilewati, sehingga pemanggilan tetap terblokir meskipun Anda memiliki izin Execute. Pemanggil yang tidak memiliki izin menerima403sebelum mencapai gerbang ini. Karena gerbang ini hanya ada pada endpoint tersebut, aksi terhubung (script) pada Webhook dan Scheduler tetap menjalankannya seperti biasa. Nilai bawaannya adalahtrue(memperbolehkan pemanggilan langsung). - Panggilan anonim (
anonymousCallEnabled): nilai bawaannya adalahfalse. Jika diseteltrue, hanya Script itu yang juga dapat dijalankan melalui jalur khusus tanpa autentikasi (/execute/anonymous), dan saat itu identitas eksekusinya adalah penulis, bukan pemanggil. Dari kedua batas di atas, pemeriksaan saat pemanggilan (izin Execute) tidak ada pada jalur itu, sehingga autentikasi yang sebenarnya dilakukan oleh Script itu sendiri (verifikasi tanda tangan atas permintaan yang diterima). Syarat mengaktifkannya dan aturan penyimpanannya dibahas di Panggilan anonim. - Cakupan kepemilikan:
createdBy: ":self"pada filterwhereberarti "hanya yang dibuat oleh pemanggil saat ini" (misalnya, hanya membaca dompet milik Anda sendiri). Pada Script yang mengizinkan panggilan anonim, filter ini tidak dapat dipakai. Karena tidak ada pemanggil, ia diuraikan menjadi penulis, sehingga makna aslinya sebagai cakupan kepemilikan tidak lagi berlaku. - Izin yang didelegasikan (perhatian bagi penulis): menggabungkan kedua batas di atas, menjalankan sebuah Script setara dengan bertindak dengan izin penulis yang didelegasikan kepadanya. Pemanggil hanya perlu Execute, dan statement di dalam Script berjalan persis dalam cakupan yang diotorisasikan kepada penulis saat disimpan. Akibatnya, operasi resource yang tidak dapat dilakukan sendiri oleh pemanggil pun tetap bisa terjadi melalui Script. Karena izin yang diberikan kepada penulis itulah jangkauan efektif dari Script tersebut, tentukan dengan cermat operasi apa yang Anda masukkan ke dalam Script.
Daftar periksa ringkas
Sebelum menyimpan, periksa hal-hal berikut.
- Jika Anda mengaktifkan panggilan anonim (
anonymousCallEnabled), tidak adacreatedBy: ":self"padawhere, dan statement yang memverifikasi permintaan yang diterima (Signature, dan sejenisnya) diletakkan di paling depan. - Jumlah panggilan eksternal (
Http·EmailSend) dan total statement berada dalam batas paket,SetVar10 atau kurang, danCache5 atau kurang. - Jika memakai
Cache, Anda menuliskeysebagai literal dan tidak meletakkannya di dalamLoopatauResourceForEach. - Jika menelusuri kumpulan besar dengan
ResourceForEach, Anda telah mendeklarasikanlimitatau memastikan ukurannya dapat diselesaikan seluruhnya. - Jika Anda memuat perulangan (
Loop·ResourceForEach), Anda sudah memastikan bahwa perulangan itu masuk sebagai perkalian ke Anggaran waktu (tanpa panggilan eksternal, batasnya adalah anggaran dasar 30 detik). - Nilai secret hanya dimasukkan melalui
secret:truepadaHttp.headers(karenaSignature.secrettidak disimpan terenkripsi, Anda sudah memeriksa role yang dapat membaca Script tersebut). - Pesan yang dipakai untuk verifikasi tanda tangan diambil dengan
{ /rawPayload }, bukan/payload. - Jika ada statement yang membaca anggota (ServiceUser), penulis memiliki
SETTING_SERVICE_LOGIN, dan Anda tidak menyertakan statement yang mengubah resource tersebut. - Operasi yang tidak dapat dibatalkan (panggilan eksternal) ditempatkan sebisa mungkin di bagian akhir.
- Jika Anda mengkhawatirkan persaingan update/patch, gunakan
versionpadaResourceUpdateatauResourcePatch. - Untuk mengembalikan hasil, Anda menentukan
Return.value.
Error
Berikut adalah kode yang muncul saat penyimpanan ditolak karena bentuk definisi melanggar batasan statis. Kode yang melanggar aturan ekspresi nilai ada di Error pada Ekspresi nilai, sedangkan kode yang muncul saat pemanggilan dan penghapusan ada di Error pada endpoint. Untuk kode yang berlaku umum pada semua resource, lihat Error umum.
| Kode | Kondisi |
|---|---|
WGL400066 | Jumlah statement Cache dalam satu definisi melebihi 5. |
WGL400068 | Anda menempatkan statement Cache di dalam blok Loop atau ResourceForEach. |
WGL400067 | Anda menulis referensi { /pointer } pada key statement Cache, bukan literal. |
WGL400065 | ttl pada statement Cache berada di luar rentang yang diizinkan. |
WGL400063 | Anda menulis field yang tidak sesuai dengan action pada statement Cache (ttl pada Get, defaultValue pada Set). |
WGL400060 | Anda menulis "ServiceUser" pada resource milik statement penulisan (ResourceCreate·ResourceUpdate·ResourcePatch·ResourceDelete serta statement publikasi dan pengarsipan). |
WGL400061 | Anda menulis createdBy: ":self" pada where statement pembacaan di dalam Script yang mengizinkan panggilan anonim (anonymousCallEnabled). |
WGL400023 | Jumlah statement SetVar dalam satu definisi melebihi 10. |
WGL400026 | retry pada statement Http melebihi batas atas 2. |
WGL400036 | ResourceForEach melebihi batas atas jumlah item yang dapat diproses. |
WGL429005 | Total jumlah statement dalam satu definisi melebihi batas paket. |
WGL429006 | Jumlah panggilan eksternal (Http·EmailSend) dalam satu definisi melebihi batas paket. |
WGL403015 | Penulis tidak memiliki izin atas resource dan aksi yang ditangani statement di dalam definisi itu. Meskipun izin itu ada, permintaan tetap ditolak bila izin tersebut disertai filter contentType, createdBy, atau tag. Izin itu harus berupa izin tanpa syarat. Satu-satunya pengecualian adalah Create pada Content: untuk aksi itu izin yang dibatasi cakupan contentType pun diterima, dan cakupan itu dibandingkan dengan contentType yang ditulis pada statement tersebut (Create pada Media tidak memiliki pengecualian ini). Kasus ketika resource pada statement pembacaan diisi "ServiceUser" sedangkan penulis tidak memiliki SETTING_SERVICE_LOGIN juga memakai kode ini. |
Dokumen terkait
- Ekspresi nilai: aturan nilai dan kondisi.
- Katalog Statement: field dan hasil dari setiap statement.
- Cookbook: kumpulan contoh yang lengkap.
- Resource dan endpoint Script: struktur resource
Scriptdan endpoint HTTP seperti/execute. - Ikhtisar Script: struktur tingkat atas dan waktu yang diberikan untuk satu eksekusi.
