mirror of
https://github.com/oven-sh/bun
synced 2026-02-09 10:28:47 +00:00
refactor: extract SQL performance tracking into shared logger
Create SQLPerformanceEntryLogger struct to eliminate code duplication between MySQL and PostgreSQL query implementations. This shared logger handles performance timing and reporting for SQL operations across different database adapters. 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
@@ -1002,6 +1002,7 @@ src/sql/shared/ObjectIterator.zig
|
||||
src/sql/shared/QueryBindingIterator.zig
|
||||
src/sql/shared/SQLDataCell.zig
|
||||
src/sql/shared/SQLQueryResultMode.zig
|
||||
src/sql/SQLPerformanceEntryLogger.zig
|
||||
src/StandaloneModuleGraph.zig
|
||||
src/StaticHashMap.zig
|
||||
src/string.zig
|
||||
|
||||
91
src/sql/SQLPerformanceEntryLogger.zig
Normal file
91
src/sql/SQLPerformanceEntryLogger.zig
Normal file
@@ -0,0 +1,91 @@
|
||||
const std = @import("std");
|
||||
const bun = @import("bun");
|
||||
const jsc = bun.jsc;
|
||||
|
||||
extern "C" fn JSC__addSQLQueryPerformanceEntry(globalObject: *jsc.JSGlobalObject, name: [*:0]const u8, description: [*:0]const u8, startTime: f64, endTime: f64) void;
|
||||
|
||||
/// Shared SQL performance entry logger for tracking query performance across different SQL adapters
|
||||
pub const SQLPerformanceEntryLogger = struct {
|
||||
/// Start time for performance tracking (in nanoseconds)
|
||||
start_time_ns: u64 = 0,
|
||||
|
||||
const Self = @This();
|
||||
|
||||
/// Extract the SQL command from the query string (e.g., "SELECT", "INSERT", etc.)
|
||||
fn extractSQLCommand(query: []const u8) []const u8 {
|
||||
if (query.len == 0) return "UNKNOWN";
|
||||
|
||||
var i: usize = 0;
|
||||
// Skip leading whitespace
|
||||
while (i < query.len and std.ascii.isWhitespace(query[i])) {
|
||||
i += 1;
|
||||
}
|
||||
|
||||
const start_pos = i;
|
||||
// Find the end of the first word
|
||||
while (i < query.len and !std.ascii.isWhitespace(query[i])) {
|
||||
i += 1;
|
||||
}
|
||||
|
||||
if (i <= start_pos) return "UNKNOWN";
|
||||
|
||||
const command = query[start_pos..i];
|
||||
|
||||
// Convert common commands to uppercase
|
||||
if (std.ascii.eqlIgnoreCase(command, "select")) return "SELECT";
|
||||
if (std.ascii.eqlIgnoreCase(command, "insert")) return "INSERT";
|
||||
if (std.ascii.eqlIgnoreCase(command, "update")) return "UPDATE";
|
||||
if (std.ascii.eqlIgnoreCase(command, "delete")) return "DELETE";
|
||||
if (std.ascii.eqlIgnoreCase(command, "create")) return "CREATE";
|
||||
if (std.ascii.eqlIgnoreCase(command, "drop")) return "DROP";
|
||||
if (std.ascii.eqlIgnoreCase(command, "alter")) return "ALTER";
|
||||
if (std.ascii.eqlIgnoreCase(command, "show")) return "SHOW";
|
||||
if (std.ascii.eqlIgnoreCase(command, "describe") or std.ascii.eqlIgnoreCase(command, "desc")) return "DESCRIBE";
|
||||
if (std.ascii.eqlIgnoreCase(command, "explain")) return "EXPLAIN";
|
||||
if (std.ascii.eqlIgnoreCase(command, "truncate")) return "TRUNCATE";
|
||||
if (std.ascii.eqlIgnoreCase(command, "grant")) return "GRANT";
|
||||
if (std.ascii.eqlIgnoreCase(command, "revoke")) return "REVOKE";
|
||||
|
||||
return "UNKNOWN";
|
||||
}
|
||||
|
||||
/// Start performance tracking for a query
|
||||
pub fn start(self: *Self) void {
|
||||
self.start_time_ns = @as(u64, @intCast(@max(0, std.time.nanoTimestamp())));
|
||||
}
|
||||
|
||||
/// End performance tracking and report to the performance API
|
||||
pub fn end(self: *Self, performance_entries_enabled: bool, query: bun.String, globalObject: *jsc.JSGlobalObject) void {
|
||||
if (!performance_entries_enabled or self.start_time_ns == 0) return;
|
||||
|
||||
const end_time_ns = @as(u64, @intCast(@max(0, std.time.nanoTimestamp())));
|
||||
const start_time_ms = @as(f64, @floatFromInt(self.start_time_ns)) / 1_000_000.0;
|
||||
const end_time_ms = @as(f64, @floatFromInt(end_time_ns)) / 1_000_000.0;
|
||||
|
||||
// Get the SQL command and query string
|
||||
var query_utf8 = query.toUTF8(bun.default_allocator);
|
||||
defer query_utf8.deinit();
|
||||
|
||||
const command = extractSQLCommand(query_utf8.slice());
|
||||
|
||||
// Create null-terminated strings for the C function
|
||||
const command_cstr = bun.default_allocator.dupeZ(u8, command) catch return;
|
||||
defer bun.default_allocator.free(command_cstr);
|
||||
|
||||
const query_cstr = bun.default_allocator.dupeZ(u8, query_utf8.slice()) catch return;
|
||||
defer bun.default_allocator.free(query_cstr);
|
||||
|
||||
// Call the C++ binding to add the performance entry
|
||||
JSC__addSQLQueryPerformanceEntry(globalObject, command_cstr.ptr, query_cstr.ptr, start_time_ms, end_time_ms);
|
||||
}
|
||||
|
||||
/// Initialize a new logger instance
|
||||
pub fn init() Self {
|
||||
return Self{};
|
||||
}
|
||||
|
||||
/// Reset the logger (clear start time)
|
||||
pub fn reset(self: *Self) void {
|
||||
self.start_time_ns = 0;
|
||||
}
|
||||
};
|
||||
@@ -1,6 +1,9 @@
|
||||
const MySQLQuery = @This();
|
||||
const RefCount = bun.ptr.ThreadSafeRefCount(@This(), "ref_count", deinit, .{});
|
||||
|
||||
const std = @import("std");
|
||||
const SQLPerformanceEntryLogger = @import("../SQLPerformanceEntryLogger.zig").SQLPerformanceEntryLogger;
|
||||
|
||||
extern "C" fn JSC__addSQLQueryPerformanceEntry(globalObject: *jsc.JSGlobalObject, name: [*:0]const u8, description: [*:0]const u8, startTime: f64, endTime: f64) void;
|
||||
|
||||
statement: ?*MySQLStatement = null,
|
||||
@@ -12,8 +15,8 @@ status: Status = Status.pending,
|
||||
|
||||
ref_count: RefCount = RefCount.init(),
|
||||
|
||||
/// Start time for performance tracking (in nanoseconds)
|
||||
start_time_ns: u64 = 0,
|
||||
/// Performance tracking logger
|
||||
performance_logger: SQLPerformanceEntryLogger = SQLPerformanceEntryLogger.init(),
|
||||
|
||||
flags: packed struct(u8) {
|
||||
is_done: bool = false,
|
||||
@@ -51,69 +54,14 @@ pub fn hasPendingActivity(this: *@This()) bool {
|
||||
return this.ref_count.load(.monotonic) > 1;
|
||||
}
|
||||
|
||||
/// Extract the SQL command from the query string (e.g., "SELECT", "INSERT", etc.)
|
||||
fn extractSQLCommand(query: []const u8) []const u8 {
|
||||
if (query.len == 0) return "UNKNOWN";
|
||||
|
||||
var i: usize = 0;
|
||||
// Skip leading whitespace
|
||||
while (i < query.len and std.ascii.isWhitespace(query[i])) {
|
||||
i += 1;
|
||||
}
|
||||
|
||||
const start = i;
|
||||
// Find the end of the first word
|
||||
while (i < query.len and !std.ascii.isWhitespace(query[i])) {
|
||||
i += 1;
|
||||
}
|
||||
|
||||
if (i <= start) return "UNKNOWN";
|
||||
|
||||
const command = query[start..i];
|
||||
|
||||
// Convert common commands to uppercase
|
||||
if (std.ascii.eqlIgnoreCase(command, "select")) return "SELECT";
|
||||
if (std.ascii.eqlIgnoreCase(command, "insert")) return "INSERT";
|
||||
if (std.ascii.eqlIgnoreCase(command, "update")) return "UPDATE";
|
||||
if (std.ascii.eqlIgnoreCase(command, "delete")) return "DELETE";
|
||||
if (std.ascii.eqlIgnoreCase(command, "create")) return "CREATE";
|
||||
if (std.ascii.eqlIgnoreCase(command, "drop")) return "DROP";
|
||||
if (std.ascii.eqlIgnoreCase(command, "alter")) return "ALTER";
|
||||
if (std.ascii.eqlIgnoreCase(command, "show")) return "SHOW";
|
||||
if (std.ascii.eqlIgnoreCase(command, "describe") or std.ascii.eqlIgnoreCase(command, "desc")) return "DESCRIBE";
|
||||
if (std.ascii.eqlIgnoreCase(command, "explain")) return "EXPLAIN";
|
||||
|
||||
return "UNKNOWN";
|
||||
}
|
||||
|
||||
/// Start performance tracking for this query
|
||||
pub fn startPerformanceTracking(this: *@This()) void {
|
||||
this.start_time_ns = @as(u64, @intCast(@max(0, std.time.nanoTimestamp())));
|
||||
this.performance_logger.start();
|
||||
}
|
||||
|
||||
/// End performance tracking and report to the performance API
|
||||
pub fn endPerformanceTracking(this: *@This(), connection: anytype, globalObject: *jsc.JSGlobalObject) void {
|
||||
if (!connection.performance_entries_enabled or this.start_time_ns == 0) return;
|
||||
|
||||
const end_time_ns = @as(u64, @intCast(@max(0, std.time.nanoTimestamp())));
|
||||
const start_time_ms = @as(f64, @floatFromInt(this.start_time_ns)) / 1_000_000.0;
|
||||
const end_time_ms = @as(f64, @floatFromInt(end_time_ns)) / 1_000_000.0;
|
||||
|
||||
// Get the SQL command and query string
|
||||
var query_utf8 = this.query.toUTF8(bun.default_allocator);
|
||||
defer query_utf8.deinit();
|
||||
|
||||
const command = extractSQLCommand(query_utf8.slice());
|
||||
|
||||
// Create null-terminated strings for the C function
|
||||
const command_cstr = bun.default_allocator.dupeZ(u8, command) catch return;
|
||||
defer bun.default_allocator.free(command_cstr);
|
||||
|
||||
const query_cstr = bun.default_allocator.dupeZ(u8, query_utf8.slice()) catch return;
|
||||
defer bun.default_allocator.free(query_cstr);
|
||||
|
||||
// Call the C++ binding to add the performance entry
|
||||
JSC__addSQLQueryPerformanceEntry(globalObject, command_cstr.ptr, query_cstr.ptr, start_time_ms, end_time_ms);
|
||||
this.performance_logger.end(connection.performance_entries_enabled, this.query, globalObject);
|
||||
}
|
||||
|
||||
pub fn deinit(this: *@This()) void {
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
const PostgresSQLQuery = @This();
|
||||
const RefCount = bun.ptr.ThreadSafeRefCount(@This(), "ref_count", deinit, .{});
|
||||
|
||||
const std = @import("std");
|
||||
const SQLPerformanceEntryLogger = @import("../SQLPerformanceEntryLogger.zig").SQLPerformanceEntryLogger;
|
||||
extern "C" fn JSC__addSQLQueryPerformanceEntry(globalObject: *jsc.JSGlobalObject, name: [*:0]const u8, description: [*:0]const u8, startTime: f64, endTime: f64) void;
|
||||
statement: ?*PostgresSQLStatement = null,
|
||||
query: bun.String = bun.String.empty,
|
||||
@@ -12,8 +14,7 @@ status: Status = Status.pending,
|
||||
|
||||
ref_count: RefCount = RefCount.init(),
|
||||
|
||||
/// Start time for performance tracking (in nanoseconds)
|
||||
start_time_ns: u64 = 0,
|
||||
performance_logger: SQLPerformanceEntryLogger = SQLPerformanceEntryLogger.init(),
|
||||
|
||||
flags: packed struct(u8) {
|
||||
is_done: bool = false,
|
||||
@@ -60,69 +61,14 @@ pub fn hasPendingActivity(this: *@This()) bool {
|
||||
return this.ref_count.get() > 1;
|
||||
}
|
||||
|
||||
/// Extract the SQL command from the query string (e.g., "SELECT", "INSERT", etc.)
|
||||
fn extractSQLCommand(query: []const u8) []const u8 {
|
||||
if (query.len == 0) return "UNKNOWN";
|
||||
|
||||
var i: usize = 0;
|
||||
// Skip leading whitespace
|
||||
while (i < query.len and std.ascii.isWhitespace(query[i])) {
|
||||
i += 1;
|
||||
}
|
||||
|
||||
const start = i;
|
||||
// Find the end of the first word
|
||||
while (i < query.len and !std.ascii.isWhitespace(query[i])) {
|
||||
i += 1;
|
||||
}
|
||||
|
||||
if (i <= start) return "UNKNOWN";
|
||||
|
||||
const command = query[start..i];
|
||||
|
||||
// Convert common commands to uppercase
|
||||
if (std.ascii.eqlIgnoreCase(command, "select")) return "SELECT";
|
||||
if (std.ascii.eqlIgnoreCase(command, "insert")) return "INSERT";
|
||||
if (std.ascii.eqlIgnoreCase(command, "update")) return "UPDATE";
|
||||
if (std.ascii.eqlIgnoreCase(command, "delete")) return "DELETE";
|
||||
if (std.ascii.eqlIgnoreCase(command, "create")) return "CREATE";
|
||||
if (std.ascii.eqlIgnoreCase(command, "drop")) return "DROP";
|
||||
if (std.ascii.eqlIgnoreCase(command, "alter")) return "ALTER";
|
||||
if (std.ascii.eqlIgnoreCase(command, "show")) return "SHOW";
|
||||
if (std.ascii.eqlIgnoreCase(command, "describe") or std.ascii.eqlIgnoreCase(command, "desc")) return "DESCRIBE";
|
||||
if (std.ascii.eqlIgnoreCase(command, "explain")) return "EXPLAIN";
|
||||
|
||||
return "UNKNOWN";
|
||||
}
|
||||
|
||||
/// Start performance tracking for this query
|
||||
pub fn startPerformanceTracking(this: *@This()) void {
|
||||
this.start_time_ns = @as(u64, @intCast(@max(0, std.time.nanoTimestamp())));
|
||||
this.performance_logger.start();
|
||||
}
|
||||
|
||||
/// End performance tracking and report to the performance API
|
||||
pub fn endPerformanceTracking(this: *@This(), connection: anytype, globalObject: *jsc.JSGlobalObject) void {
|
||||
if (!connection.performance_entries_enabled or this.start_time_ns == 0) return;
|
||||
|
||||
const end_time_ns = @as(u64, @intCast(@max(0, std.time.nanoTimestamp())));
|
||||
const start_time_ms = @as(f64, @floatFromInt(this.start_time_ns)) / 1_000_000.0;
|
||||
const end_time_ms = @as(f64, @floatFromInt(end_time_ns)) / 1_000_000.0;
|
||||
|
||||
// Get the SQL command and query string
|
||||
var query_utf8 = this.query.toUTF8(bun.default_allocator);
|
||||
defer query_utf8.deinit();
|
||||
|
||||
const command = extractSQLCommand(query_utf8.slice());
|
||||
|
||||
// Create null-terminated strings for the C function
|
||||
const command_cstr = bun.default_allocator.dupeZ(u8, command) catch return;
|
||||
defer bun.default_allocator.free(command_cstr);
|
||||
|
||||
const query_cstr = bun.default_allocator.dupeZ(u8, query_utf8.slice()) catch return;
|
||||
defer bun.default_allocator.free(query_cstr);
|
||||
|
||||
// Call the C++ binding to add the performance entry
|
||||
JSC__addSQLQueryPerformanceEntry(globalObject, command_cstr.ptr, query_cstr.ptr, start_time_ms, end_time_ms);
|
||||
this.performance_logger.end(connection.performance_entries_enabled, this.query, globalObject);
|
||||
}
|
||||
|
||||
pub fn deinit(this: *@This()) void {
|
||||
|
||||
Reference in New Issue
Block a user