Class: Sequel::Postgres::StatementCache::StatementCache

Inherits:
Object
  • Object
show all
Includes:
Enumerable
Defined in:
lib/sequel/extensions/pg_statement_cache.rb

Overview

The backbone of the block, a modified LRU (least recently used) cache mapping SQL query strings to Statement objects.

Instance Method Summary collapse

Constructor Details

#initialize(opts = {}, &block) ⇒ StatementCache

Set the options for the statement cache. These are generally set at the database level using the :statement_cache_opts Database option.

:max_size

The maximum size (high water mark) for the cache. If an entry is added when the current size of the cache is equal to the maximum size, the cache is cleaned up to reduce the number of entries to the :min_size. Defaults to 1000.

:min_size

The minimum size (low water mark) for the cache. On cleanup, the size of the cache is reduced to this number. Note that there could be fewer than this number of entries in the cache. Defaults to :max_size/2.

:prepare_after

The number of executions to wait for before preparing

the query server-side.  If set to 1, prepares all executed
queries server-side.  If set to 5, does not attempt to
prepare the query until the 5th execution.  Defaults to 2.
:sorter

A callable object that takes two arguments, the current time and the related Statement instance, and should return some Comparable (usually a numeric) such that the lowest values returned are the first to be removed when it comes time to clean the pool. The default is basically:

lambda{|t, stmt| (stmt.last_seen - t)/stmt.num_executes}

so that it doesn’t remove statements that have been executed many times just because many less-frequently executed statements have been executed recently.

The block passed is called with the Statement object’s name, only for statements that have been prepared, and should be used to deallocate the statements.



108
109
110
111
112
113
114
115
116
117
118
119
120
# File 'lib/sequel/extensions/pg_statement_cache.rb', line 108

def initialize(opts={}, &block)
  @cleanup_proc = block
  @prepare_after = opts.fetch(:prepare_after, 2)
  @max_size = opts.fetch(:max_size, 1000)
  @min_size = opts.fetch(:min_size, @max_size/2)
  @sorter = opts.fetch(:sorter){method(:default_sorter)}
  @ids = (1..@max_size).to_a.reverse
  @hash = {}
  #
  # We add one so that when we clean the cache, the entry
  # about to be added brings us to the min_size.
  @size_diff = @max_size - @min_size + 1
end

Instance Method Details

#clearObject

Completely clear the statement cache, deallocating on the server side all statements that have been prepared.



124
125
126
# File 'lib/sequel/extensions/pg_statement_cache.rb', line 124

def clear
  @hash.keys.each{|k| remove(k)}
end

#each(&block) ⇒ Object

Yield each SQL string and Statement instance in the cache to the block.



130
131
132
# File 'lib/sequel/extensions/pg_statement_cache.rb', line 130

def each(&block)
  @hash.each(&block)
end

#fetch(sql) ⇒ Object

Get the related statement name from the cache. If the entry is already in the cache, just bump it’s last seen time and the number of executions. Otherwise, add it to the cache. If the cache is already full, clean it up before adding it.

If the num of executions has passed the threshhold, yield the statement name to the block, which should be used to prepare the statement on the server side.

This method should return the prepared statment name if the statement has been prepared, and nil if the query has not been prepared and the statement should be executed normally.



148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
# File 'lib/sequel/extensions/pg_statement_cache.rb', line 148

def fetch(sql)
  unless stmt = @hash[sql]
    # Get the next id from the id pool.
    unless id = @ids.pop
      # No id left, cache must be full, so cleanup and then
      # get the next id from the id pool.
      cleanup
      id = @ids.pop
    end
    @hash[sql] = stmt = Statement.new(id)
  end

  stmt.last_seen = Time.now
  stmt.num_executes += 1

  if stmt.num_executes >= @prepare_after
    if stmt.num_executes == @prepare_after
      begin
        yield(stmt.name)
      rescue PGError
        # An error occurred while preparing the statement,
        # execute it normally (which will probably raise
        # the error again elsewhere), but decrement the
        # number of executions so we don't think we've
        # prepared the statement when we haven't.
        stmt.num_executes -= 1
        return nil
      end
    end
    stmt.name
  end
end

#sizeObject

The current size of the statement cache.



182
183
184
# File 'lib/sequel/extensions/pg_statement_cache.rb', line 182

def size
  @hash.length
end