Return Objects ve Meta Data
Ders 8: Return Objects ve Meta Data
Section titled “Ders 8: Return Objects ve Meta Data”Öğrenme Hedefleri
Section titled “Öğrenme Hedefleri”Bu dersi tamamladıktan sonra:
- D1Result ve D1ExecResult objelerini detaylı şekilde anlayacaksınız
- Meta bilgilerini nasıl kullanacağınızı öğreneceksiniz
- Monitoring ve debugging için meta data’yı nasıl kullanacağınızı kavrayacaksınız
- Performance optimizasyonu için meta bilgilerinden nasıl faydalanacağınızı bileceksiniz
İçerik İçindekiler
Section titled “İçerik İçindekiler”- D1Result Objesi
- D1ExecResult Objesi
- Meta Bilgileri Detaylı
- Monitoring ve Analytics
- Performance Optimization
D1Result Objesi
Section titled “D1Result Objesi”interface D1Result { success: boolean; meta: D1ResultMeta; results: any[] | null;}Success Alanı
Section titled “Success Alanı”İşlemin başarılı olup olmadığını belirtir:
const result = await db.prepare("SELECT * FROM users").run();
if (result.success) { console.log("Query successful!");} else { console.log("Query failed!");}Results Alanı
Section titled “Results Alanı”Sorgu sonuçlarını içerir. SELECT işlemlerinde doludur, INSERT/UPDATE/DELETE’de boştur:
// SELECT - results doludurconst { results } = await db .prepare("SELECT * FROM users") .run();console.log(results); // [{...}, {...}, ...]
// INSERT - results boşturconst { results } = await db .prepare("INSERT INTO users (username) VALUES (?)") .bind("newuser") .run();console.log(results); // nullD1ExecResult Objesi
Section titled “D1ExecResult Objesi”interface D1ExecResult { count: number; duration: number;}Kullanım
Section titled “Kullanım”exec() metodu D1ExecResult döner:
const result = await db.exec(` CREATE TABLE users (id INTEGER PRIMARY KEY, username TEXT); INSERT INTO users VALUES (1, 'ahmet'); SELECT * FROM users;`);
console.log(result);// {// count: 3, // 3 işlem çalıştırıldı// duration: 15 // Toplam 15ms// }Meta Bilgileri Detaylı
Section titled “Meta Bilgileri Detaylı”Sunucu Bilgileri
Section titled “Sunucu Bilgileri”const { meta } = await db.prepare("SELECT * FROM users LIMIT 1").run();
console.log(meta.served_by); // "D1" veya "miniflare.db"console.log(meta.served_by_region); // "WEUR", "ENAM" vb.console.log(meta.served_by_primary); // true/falsePerformans Metrikleri
Section titled “Performans Metrikleri”const { meta } = await db.prepare("SELECT * FROM users").run();
console.log(meta.duration); // Toplam süre (ms)console.log(meta.sql_duration_ms); // Sadece SQL süresi (ms)
// Performans uyarısıif (meta.duration > 1000) { console.warn(`Slow query detected: ${meta.duration}ms`);}Değişiklik Bilgileri
Section titled “Değişiklik Bilgileri”const { meta } = await db .prepare("UPDATE users SET first_name = ? WHERE id = ?") .bind("Updated", 1) .run();
console.log(meta.changes); // Etkilenen row sayısıconsole.log(meta.last_row_id); // Son eklenen row IDconsole.log(meta.changed_db); // DB değişti mi?
if (meta.changes === 0) { console.log("No rows were updated");}Storage Bilgileri
Section titled “Storage Bilgileri”const { meta } = await db .prepare("INSERT INTO posts (title) VALUES (?)") .bind("New Post") .run();
console.log(meta.size_after); // DB boyutu (byte)
// Storage kullanımı takibiconst sizeInMB = meta.size_after / (1024 * 1024);console.log(`Database size: ${sizeInMB.toFixed(2)} MB`);Row Metrikleri
Section titled “Row Metrikleri”const { meta } = await db .prepare("SELECT * FROM posts WHERE user_id = ?") .bind(1) .run();
console.log(meta.rows_read); // Okunan row sayısıconsole.log(meta.rows_written); // Yazılan row sayısı
// Maliyet tahminiconst estimatedCost = (meta.rows_read * 0.000001) + (meta.rows_written * 0.00001);
console.log(`Estimated cost: $${estimatedCost.toFixed(6)}`);Retry Bilgisi
Section titled “Retry Bilgisi”const { meta } = await db .prepare("SELECT * FROM users") .run();
console.log(meta.total_attempts); // Toplam deneme
// Retry analiziif (meta.total_attempts > 1) { console.log(`Query was retried ${meta.total_attempts - 1} times`);}Monitoring ve Analytics
Section titled “Monitoring ve Analytics”Query Performance Tracking
Section titled “Query Performance Tracking”interface QueryMetrics { query: string; duration: number; rowsRead: number; rowsWritten: number; timestamp: number;}
class QueryMonitor { private metrics: QueryMetrics[] = [];
async executeQuery( db: D1Database, query: string, params: any[] = [] ) { const startTime = Date.now();
let stmt = db.prepare(query); for (const param of params) { stmt = stmt.bind(param); }
const { results, meta } = await stmt.run();
const duration = Date.now() - startTime;
// Metric kaydı this.metrics.push({ query, duration, rowsRead: meta.rows_read, rowsWritten: meta.rows_written, timestamp: Date.now(), });
return results; }
getSlowQueries(threshold = 1000) { return this.metrics.filter(m => m.duration > threshold); }
getExpensiveQueries(readThreshold = 10000, writeThreshold = 1000) { return this.metrics.filter(m => m.rowsRead > readThreshold || m.rowsWritten > writeThreshold ); }}
// Kullanımconst monitor = new QueryMonitor();const users = await monitor.executeQuery( db, "SELECT * FROM users WHERE id = ?", [1]);
const slowQueries = monitor.getSlowQueries();console.log("Slow queries:", slowQueries);Real-time Monitoring
Section titled “Real-time Monitoring”export default { async fetch(request: Request, env: Env, ctx: ExecutionContext) { const startTime = Date.now();
// Query çalıştır const { results, meta } = await env.DB .prepare("SELECT * FROM posts") .run();
const duration = Date.now() - startTime;
// Response header'larına timing ekle return Response.json(results, { headers: { 'X-Query-Duration': duration.toString(), 'X-Rows-Read': meta.rows_read.toString(), 'X-Rows-Written': meta.rows_written.toString(), 'X-Served-By': meta.served_by_region, }, }); },};Analytics Dashboard
Section titled “Analytics Dashboard”interface DashboardMetrics { totalQueries: number; avgQueryDuration: number; totalRowsRead: number; totalRowsWritten: number; slowQueries: number; expensiveQueries: number;}
class AnalyticsDashboard { private queryHistory: Array<{ duration: number; rowsRead: number; rowsWritten: number; timestamp: number; }> = [];
recordQuery(meta: D1ResultMeta) { this.queryHistory.push({ duration: meta.duration, rowsRead: meta.rows_read, rowsWritten: meta.rows_written, timestamp: Date.now(), });
// Son 1000 query'i tut if (this.queryHistory.length > 1000) { this.queryHistory.shift(); } }
getMetrics(): DashboardMetrics { const totalQueries = this.queryHistory.length; const avgQueryDuration = this.queryHistory.reduce((sum, q) => sum + q.duration, 0) / totalQueries; const totalRowsRead = this.queryHistory.reduce((sum, q) => sum + q.rowsRead, 0); const totalRowsWritten = this.queryHistory.reduce((sum, q) => sum + q.rowsWritten, 0); const slowQueries = this.queryHistory.filter(q => q.duration > 1000).length; const expensiveQueries = this.queryHistory.filter(q => q.rowsRead > 10000 || q.rowsWritten > 1000).length;
return { totalQueries, avgQueryDuration, totalRowsRead, totalRowsWritten, slowQueries, expensiveQueries, }; }}Performance Optimization
Section titled “Performance Optimization”Row Read Optimization
Section titled “Row Read Optimization”// ❌ Yavaş: Çok row okuyorconst { meta } = await db .prepare("SELECT * FROM posts") // Tüm post'lar .run();console.log("Rows read:", meta.rows_read); // 100,000+
// ✅ Hızlı: Sadece gerekli row'ları okuyorconst { meta } = await db .prepare("SELECT * FROM posts WHERE user_id = ? LIMIT 100") .bind(userId) .run();console.log("Rows read:", meta.rows_read); // 100Query Duration Optimization
Section titled “Query Duration Optimization”// Slow query detectionasync function executeWithMonitoring( db: D1Database, query: string, params: any[] = []) { const { results, meta } = await db .prepare(query) .bind(...params) .run();
// Slow query loglama if (meta.duration > 500) { console.warn(`Slow query detected:`); console.warn(`Query: ${query}`); console.warn(`Duration: ${meta.duration}ms`); console.warn(`Rows read: ${meta.rows_read}`); }
return results;}Cost Optimization
Section titled “Cost Optimization”interface CostEstimate { rowsRead: number; rowsWritten: number; estimatedCost: number;}
function estimateCost(meta: D1ResultMeta): CostEstimate { // D1 pricing (örnek) const readCostPerRow = 0.000001; // $1 per million rows const writeCostPerRow = 0.00001; // $10 per million rows
const readCost = meta.rows_read * readCostPerRow; const writeCost = meta.rows_written * writeCostPerRow; const totalCost = readCost + writeCost;
return { rowsRead: meta.rows_read, rowsWritten: meta.rows_written, estimatedCost: totalCost, };}
// Kullanımconst { meta } = await db .prepare("SELECT * FROM posts") .run();
const cost = estimateCost(meta);console.log(`Estimated cost: $${cost.estimatedCost.toFixed(6)}`);Pratik Örnekler
Section titled “Pratik Örnekler”Örnek 1: Query Logger
Section titled “Örnek 1: Query Logger”class QueryLogger { private logs: Array<{ query: string; params: any[]; duration: number; rowsRead: number; rowsWritten: number; timestamp: number; }> = [];
async log( db: D1Database, query: string, params: any[] = [] ) { const startTime = Date.now();
let stmt = db.prepare(query); for (const param of params) { stmt = stmt.bind(param); }
const { results, meta } = await stmt.run(); const duration = Date.now() - startTime;
// Log kaydı this.logs.push({ query, params, duration, rowsRead: meta.rows_read, rowsWritten: meta.rows_written, timestamp: Date.now(), });
// Uyarı logları if (duration > 1000) { console.warn(`[SLOW QUERY] ${query} took ${duration}ms`); }
if (meta.rows_read > 10000) { console.warn(`[HIGH ROW READ] ${query} read ${meta.rows_read} rows`); }
return results; }
getLogs() { return this.logs; }
exportLogs() { return JSON.stringify(this.logs, null, 2); }}Örnek 2: Performance Middleware
Section titled “Örnek 2: Performance Middleware”interface PerformanceConfig { slowQueryThreshold?: number; highReadThreshold?: number; logSlowQueries?: boolean;}
export function withPerformanceMonitoring( db: D1Database, config: PerformanceConfig = {}) { const { slowQueryThreshold = 1000, highReadThreshold = 10000, logSlowQueries = true, } = config;
return { async prepare(query: string) { const startTime = Date.now();
// Original prepare çağrısı const originalPrepare = db.prepare.bind(db); const stmt = originalPrepare(query);
// Run metodunu wrap et const originalRun = stmt.run.bind(stmt); stmt.run = async function() { const { results, meta } = await originalRun(); const duration = Date.now() - startTime;
// Loglama if (logSlowQueries && duration > slowQueryThreshold) { console.warn({ type: "SLOW_QUERY", query, duration, rowsRead: meta.rows_read, }); }
if (meta.rows_read > highReadThreshold) { console.warn({ type: "HIGH_ROW_READ", query, rowsRead: meta.rows_read, }); }
return { results, meta }; };
return stmt; }, };}Bu derste aşağıdaki konuları öğrendiniz:
✅ D1Result ve D1ExecResult objeleri ✅ Meta bilgileri ve kullanım alanları ✅ Monitoring ve analytics implementasyonu ✅ Performance optimizasyonu ✅ Cost estimation ✅ Practical monitoring solutions
Sonraki Ders
Section titled “Sonraki Ders”Bir sonraki dersimizde “SQL Statements” başlığı altında:
- SQLite PRAGMA komutları
- sqlite_master sorguları
- Pattern matching
- İleri seviye SQL özellikleri konularını inceleyeceğiz.
Kaynaklar
Section titled “Kaynaklar”Alıştırma Soruları
Section titled “Alıştırma Soruları”- D1Result ve D1ExecResult arasındaki fark nedir?
- Meta bilgileri monitoring için nasıl kullanılır?
- Slow query nasıl tespit edilir?
- Cost estimation nasıl yapılır?
- Performance monitoring middleware nasıl implement edilir?
Ders Süresi: 45 dakika Zorluk Seviyesi: Orta Ön Koşullar: Ders 7: Prepared Statements