Panduan Transaksi

Pengantar

Saat menggunakan library C/C++ yang tidak di-sandbox, penaut akan memastikan semua fungsi yang diperlukan tersedia setelah kompilasi sehingga tidak perlu khawatir apakah panggilan API mungkin gagal saat runtime.

Namun, saat menggunakan Library dengan Sandbox, eksekusi library akan berlangsung dalam proses terpisah. Kegagalan dalam panggilan API memerlukan pemeriksaan untuk semua jenis masalah yang terkait dengan penerusan panggilan melalui lapisan RPC. Terkadang, error lapisan PPK mungkin tidak relevan, misalnya saat melakukan pemrosesan massal dan sandbox baru saja dimulai ulang.

Meskipun demikian, karena alasan yang disebutkan di atas, penting untuk memperluas pemeriksaan error rutin terhadap nilai yang ditampilkan panggilan API dengan sandbox untuk menyertakan pemeriksaan apakah error ditampilkan pada lapisan RPC. Inilah sebabnya semua prototipe fungsi library menampilkan ::sapi::StatusOr<T>, bukan T. Jika pemanggilan fungsi library gagal (misalnya karena pelanggaran sandbox), nilai yang ditampilkan akan berisi detail tentang error yang terjadi.

Penanganan error lapisan RPC akan berarti bahwa setiap panggilan ke Library dengan Sandbox diikuti dengan pemeriksaan tambahan pada lapisan RPC SAPI. Untuk menangani situasi luar biasa tersebut, SAPI menyediakan modul Transaksi SAPI (transaction.h). Modul ini berisi class ::sapi::Transaction dan memastikan bahwa semua panggilan fungsi ke Library dengan Sandbox diselesaikan tanpa masalah tingkat RPC, atau menampilkan error yang relevan.

Transaksi SAPI

SAPI mengisolasi Kode Host dari Library Sandbox dan memberi pemanggil kemampuan untuk memulai ulang atau membatalkan permintaan pemrosesan data yang bermasalah. Transaksi SAPI berjalan selangkah lebih maju dan secara otomatis mengulangi proses yang gagal.

Transaksi SAPI dapat digunakan dengan dua cara yang berbeda: mewarisi secara langsung dari ::sapi::Transaction, atau menggunakan pointer fungsi yang diteruskan ke ::sapi::BasicTransaction.

Transaksi SAPI ditentukan dengan mengganti tiga fungsi berikut:

Metode Transaksi SAPI
::sapi::Transaction::Init() Ini mirip dengan memanggil metode inisialisasi library C/C++ standar. Metode ini hanya dipanggil sekali selama setiap transaksi ke Library Sandbox, kecuali jika transaksi dimulai ulang. Jika mulai ulang, metode akan dipanggil lagi, terlepas dari berapa banyak mulai ulang yang telah terjadi sebelumnya.
::sapi::Transaction::Main() Metode ini dipanggil untuk setiap panggilan ke ::sapi::Transaction::Run() .
::sapi::Transaction::Finish() Ini mirip dengan memanggil metode pembersihan library C/C++ pada umumnya. Metode ini hanya dipanggil sekali selama penghancuran objek Transaksi SAPI.

Penggunaan Library Normal

Dalam project tanpa library dalam sandbox, pola yang biasa digunakan saat menangani library akan terlihat seperti ini:

LibInit();
while (data = NextDataToProcess()) {
  result += LibProcessData(data);
}
LibClose();

Library diinisialisasi, lalu fungsi library yang diekspor digunakan, dan terakhir fungsi akhir/tutup dipanggil untuk membersihkan lingkungan.

Penggunaan Library dengan Sandbox

Dalam project dengan library dalam sandbox, kode dari Penggunaan Library Normal diterjemahkan ke dalam cuplikan kode berikut saat menggunakan transaksi dengan callback:

// Overwrite the Init method, passed to BasicTransaction initialization
::absl::Status Init(::sapi::Sandbox* sandbox) {
  // Instantiate the SAPI Object
  LibraryAPI lib(sandbox);
  // Instantiate the Sandboxed Library
  SAPI_RETURN_IF_ERROR(lib.LibInit());
  return ::absl::OkStatus();
}

// Overwrite the Finish method, passed to BasicTransaction initialization
::absl::Status Finish(::sapi::Sandbox *sandbox) {
  // Instantiate the SAPI Object
  LibraryAPI lib(sandbox);
  // Clean-up sandboxed library instance
  SAPI_RETURN_IF_ERROR(lib.LibClose());
  return ::absl::OkStatus();
}

