Skip to content

Commit fd82c94

Browse files
committed
Enhance CQL performance monitoring with interface implementation and refactoring
- Introduced interfaces for QueryProfiler and NPlusOneDetector to standardize performance monitoring components. - Refactored the Monitor class to improve dependency injection and initialization logic. - Updated SQL utility methods to be class methods for better accessibility. - Removed deprecated performance components and streamlined the overall structure for improved maintainability. - Enhanced error handling and logging for SQL operations, ensuring clearer output and better debugging capabilities.
1 parent f74730a commit fd82c94

File tree

11 files changed

+67
-74
lines changed

11 files changed

+67
-74
lines changed

src/alter_table.cr

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -67,12 +67,14 @@ module CQL
6767
# drop_column(:age)
6868
# ```
6969
def drop_column(column : Symbol)
70-
col = @table.columns[column]
70+
col = @table.columns[column]?
71+
if col.nil?
72+
Log.error { "Column #{column} does not exist in table #{@table}" }
73+
return
74+
end
75+
7176
@table.columns.delete(column)
7277
@actions << Expression::DropColumn.new(col.name.to_s)
73-
rescue exception
74-
@table.columns[column] = col.not_nil!
75-
Log.error { "Column #{column} does not exist in table #{@table}" }
7678
end
7779

7880
# Renames a column in the table.

src/configure/enviroment_strategy.cr

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -56,7 +56,6 @@ module CQL::Configure
5656
class DevelopmentStrategy < EnvironmentStrategy
5757
def apply(config : Config) : Nil
5858
# === 📊 PERFORMANCE ===
59-
config.monitor_performance = true
6059
config.pool_size = 5
6160
config.pool.size = 5
6261
config.pool.initial_size = 2

src/performance/config.cr

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -93,8 +93,12 @@ module CQL::Performance
9393
property reporting = Reporting.new
9494
property cache = Cache.new
9595

96-
def initialize(&)
97-
yield self if block_given?
96+
def initialize(&block : self -> _)
97+
block.call(self)
98+
end
99+
100+
def initialize
101+
# Default initialization without block
98102
end
99103

100104
# Convenience methods

src/performance/interfaces.cr

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
# Interface modules for performance monitoring components
2+
3+
module CQL::Performance
4+
module QueryProfilerInterface
5+
abstract def record_query(sql : String, params : Array(DB::Any),
6+
execution_time : Time::Span,
7+
rows_affected : Int64? = nil,
8+
error : String? = nil) : Void
9+
abstract def statistics
10+
abstract def slowest_queries(limit : Int32) : Array(QueryData)
11+
abstract def issues : Array(Issue)
12+
abstract def clear : Void
13+
end
14+
15+
module NPlusOneDetectorInterface
16+
abstract def record_query(sql : String) : Void
17+
abstract def start_relation_loading(relation_name : String, parent_model : String) : Void
18+
abstract def end_relation_loading : Void
19+
abstract def patterns
20+
abstract def issues : Array(Issue)
21+
abstract def clear : Void
22+
end
23+
end

src/performance/monitor.cr

Lines changed: 13 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -8,29 +8,9 @@ require "./sql_formatter"
88
require "./query_profiler"
99
require "./n_plus_one_detector"
1010
require "./unified_report_generator"
11+
require "./interfaces"
1112

1213
module CQL::Performance
13-
# Component interfaces for dependency injection
14-
abstract class QueryProfilerInterface
15-
abstract def record_query(sql : String, params : Array(DB::Any),
16-
execution_time : Time::Span,
17-
rows_affected : Int64? = nil,
18-
error : String? = nil) : Void
19-
abstract def statistics
20-
abstract def slowest_queries(limit : Int32) : Array(QueryData)
21-
abstract def issues : Array(Issue)
22-
abstract def clear : Void
23-
end
24-
25-
abstract class NPlusOneDetectorInterface
26-
abstract def record_query(sql : String) : Void
27-
abstract def start_relation_loading(relation_name : String, parent_model : String) : Void
28-
abstract def end_relation_loading : Void
29-
abstract def patterns
30-
abstract def issues : Array(Issue)
31-
abstract def clear : Void
32-
end
33-
3414
# Monitor with dependency injection
3515
class Monitor
3616
include TimingUtils
@@ -45,16 +25,18 @@ module CQL::Performance
4525
@start_time : Time = Time.utc
4626
@context : String? = nil
4727

48-
def initialize(@config : Config = Config.from_env,
49-
@profiler : QueryProfilerInterface? = nil,
50-
@detector : NPlusOneDetectorInterface? = nil,
51-
@sql_formatter : SQLFormatter? = nil,
52-
@report_generator : UnifiedReportGenerator? = nil)
53-
# Use provided components or create defaults
54-
@profiler ||= QueryProfiler.new(@config.profiling) if @config.profiling.enabled?
55-
@detector ||= NPlusOneDetector.new(@config.detection) if @config.detection.enabled?
56-
@sql_formatter ||= create_sql_formatter
57-
@report_generator ||= UnifiedReportGenerator.new
28+
def initialize(
29+
config : Config = Config.from_env,
30+
profiler : QueryProfilerInterface? = nil,
31+
detector : NPlusOneDetectorInterface? = nil,
32+
sql_formatter : SQLFormatter? = nil,
33+
report_generator : UnifiedReportGenerator? = nil,
34+
)
35+
@config = config
36+
@profiler = profiler || (config.profiling.enabled? ? QueryProfiler.new(config.profiling).as(QueryProfilerInterface) : nil)
37+
@detector = detector || (config.detection.enabled? ? NPlusOneDetector.new(config.detection).as(NPlusOneDetectorInterface) : nil)
38+
@sql_formatter = sql_formatter || create_sql_formatter
39+
@report_generator = report_generator || UnifiedReportGenerator.new
5840
end
5941

