class CQL::Cache::RequestQueryCache

Overview

Per-request query tracker: counts the SQL statements a request runs, so repeated ones (an N+1, a duplicated lookup) show up. It stores and serves no rows: #with_cache always runs its block.

Tracking is opt-in. A request is the fiber that called #start_request, until its #end_request: each request fiber has its own request id, statements and counters, so concurrent requests do not see or reset each other's. Queries run by a fiber the request spawns are not tracked (that fiber has no request). Outside a request nothing is tracked, unless tracking is turned on with enabled = true; those statements go to one process-wide tracker.

Entries are keyed by the SQL text plus a hash of the bound parameters, so no parameter values are retained, and bounded by max_size per request; the oldest entry (insertion order) is evicted first.

A request fiber that dies without #end_request leaves its entry behind until the next #start_request that finds at least SWEEP_THRESHOLD entries (or twice as many as the last sweep left, a bound that #end_request lowers again as entries go), which drops every entry whose fiber is dead.

Defined in:

cache/request_query_cache.cr

Constant Summary

SWEEP_THRESHOLD = 64

Constructors

Instance Method Summary

Constructor Detail

def self.instance : RequestQueryCache #

[View source]
def self.new(max_size : Int32 = 1000) #

[View source]

Instance Method Detail

def active_requests : Int32 #

The number of request fibers with state: requests in flight, plus fibers that ended without calling #end_request and have not been swept yet.


[View source]
def clear : Nil #

Drops the current request's statements (outside a request, the process-wide tracker's); keeps the id.


[View source]
def current_request_id : String | Nil #

The current fiber's request id; nil outside a request.


[View source]
def enabled=(enabled : Bool) : Nil #

Explicitly enables tracking outside requests; false disables tracking entirely, including for requests that are still active.


[View source]
def enabled? : Bool #

True on a fiber with a request, or everywhere once explicitly enabled.


[View source]
def end_request : Nil #

Ends the current fiber's request; once its last #start_request is matched, drops its state. Does nothing on a fiber without a request.


[View source]
def get(sql : String, params : Array(DB::Any)) : Result | Nil #

Get cached result for a query. Keys are the SQL plus a hash of the parameters, so a stored result is advisory, not a guaranteed match for these exact values.


[View source]
def mark_query_executed(sql : String, params : Array(DB::Any)) : Nil #

Records a query execution, counting a hit when it was already seen.


[View source]
def query_executed?(sql : String, params : Array(DB::Any)) : Bool #

Check if a query has been executed before in this request


[View source]
def set(sql : String, params : Array(DB::Any), result : Result) : Nil #

Store query result in cache


[View source]
def size : Int32 #

Statements tracked for the current request (outside a request, by the process-wide tracker).


[View source]
def start_request(request_id : String | Nil = nil) : Nil #

Starts a request on the current fiber: clears its statements and tracks until the matching #end_request. A nested #start_request on the same fiber clears them again and needs its own #end_request.


[View source]
def stats : Hash(String, String | Int32 | Int64 | Float64) #

Inside a request, its id, size and counters. Outside one, request_id is "none", #size is the process-wide tracker's, and hits, misses, sets and clears are totals for the process.


[View source]
def with_cache(sql : String, params : Array(DB::Any), & : -> T) : T forall T #

Simple cache tracking without result storage


[View source]