Class: Pgtk::Stash
- Inherits:
-
Object
- Object
- Pgtk::Stash
- 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 ([email protected])
- Copyright
-
Copyright © 2019-2025 Yegor Bugayenko
- License
-
MIT
Instance Method Summary collapse
-
#exec(query, params = []) ⇒ PG::Result
Execute a SQL query with optional caching.
-
#initialize(pgsql, stash = {}) ⇒ Stash
constructor
Initialize a new Stash with query caching.
-
#start(*args) ⇒ Pgtk::Stash
Start a new connection pool with the given arguments.
-
#transaction {|Pgtk::Stash| ... } ⇒ Object
Execute a database transaction.
-
#version ⇒ String
Get the PostgreSQL server version.
Constructor Details
#initialize(pgsql, stash = {}) ⇒ Stash
Initialize a new Stash with query caching.
30 31 32 33 34 35 36 |
# File 'lib/pgtk/stash.rb', line 30 def initialize(pgsql, stash = {}) @pgsql = pgsql @stash = stash @stash[:queries] ||= {} @stash[:tables] ||= {} @entrance = Concurrent::ReentrantReadWriteLock.new end |
Instance Method Details
#exec(query, params = []) ⇒ PG::Result
Execute a SQL query with optional caching.
Read queries are cached, while write queries bypass the cache and invalidate related entries.
45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 |
# File 'lib/pgtk/stash.rb', line 45 def exec(query, params = []) pure = (query.is_a?(Array) ? query.join(' ') : query).gsub(/\s+/, ' ').strip if /(^|\s)(INSERT|DELETE|UPDATE|LOCK)\s/.match?(pure) || /(^|\s)pg_[a-z_]+\(/.match?(pure) tables = pure.scan(/(?<=^|\s)(?:UPDATE|INSERT INTO|DELETE FROM|TRUNCATE)\s([a-z]+)(?=[^a-z]|$)/).map(&:first).uniq ret = @pgsql.exec(pure, params) @entrance.with_write_lock do tables.each do |t| @stash[:tables][t]&.each do |q| @stash[:queries].delete(q) end @stash[:tables].delete(t) end end else key = params.map(&:to_s).join(' -*&%^- ') @entrance.with_write_lock { @stash[:queries][pure] ||= {} } ret = @stash[:queries][pure][key] if ret.nil? ret = @pgsql.exec(pure, params) unless /(?<=^|\s)(NOW\(\)|COMMIT|ROLLBACK|START TRANSACTION|TRUNCATE|TO WARNING)(?=;|\s|$)/.match?(pure) @entrance.with_write_lock do @stash[:queries][pure] ||= {} @stash[:queries][pure][key] = ret tables = pure.scan(/(?<=^|\s)(?:FROM|JOIN) ([a-z_]+)(?=\s|$)/).map(&:first).uniq tables.each do |t| @stash[:tables][t] = [] if @stash[:tables][t].nil? @stash[:tables][t].append(pure).uniq! end raise "No tables at #{pure.inspect}" if tables.empty? end end end end ret end |
#start(*args) ⇒ Pgtk::Stash
Start a new connection pool with the given arguments.
97 98 99 |
# File 'lib/pgtk/stash.rb', line 97 def start(*args) Pgtk::Stash.new(@pgsql.start(*args), @stash) end |
#transaction {|Pgtk::Stash| ... } ⇒ Object
Execute a database transaction.
Yields a new Stash that shares the same cache but uses the transaction connection.
87 88 89 90 91 |
# File 'lib/pgtk/stash.rb', line 87 def transaction @pgsql.transaction do |t| yield Pgtk::Stash.new(t, @stash) end end |
#version ⇒ String
Get the PostgreSQL server version.
104 105 106 |
# File 'lib/pgtk/stash.rb', line 104 def version @pgsql.version end |