6042
# Main monitoring methods

src/performance/n_plus_one_detector.cr

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ require "db"
55
require "./utilities"
66
require "./config"
77
require "./unified_report_generator"
8+
require "./interfaces"
89

910
module CQL::Performance
1011
# N+1 query pattern data
@@ -20,6 +21,7 @@ module CQL::Performance
2021

2122
# N+1 detector without event system
2223
class NPlusOneDetector < BasePerformanceComponent
24+
include CQL::Performance::NPlusOneDetectorInterface
2325
@recent_queries : Array(String) = [] of String
2426
@patterns : Array(NPlusOnePattern) = [] of NPlusOnePattern
2527
@config : Config::Detection
@@ -34,7 +36,7 @@ module CQL::Performance
3436
return unless @enabled
3537
return if should_ignore?(sql)
3638

37-
normalized = normalize_sql(sql)
39+
normalized = SQLUtils.normalize_sql(sql)
3840
@recent_queries << normalized
3941

4042
# Keep window size manageable
@@ -87,7 +89,7 @@ module CQL::Performance
8789
end
8890

8991
# Clear all data
90-
def clear
92+
def clear : Void
9193
@recent_queries.clear
9294
@patterns.clear
9395
reset
@@ -147,7 +149,7 @@ module CQL::Performance
147149
end
148150

149151
private def log_detection(query : String, count : Int32)
150-
Log.warn { "N+1 Query Pattern Detected: #{truncate_sql(query)} (#{count} times)" }
152+
Log.warn { "N+1 Query Pattern Detected: #{SQLUtils.truncate_sql(query)} (#{count} times)" }
151153
end
152154
end
153155
end

src/performance/query_profiler.cr

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
require "db"
55
require "./utilities"
66
require "./config"
7+
require "./interfaces"
78

89
module CQL::Performance
910
# Query execution data
@@ -14,6 +15,7 @@ module CQL::Performance
1415
getter timestamp : Time = Time.utc
1516
getter rows_affected : Int64?
1617
getter error : String?
18+
@normalized_sql : String?
1719

1820
def initialize(@sql, @params, @execution_time, @rows_affected = nil, @error = nil)
1921
end
@@ -29,6 +31,7 @@ module CQL::Performance
2931

3032
# Query profiler without event system
3133
class QueryProfiler < BasePerformanceComponent
34+
include CQL::Performance::QueryProfilerInterface
3235
@queries : Array(QueryData) = [] of QueryData
3336
@query_stats : Hash(String, StatsTracker) = {} of String => StatsTracker
3437
@slow_queries : Array(QueryData) = [] of QueryData
@@ -100,7 +103,7 @@ module CQL::Performance
100103
end
101104

102105
# Clear all data
103-
def clear
106+
def clear : Void
104107
@queries.clear
105108
@query_stats.clear
106109
@slow_queries.clear
@@ -159,7 +162,7 @@ module CQL::Performance
159162
private def log_slow_query(query : QueryData)
160163
severity = query.execution_time > @config.very_slow_threshold ? "VERY SLOW" : "SLOW"
161164
formatted_time = format_duration(query.execution_time)
162-
Log.warn { "#{severity} QUERY (#{formatted_time}): #{truncate_sql(query.sql)}" }
165+
Log.warn { "#{severity} QUERY (#{formatted_time}): #{SQLUtils.truncate_sql(query.sql)}" }
163166
end
164167
end
165168
end

src/performance/sql_formatter.cr

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -140,7 +140,7 @@ module CQL::Performance
140140
end
141141

142142
private def colorize_token(token : String) : String
143-
return token if token.empty? || token.whitespace?
143+
return token if token.empty? || token.each_char.all?(&.whitespace?)
144144

145145
upper = token.upcase
146146

src/performance/utilities.cr

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -42,23 +42,23 @@ module CQL::Performance
4242

4343
# SQL normalization and formatting
4444
module SQLUtils
45-
def normalize_sql(sql : String) : String
45+
def self.normalize_sql(sql : String) : String
4646
sql.gsub(/\$\d+|\?/, "?")
4747
.gsub(/\b\d+\b/, "?")
4848
.gsub(/'.+?'/, "'?'")
4949
.gsub(/\s+/, " ")
5050
.strip
5151
end
5252

53-
def truncate_sql(sql : String, max_length : Int32 = 100) : String
53+
def self.truncate_sql(sql : String, max_length : Int32 = 100) : String
5454
if sql.size > max_length
5555
"#{sql[0...max_length]}..."
5656
else
5757
sql
5858
end
5959
end
6060

61-
def format_params(params : Array(DB::Any), max_length : Int32 = 50) : String
61+
def self.format_params(params : Array(DB::Any), max_length : Int32 = 50) : String
6262
return "[]" if params.empty?
6363

6464
formatted = params.map do |param|

src/schema.cr

Lines changed: 5 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -156,13 +156,11 @@ module CQL
156156
# schema.exec("CREATE TABLE users (id INTEGER PRIMARY KEY, name TEXT)")
157157
# ```
158158
def exec(sql : String)
159-
CQL::Performance.benchmark(sql, [] of DB::Any) do
160-
if conn = @active_connection
161-
conn.exec(sql)
162-
else
163-
@db.using_connection do |db_conn|
164-
db_conn.exec(sql)
165-
end
159+
if conn = @active_connection
160+
conn.exec(sql)
161+
else
162+
@db.using_connection do |db_conn|
163+
db_conn.exec(sql)
166164
end
167165
end
168166
end

0 commit comments

Comments
 (0)