Class: Spurline::Session::Store::Postgres
- Defined in:
- lib/spurline/session/store/postgres.rb
Overview
PostgreSQL-backed session store. Persists sessions across process restarts. Thread-safe via a single connection guarded by a Mutex.
Constant Summary collapse
- TABLE_NAME =
"spurline_sessions"
Instance Method Summary collapse
- #clear! ⇒ Object
- #close ⇒ Object
- #delete(id) ⇒ Object
- #exists?(id) ⇒ Boolean
- #ids ⇒ Object
-
#initialize(url: Spurline.config.session_store_postgres_url, serializer: Spurline::Session::Serializer.new) ⇒ Postgres
constructor
A new instance of Postgres.
-
#load(id) ⇒ Object
ASYNC-READY: Keep read path isolated for future async driver swap.
- #save(session) ⇒ Object
- #size ⇒ Object
Constructor Details
#initialize(url: Spurline.config.session_store_postgres_url, serializer: Spurline::Session::Serializer.new) ⇒ Postgres
Returns a new instance of Postgres.
13 14 15 16 17 18 19 20 |
# File 'lib/spurline/session/store/postgres.rb', line 13 def initialize(url: Spurline.config.session_store_postgres_url, serializer: Spurline::Session::Serializer.new) @url = url @serializer = serializer @mutex = Mutex.new @connection = nil require_pg! ensure_url! end |
Instance Method Details
#clear! ⇒ Object
78 79 80 81 82 |
# File 'lib/spurline/session/store/postgres.rb', line 78 def clear! @mutex.synchronize do connection.exec("DELETE FROM #{TABLE_NAME}") end end |
#close ⇒ Object
90 91 92 93 94 95 |
# File 'lib/spurline/session/store/postgres.rb', line 90 def close @mutex.synchronize do @connection&.close @connection = nil end end |
#delete(id) ⇒ Object
59 60 61 62 63 |
# File 'lib/spurline/session/store/postgres.rb', line 59 def delete(id) @mutex.synchronize do connection.exec_params("DELETE FROM #{TABLE_NAME} WHERE id = $1", [id]) end end |
#exists?(id) ⇒ Boolean
65 66 67 68 69 70 |
# File 'lib/spurline/session/store/postgres.rb', line 65 def exists?(id) @mutex.synchronize do result = connection.exec_params("SELECT 1 FROM #{TABLE_NAME} WHERE id = $1 LIMIT 1", [id]) result.ntuples.positive? end end |
#ids ⇒ Object
84 85 86 87 88 |
# File 'lib/spurline/session/store/postgres.rb', line 84 def ids @mutex.synchronize do connection.exec("SELECT id FROM #{TABLE_NAME} ORDER BY id").map { |row| row.fetch("id") } end end |
#load(id) ⇒ Object
ASYNC-READY: Keep read path isolated for future async driver swap.
45 46 47 48 49 50 51 52 53 54 55 56 57 |
# File 'lib/spurline/session/store/postgres.rb', line 45 def load(id) row = @mutex.synchronize do result = connection.exec_params( "SELECT data::text AS data FROM #{TABLE_NAME} WHERE id = $1 LIMIT 1", [id] ) result.ntuples.positive? ? result[0] : nil end return nil unless row payload = row.fetch("data") @serializer.from_json(payload, store: self) end |
#save(session) ⇒ Object
22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 |
# File 'lib/spurline/session/store/postgres.rb', line 22 def save(session) now = Time.now.utc.iso8601(6) payload = @serializer.to_json(session) @mutex.synchronize do connection.exec_params( " INSERT INTO \#{TABLE_NAME} (id, state, agent_class, created_at, updated_at, data)\n VALUES ($1, $2, $3, COALESCE((SELECT created_at FROM \#{TABLE_NAME} WHERE id = $1), $4), $5, $6::jsonb)\n ON CONFLICT (id) DO UPDATE SET\n state = EXCLUDED.state,\n agent_class = EXCLUDED.agent_class,\n updated_at = EXCLUDED.updated_at,\n data = EXCLUDED.data\n SQL\n [session.id, session.state.to_s, session.agent_class, now, now, payload]\n )\n end\n\n session\nend\n", |
#size ⇒ Object
72 73 74 75 76 |
# File 'lib/spurline/session/store/postgres.rb', line 72 def size @mutex.synchronize do connection.exec("SELECT COUNT(*) FROM #{TABLE_NAME}")[0]["count"].to_i end end |