- Implement comprehensive SQL query logging for queries over 10ms - Add logging wrapper methods for all database operations - Replace timing code in GetStats with simple info log messages - Add missing database indexes for better query performance: - idx_live_routes_lookup for common prefix/origin/peer lookups - idx_live_routes_withdraw for withdrawal updates - idx_prefixes_prefix for prefix lookups - idx_asn_peerings_lookup for peering relationship queries - Increase SQLite cache size to 512MB - Add performance-oriented SQLite pragmas - Extract HTML templates to separate files using go:embed - Add JSON response middleware with @meta field (like bgpview.io API) - Fix concurrent map write errors in HTTP handlers - Add request timeout handling with proper JSON error responses These changes significantly improve database query performance and provide visibility into slow queries for debugging purposes.
99 lines
2.7 KiB
Go
99 lines
2.7 KiB
Go
package database
|
|
|
|
import (
|
|
"context"
|
|
"database/sql"
|
|
"log/slog"
|
|
"time"
|
|
)
|
|
|
|
const slowQueryThreshold = 10 * time.Millisecond
|
|
|
|
// logSlowQuery logs queries that take longer than slowQueryThreshold
|
|
func logSlowQuery(logger *slog.Logger, query string, start time.Time) {
|
|
elapsed := time.Since(start)
|
|
if elapsed > slowQueryThreshold {
|
|
logger.Debug("Slow query", "query", query, "duration", elapsed)
|
|
}
|
|
}
|
|
|
|
// queryRow wraps QueryRow with slow query logging
|
|
func (d *Database) queryRow(query string, args ...interface{}) *sql.Row {
|
|
start := time.Now()
|
|
defer logSlowQuery(d.logger, query, start)
|
|
|
|
return d.db.QueryRow(query, args...)
|
|
}
|
|
|
|
// query wraps Query with slow query logging
|
|
func (d *Database) query(query string, args ...interface{}) (*sql.Rows, error) {
|
|
start := time.Now()
|
|
defer logSlowQuery(d.logger, query, start)
|
|
|
|
return d.db.Query(query, args...)
|
|
}
|
|
|
|
// exec wraps Exec with slow query logging
|
|
func (d *Database) exec(query string, args ...interface{}) error {
|
|
start := time.Now()
|
|
defer logSlowQuery(d.logger, query, start)
|
|
|
|
_, err := d.db.Exec(query, args...)
|
|
|
|
return err
|
|
}
|
|
|
|
// loggingTx wraps sql.Tx to log slow queries
|
|
type loggingTx struct {
|
|
*sql.Tx
|
|
logger *slog.Logger
|
|
}
|
|
|
|
// QueryRow wraps sql.Tx.QueryRow to log slow queries
|
|
func (tx *loggingTx) QueryRow(query string, args ...interface{}) *sql.Row {
|
|
start := time.Now()
|
|
defer logSlowQuery(tx.logger, query, start)
|
|
|
|
return tx.Tx.QueryRow(query, args...)
|
|
}
|
|
|
|
// Query wraps sql.Tx.Query to log slow queries
|
|
func (tx *loggingTx) Query(query string, args ...interface{}) (*sql.Rows, error) {
|
|
start := time.Now()
|
|
defer logSlowQuery(tx.logger, query, start)
|
|
|
|
return tx.Tx.Query(query, args...)
|
|
}
|
|
|
|
// QueryContext wraps sql.Tx.QueryContext to log slow queries
|
|
func (tx *loggingTx) QueryContext(ctx context.Context, query string, args ...interface{}) (*sql.Rows, error) {
|
|
start := time.Now()
|
|
defer logSlowQuery(tx.logger, query, start)
|
|
|
|
return tx.Tx.QueryContext(ctx, query, args...)
|
|
}
|
|
|
|
// QueryRowContext wraps sql.Tx.QueryRowContext to log slow queries
|
|
func (tx *loggingTx) QueryRowContext(ctx context.Context, query string, args ...interface{}) *sql.Row {
|
|
start := time.Now()
|
|
defer logSlowQuery(tx.logger, query, start)
|
|
|
|
return tx.Tx.QueryRowContext(ctx, query, args...)
|
|
}
|
|
|
|
// Exec wraps sql.Tx.Exec to log slow queries
|
|
func (tx *loggingTx) Exec(query string, args ...interface{}) (sql.Result, error) {
|
|
start := time.Now()
|
|
defer logSlowQuery(tx.logger, query, start)
|
|
|
|
return tx.Tx.Exec(query, args...)
|
|
}
|
|
|
|
// ExecContext wraps sql.Tx.ExecContext to log slow queries
|
|
func (tx *loggingTx) ExecContext(ctx context.Context, query string, args ...interface{}) (sql.Result, error) {
|
|
start := time.Now()
|
|
defer logSlowQuery(tx.logger, query, start)
|
|
|
|
return tx.Tx.ExecContext(ctx, query, args...)
|
|
}
|