// Wrapper function to process data, passed to Run method call
::absl::Status HandleData(::sapi::Sandbox *sandbox, Data data_to_process,
                           Result *out) {
  // Instantiate the SAPI Object
  LibraryAPI lib(sandbox);
  // Call the sandboxed function LibProcessData
  SAPI_ASSIGN_OR_RETURN(*out, lib.LibProcessData(data_to_process));
  return ::absl::OkStatus();
}

void Handle() {
  // Use SAPI Transactions by passing function pointers to ::sapi::BasicTransaction
  ::sapi::BasicTransaction transaction(Init, Finish);
  while (data = NextDataToProcess()) {
    ::sandbox2::Result result;
    // call the ::sapi::Transaction::Run() method
    transaction.Run(HandleData, data, &result);
    // ...
  }
  // ...
}

Class transaksi memastikan untuk menginisialisasi ulang library jika terjadi error selama pemanggilan handle_data. Selengkapnya di bagian berikut.

Mulai Ulang Transaksi

Jika panggilan API library dalam sandbox memunculkan error selama eksekusi metode Transaksi SAPI (lihat tabel di atas), transaksi akan dimulai ulang. Jumlah mulai ulang default ditentukan oleh kDefaultRetryCnt dalam transaction.h.

Contoh error yang dilaporkan yang akan memicu mulai ulang adalah:

  • Terjadi pelanggaran sandbox
  • Proses yang di-sandbox mengalami error
  • Fungsi dalam sandbox menampilkan kode error karena terjadi error library

Prosedur mulai ulang mengamati alur Init() dan Main() yang normal, dan jika panggilan berulang ke metode ::sapi::Transaction::Run() akan menampilkan error, maka seluruh metode akan menampilkan error ke pemanggilnya

Penanganan Error Sandbox atau RPC

Antarmuka Library dengan Sandbox yang dibuat otomatis berusaha semirip mungkin dengan prototipe fungsi library C/C++ asli. Namun, Library dengan Sandbox harus dapat menandai error sandbox atau RPC.

Hal ini dapat dilakukan dengan menggunakan jenis nilai yang ditampilkan ::sapi::StatusOr<T> (atau ::sapi::Status untuk fungsi yang menampilkan void), bukan menampilkan nilai fungsi yang di-sandbox secara langsung.

SAPI selanjutnya menyediakan beberapa makro yang mudah digunakan untuk memeriksa dan bereaksi terhadap objek Status SAPI. Makro ini ditentukan dalam file header status_macro.h.

Cuplikan kode berikut adalah kutipan dari contoh sum dan menunjukkan penggunaan Status SAPI dan makro:

// Instead of void, use ::sapi::Status
::sapi::Status SumTransaction::Main() {
  // Instantiate the SAPI Object
  SumApi f(sandbox());

  // ::sapi::StatusOr<int> sum(int a, int b)
  SAPI_ASSIGN_OR_RETURN(int v, f.sum(1000, 337));
  // ...

  // ::sapi::Status sums(sapi::v::Ptr* params)
  SumParams params;
  params.mutable_data()->a = 1111;
  params.mutable_data()->b = 222;
  params.mutable_data()->ret = 0;
  SAPI_RETURN_IF_ERROR(f.sums(params.PtrBoth()));
  // ...
  // Gets symbol address and prints its value
  int *ssaddr;
  SAPI_RETURN_IF_ERROR(sandbox()->Symbol(
      "sumsymbol", reinterpret_cast<void**>(&ssaddr)));
  ::sapi::v::Int sumsymbol;
  sumsymbol.SetRemote(ssaddr);
  SAPI_RETURN_IF_ERROR(sandbox()->TransferFromSandboxee(&sumsymbol));
  // ...
  return ::sapi::OkStatus();
}

Mulai Ulang Sandbox

Banyak library dalam sandbox menangani input pengguna yang sensitif. Jika library dalam sandbox pada suatu saat rusak dan menyimpan data antar-proses, data sensitif ini berisiko. Misalnya, jika library Imagemagick versi sandbox mulai mengirimkan gambar proses sebelumnya.

Untuk menghindari skenario tersebut, sandbox tidak boleh digunakan kembali untuk beberapa operasi. Untuk menghentikan penggunaan kembali sandbox, Kode Host dapat memulai mulai ulang proses library dalam sandbox menggunakan ::sapi::Sandbox::Restart() atau ::sapi::Transaction::Restart() saat menggunakan Transaksi SAPI.

Mulai ulang akan membatalkan referensi apa pun ke proses library dalam sandbox. Artinya, deskriptor file yang diteruskan, atau memori yang dialokasikan tidak akan ada lagi.