Class: Pgtk::Stash

Inherits:
Object
  • Object
show all
Defined in:
lib/pgtk/stash.rb

Overview

Database query cache implementation.

Provides a caching layer for PostgreSQL queries, automatically invalidating the cache when tables are modified. Read queries are cached while write queries bypass the cache and invalidate related cached entries.

Thread-safe with read-write locking.

The implementation is very naive! Use it at your own risk.

Author

Yegor Bugayenko (yegor256@gmail.com)

Copyright

Copyright (c) 2019-2026 Yegor Bugayenko

License

MIT

Examples:

Basic usage

pool = Pgtk::Pool.new(...)
stash = Pgtk::Stash.new(pool, cap: 1000, refill: 30)
stash.start!
result = stash.exec('SELECT * FROM users WHERE id = $1', [42])

Instance Method Summary collapse

Constructor Details

#initialize(pool, stash: { queries: {}, tables: {}, table_mod: {}, table_inflight: {} }, loog: Loog::NULL, entrance: Concurrent::ReentrantReadWriteLock.new, refill: 16, delay: 0, maxqueue: 128, threads: 4, cap: 10_000, capping: 60, retire: 15 * 60, retirement: 60) ⇒ Stash

Initialize a new Stash with query caching.

Set any of the intervals to nil to disable the cron.

Parameters:

  • pool (Object)

    The underlying connection pool

  • stash (Hash) (defaults to: { queries: {}, tables: {}, table_mod: {}, table_inflight: {} })

    Internal cache structure

  • refill (Float) (defaults to: 16)

    Interval in seconds between background refill tasks

  • delay (Float) (defaults to: 0)

    A pause in seconds before making a refill

  • maxqueue (Integer) (defaults to: 128)

    Maximum number of refilling tasks in the thread pool queue

  • threads (Integer) (defaults to: 4)

    Number of worker threads for cache refilling

  • cap (Integer) (defaults to: 10_000)

    Maximum number of cached query results to retain

  • capping (Float) (defaults to: 60)

    Interval in seconds between cache cap enforcement tasks

  • retire (Integer) (defaults to: 15 * 60)

    Maximum age in seconds to keep a query in cache

  • retirement (Float) (defaults to: 60)

    Interval in seconds between retirement tasks

  • loog (Loog) (defaults to: Loog::NULL)

    Logger instance

  • entrance (Concurrent::ReentrantReadWriteLock) (defaults to: Concurrent::ReentrantReadWriteLock.new)

    Read-write lock for thread-safe access



69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
# File 'lib/pgtk/stash.rb', line 69

def initialize(
  pool,
  stash: { queries: {}, tables: {}, table_mod: {}, table_inflight: {} },
  loog: Loog::NULL,
  entrance: Concurrent::ReentrantReadWriteLock.new,
  refill: 16,
  delay: 0,
  maxqueue: 128,
  threads: 4,
  cap: 10_000,
  capping: 60,
  retire: 15 * 60,
  retirement: 60
)
  @pool = pool
  @stash = stash
  @stash[:table_mod] ||= {}
  unless @stash[:table_inflight].default_proc
    @stash[:table_inflight] = Hash.new { |h, k| h[k] = Concurrent::AtomicFixnum.new(0) }
  end
  @loog = loog
  @entrance = entrance
  @refill = refill
  @delay = delay
  @maxqueue = maxqueue
  @threads = threads
  @cap = cap
  @capping = capping
  @retire = retire
  @retirement = retirement
end

Instance Method Details

#dumpString

Convert internal state into text.

Returns:

  • (String)

    Multi-line text representation of the current cache state



119
120
121
122
123
# File 'lib/pgtk/stash.rb', line 119

def dump
  @entrance.with_read_lock do
    body(queries)
  end
end

#exec(query, params = [], result = 0) ⇒ PG::Result

Execute a SQL query with optional caching.

Parameters:

  • query (String, Array<String>)

    The SQL query

  • params (Array) (defaults to: [])

    Query parameters

  • result (Integer) (defaults to: 0)

    Result format code

Returns:

  • (PG::Result)

    Query result object



131
132
133
134
135
136
137
138
139
140
# File 'lib/pgtk/stash.rb', line 131

def exec(query, params = [], result = 0)
  pure = (query.is_a?(Array) ? query.join(' ') : query).gsub(/\s+/, ' ').strip
  if MODS_RE.match?(pure)
    modify(pure, params, result)
  elsif /(^|\s)pg_[a-z_]+\(/.match?(pure)
    @pool.exec(pure, params, result)
  else
    select(pure, params, result)
  end
end

#start!void

This method returns an undefined value.

Start the connection pool and launch background cache management tasks.



104
105
106
107
108
# File 'lib/pgtk/stash.rb', line 104

def start!
  @pool.start!
  cascade!
  launch!
end

#transaction {|Pgtk::Stash| ... } ⇒ Object

Execute a database transaction.

Yields:

  • (Pgtk::Stash)

    A stash connected to the transaction

Returns:

  • (Object)

    The result of the block



146
147
148
149
150
# File 'lib/pgtk/stash.rb', line 146

def transaction
  @pool.transaction do |t|
    yield(Pgtk::Stash.new(t, stash: @stash, loog: @loog, entrance: @entrance))
  end
end

#versionString

Get the PostgreSQL server version.

Returns:

  • (String)

    Version string of the database server



112
113
114
# File 'lib/pgtk/stash.rb', line 112

def version
  @pool.version
end