2.2
Model Data Tabel roasts
Tabel roasts adalah satu-satunya tabel yang dibuat repository ini, dan tabel itu merangkap dua peran: menyimpan hasil roast dan berlaku sebagai cache per pasangan username dan language. Skemanya lahir dari berkas database.sql yang diimpor ke D1 lewat npx wrangler d1 import, baik ke database remote maupun lokal database.sql:2-111 README.md:7-92. Di runtime, kode mengakses tabel ini lewat binding DB yang dideklarasikan sebagai d1_databases bernama githubroast wrangler.toml:28-313; rincian binding dan variabel lain ada di Konfigurasi Environment dan Binding.
Struktur tabel
erDiagram
roasts {
integer id PK
text gh_username
text response
text created_at
text country
text language
}
style roasts fill:#336791,color:#fff
%% fig: Kolom tabel roasts
%% desc: Enam kolom yang dideklarasikan database.sql; id adalah primary key AUTOINCREMENT, sisanya menyimpan isi roast dan metadata permintaan.| Kolom | Tipe | Constraint | Nilai yang di-bind POST /llama |
|---|---|---|---|
id | integer | PRIMARY KEY AUTOINCREMENT | tidak pernah di-bind kode; diisi D1 |
gh_username | text | NOT NULL | username dari body request |
response | text | NOT NULL | completion.choices[0].message.content |
created_at | text | NOT NULL | Math.floor(new Date().getTime() / 1000) |
country | text | NOT NULL | request.cf.country, string kosong bila cf tidak ada |
language | text | NULL | language dari body request |
Nama dan tipe seluruh kolom diambil apa adanya dari database.sql:2-94. Nilai username dan language berasal dari body request src/routes/llama/+server.js:315; nilai roast diambil dari completion.choices[0].message.content src/routes/llama/+server.js:1936; empat nilai berikutnya di-bind berurutan sebagai epoch detik, country dari request.cf, hash sha256(...), dan language src/routes/llama/+server.js:198-2057.
database.sql adalah hasil dump, bukan rangkaian migration: baris pertamanya PRAGMA defer_foreign_keys=TRUE dan baris kesepuluh mengosongkan sqlite_sequence database.sql:18 database.sql:109. Tidak ada direktori migration di repository; satu-satunya sumber skema adalah berkas itu.
Peran tabel sebagai cache
Sebelum menyentuh OpenAI, handler menjalankan SELECT * FROM roasts WHERE gh_username = ? AND language = ? lalu .first(). Bila ada baris, history.response langsung dikembalikan sebagai roast dan model tidak dipanggil src/routes/llama/+server.js:40-4710. Pasangan gh_username dan language karena itu berperan sebagai kunci cache, bukan gh_username saja: satu username yang di-roast dalam bahasa berbeda menempati baris berbeda.
Nilai language yang tersimpan selalu salah satu dari 11 entri validLanguages, karena request dengan bahasa lain ditolak lebih dulu dengan status 400 src/routes/llama/+server.js:15-2711 src/routes/llama/+server.js:33-3812. Kolom language dideklarasikan text NULL database.sql:2-94, tetapi jalur tulis di endpoint ini selalu mengisinya; kolom itu nullable hanya secara deklarasi. Daftar bahasa dan prompt tiap bahasa dibahas di Prompt Multibahasa, sedangkan urutan penanganan request dibahas di Endpoint POST /llama.
Index gh_username_idx
index di tabel ini hanya satu: CREATE INDEX gh_username_idx ON roasts (gh_username) database.sql:1113. Query cache memfilter dua kolom sekaligus, gh_username dan language src/routes/llama/+server.js:40-4710, sehingga index hanya mempersempit pencarian pada bagian username; language disaring setelah baris kandidat dibaca.
index itu juga tidak unik. Tidak ada constraint UNIQUE pada pasangan gh_username dan language di database.sql:2-111, jadi satu pasangan bisa punya beberapa baris, dan .first() mengembalikan baris pertama menurut rencana eksekusi, bukan baris terbaru. Cache bisa menyajikan roast lama meski baris baru sudah tersimpan.
Ketidakcocokan kolom dengan kode
graph LR
stmt["INSERT INTO roasts: gh_username, response, created_at, country, ip_address, language"] --> cek{"cocok dengan schema?"}
cek -->|"lima kolom"| ok["baris tersimpan"]
cek -->|ip_address| gagal["statement gagal dijalankan"]
gagal --> telan["catch kosong membuang error"]
style cek fill:#f59e0b,color:#000
style ok fill:#2d7d46,color:#fff
style gagal fill:#dc2626,color:#fff
style telan fill:#dc2626,color:#fff
%% fig: INSERT yang tidak cocok dengan schema
%% desc: INSERT menyebut enam kolom, tetapi ip_address tidak ada di database.sql, sehingga kegagalan penulisan berhenti tanpa jejak.| Hal | Schema di database.sql | Kode di src/routes/llama/+server.js |
|---|---|---|
ip_address | tidak dideklarasikan | ikut di-bind pada INSERT |
created_at | text | bilangan bulat epoch detik |
| nilai hash IP | tidak ada kolomnya | hasil sha256(...) tidak di-await |
Kolom ip_address muncul di daftar kolom INSERT src/routes/llama/+server.js:195-20614, padahal skema tidak memuatnya database.sql:2-111. Nilai yang di-bind untuk kolom itu adalah hasil sha256(request.headers.get('cf-connecting-ip')) src/routes/llama/+server.js:20315, dan pemanggilan itu tidak memakai await, sehingga argumen yang masuk ke .bind bukan string hash. sha256 sendiri memanggil hex(hash) yang tidak pernah didefinisikan di repository, sehingga Promise hasilnya berakhir reject src/routes/llama/+server.js:215-22116.
⚠️ Kegagalan
INSERTditelan olehcatch {}kosong src/routes/llama/+server.js:20717. Tidak ada log dan tidak ada respons error: pengguna tetap menerima roast, tetapi baris cache tidak pernah tersimpan selamaip_addressbelum ada di skema, sehingga setiaprequestuntuk pasangan yang sama memanggil ulang model.
Memperbaiki ketidakcocokan ini berarti menambahkan kolom ip_address ke skema atau menghapusnya dari daftar INSERT. Keduanya tidak mengubah jalur baca, karena history.response hanya bergantung pada gh_username dan language src/routes/llama/+server.js:40-4710. Alur permintaan lengkap yang memakai tabel ini dijelaskan di Alur Permintaan Roast.