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



62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
# File 'lib/pgtk/stash.rb', line 62

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] ||= {}
  @stash[:table_inflight] ||= {}
  @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



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

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



123
124
125
126
127
128
129
130
# File 'lib/pgtk/stash.rb', line 123

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.



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

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



136
137
138
139
140
# File 'lib/pgtk/stash.rb', line 136

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



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

def version
  @pool.version
end