Class: Ask::AppServer::SessionStore
- Inherits:
-
Object
- Object
- Ask::AppServer::SessionStore
- Defined in:
- lib/ask/app_server/session_store.rb
Overview
Session store backed by Ask::State::Adapter.
Stores session metadata and subscriptions in the state backend. AgentAdapter instances (which hold in-memory agent sessions) are kept separately and not persisted.
Default backend is Ask::State::Memory (in-process, ephemeral). Pass Ask::State::Providers::SQLite for persistent storage:
store = SessionStore.new(
state: Ask::State::Providers::SQLite.new(path: "~/.ask-app-server/state.db")
)
Constant Summary collapse
- SESSION_PREFIX =
"session:"- SUBSCRIPTION_PREFIX =
"sub:"- SESSION_LIST_KEY =
"sessions"- EVENT_PREFIX =
"events:"
Instance Method Summary collapse
-
#_remove(session_id) ⇒ Object
Internal remove (no locking — caller must hold @mutex).
-
#add(session_id, adapter) ⇒ Object
Register a new session.
-
#append_event(session_id, event) ⇒ Object
Append an event to a session's event log.
-
#clear ⇒ Object
Clear all sessions.
-
#count ⇒ Object
Count of active sessions.
-
#each(&block) ⇒ Object
Iterate over all session adapters.
-
#events_after(session_id, after_seq, limit: nil) ⇒ Object
Get events for a session after a given sequence number.
-
#get(session_id) ⇒ Object
Get a session adapter by ID.
-
#initialize(state: nil) ⇒ SessionStore
constructor
A new instance of SessionStore.
-
#list(limit: 20) ⇒ Object
List all sessions (summaries).
-
#remove(session_id) ⇒ Object
Remove a session.
-
#subscribe(session_id, delivery_kind:) ⇒ Object
Subscribe to a session's events.
-
#subscribed?(session_id) ⇒ Boolean
Is the session subscribed?.
-
#unsubscribe(session_id) ⇒ Object
Unsubscribe from a session.
Constructor Details
#initialize(state: nil) ⇒ SessionStore
Returns a new instance of SessionStore.
25 26 27 28 29 |
# File 'lib/ask/app_server/session_store.rb', line 25 def initialize(state: nil) @state = state || Ask::State::Memory.new @adapters = {} # session_id => AgentAdapter (in-memory only) @mutex = Mutex.new end |
Instance Method Details
#_remove(session_id) ⇒ Object
Internal remove (no locking — caller must hold @mutex).
58 59 60 61 62 63 |
# File 'lib/ask/app_server/session_store.rb', line 58 def _remove(session_id) @state.delete("#{SESSION_PREFIX}#{session_id}") @state.delete("#{SUBSCRIPTION_PREFIX}#{session_id}") @state.delete("#{EVENT_PREFIX}#{session_id}") @adapters.delete(session_id) end |
#add(session_id, adapter) ⇒ Object
Register a new session.
32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 |
# File 'lib/ask/app_server/session_store.rb', line 32 def add(session_id, adapter) model = adapter.respond_to?(:instance_variable_get) ? adapter.instance_variable_get(:@model) : nil = { sessionId: session_id, model: model, createdAt: adapter.respond_to?(:created_at) ? adapter.created_at.iso8601 : Time.now.iso8601 } @mutex.synchronize do existing = @state.get("#{SESSION_PREFIX}#{session_id}") raise Ask::AppServer::SessionAlreadyExists, "Session #{session_id} already exists" if existing @state.set("#{SESSION_PREFIX}#{session_id}", ) @state.list_append(SESSION_LIST_KEY, session_id, max_length: 200) @adapters[session_id] = adapter end end |
#append_event(session_id, event) ⇒ Object
Append an event to a session's event log.
126 127 128 |
# File 'lib/ask/app_server/session_store.rb', line 126 def append_event(session_id, event) @state.list_append("#{EVENT_PREFIX}#{session_id}", event, max_length: 1000) end |
#clear ⇒ Object
Clear all sessions.
112 113 114 115 116 |
# File 'lib/ask/app_server/session_store.rb', line 112 def clear @mutex.synchronize do @adapters.each_key { |sid| _remove(sid) } end end |
#count ⇒ Object
Count of active sessions.
107 108 109 |
# File 'lib/ask/app_server/session_store.rb', line 107 def count @adapters.size end |
#each(&block) ⇒ Object
Iterate over all session adapters.
119 120 121 122 123 |
# File 'lib/ask/app_server/session_store.rb', line 119 def each(&block) # Iterate over a snapshot to avoid holding the mutex during yield snapshot = @mutex.synchronize { @adapters.values.dup } snapshot.each(&block) end |
#events_after(session_id, after_seq, limit: nil) ⇒ Object
Get events for a session after a given sequence number.
131 132 133 134 135 |
# File 'lib/ask/app_server/session_store.rb', line 131 def events_after(session_id, after_seq, limit: nil) events = @state.list_range("#{EVENT_PREFIX}#{session_id}") || [] filtered = events.select { |e| e[:seq].to_i > after_seq.to_i } limit ? filtered.first(limit) : filtered end |
#get(session_id) ⇒ Object
Get a session adapter by ID.
66 67 68 |
# File 'lib/ask/app_server/session_store.rb', line 66 def get(session_id) @mutex.synchronize { @adapters[session_id] } end |
#list(limit: 20) ⇒ Object
List all sessions (summaries).
71 72 73 74 75 76 77 78 79 80 81 82 83 |
# File 'lib/ask/app_server/session_store.rb', line 71 def list(limit: 20) session_ids = @state.list_range(SESSION_LIST_KEY, 0, limit - 1) session_ids.map do |sid| = @state.get("#{SESSION_PREFIX}#{sid}") if adapter = @adapters[sid] .merge( running: adapter&.running || false, idle: adapter&.idle? || true ) end end.compact end |
#remove(session_id) ⇒ Object
Remove a session.
51 52 53 54 55 |
# File 'lib/ask/app_server/session_store.rb', line 51 def remove(session_id) @mutex.synchronize do _remove(session_id) end end |
#subscribe(session_id, delivery_kind:) ⇒ Object
Subscribe to a session's events.
86 87 88 89 90 91 |
# File 'lib/ask/app_server/session_store.rb', line 86 def subscribe(session_id, delivery_kind:) @mutex.synchronize do raise Ask::AppServer::SessionNotFound, "Session #{session_id} not found" unless @adapters.key?(session_id) @state.set("#{SUBSCRIPTION_PREFIX}#{session_id}", { subscribed: true, deliveryKind: delivery_kind }) end end |
#subscribed?(session_id) ⇒ Boolean
Is the session subscribed?
101 102 103 104 |
# File 'lib/ask/app_server/session_store.rb', line 101 def subscribed?(session_id) sub = @state.get("#{SUBSCRIPTION_PREFIX}#{session_id}") sub && sub[:subscribed] end |
#unsubscribe(session_id) ⇒ Object
Unsubscribe from a session.
94 95 96 97 98 |
# File 'lib/ask/app_server/session_store.rb', line 94 def unsubscribe(session_id) @mutex.synchronize do @state.delete("#{SUBSCRIPTION_PREFIX}#{session_id}") end end |