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 © 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: {} }, 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: {} })

    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



58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
# File 'lib/pgtk/stash.rb', line 58

def initialize(
  pool,
  stash: { queries: {}, tables: {} },
  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
  @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



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

def dump
  @entrance.with_read_lock do
    qq = queries
    body(qq)
  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



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

def exec(query, params = [], result = 0)
  pure = (query.is_a?(Array) ? query.join(' ') : query).gsub(/\s+/, ' ').strip
  if MODS_RE.match?(pure) || /(^|\s)pg_[a-z_]+\(/.match?(pure)
    modify(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.



89
90
91
92
# File 'lib/pgtk/stash.rb', line 89

def start!
  @pool.start!
  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



129
130
131
132
133
# File 'lib/pgtk/stash.rb', line 129

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



96
97
98
# File 'lib/pgtk/stash.rb', line 96

def version
  @pool.version
end