Module: Sentiero::Store::SessionStore
- Included in:
- Sentiero::Store
- Defined in:
- lib/sentiero/store/session_store.rb
Overview
The session-replay store contract: recording windows of rrweb events and reading them back as sessions.
Window-level methods take a Sentiero::WindowRef; session-level methods take a bare session_id.
Instance Method Summary collapse
- #delete_session(session_id) ⇒ Object
- #delete_window(ref) ⇒ Object
-
#each_session_events(limit: nil, since: nil, until_time: nil, types: nil) ⇒ Object
Yields [session_summary_hash, window_id, events_array] per window, newest sessions first, capped at
limit. -
#event_counts_by_day(limit: nil, since: nil, until_time: nil) ⇒ Object
(UTC day of the ms timestamp) => count; events without a numeric timestamp are skipped.
-
#event_type_counts(limit: nil, since: nil, until_time: nil) ⇒ Object
=> count; non-integer client "type" values tally under nil.
- #get_events(ref, after: nil, limit: nil) ⇒ Object
- #get_session(session_id) ⇒ Object
-
#list_sessions(limit:, offset: 0, since: nil, until_time: nil, sort_by: nil, search: nil, min_duration_ms: nil, min_events: nil) ⇒ Object
min_duration_ms filters on the event-timestamp span (last_event_at - first_event_at, milliseconds); sessions without event timestamps fail an active duration filter.
-
#purge_older_than(seconds) ⇒ Object
Deletes every session whose updated_at is older than
secondsago, returning the count. - #save_events(ref, events) ⇒ Object
-
#save_metadata(session_id, metadata) ⇒ Object
Optional; default is a no-op so custom stores keep working without it.
Instance Method Details
#delete_session(session_id) ⇒ Object
32 33 34 |
# File 'lib/sentiero/store/session_store.rb', line 32 def delete_session(session_id) raise NoMethodError, "#{self.class}#delete_session not implemented" end |
#delete_window(ref) ⇒ Object
36 37 38 |
# File 'lib/sentiero/store/session_store.rb', line 36 def delete_window(ref) raise NoMethodError, "#{self.class}#delete_window not implemented" end |
#each_session_events(limit: nil, since: nil, until_time: nil, types: nil) ⇒ Object
Yields [session_summary_hash, window_id, events_array] per window, newest
sessions first, capped at limit. Built from list_sessions/get_session/
get_events so every backend gets it free; stores may override.
types: (integer rrweb event types) narrows only the yielded events
arrays. Summaries and the yielded window set always describe the whole
session, and carry :window_starts (=> first event
timestamp) so replay offsets stay anchored when a window's first
event is filtered out.
54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 |
# File 'lib/sentiero/store/session_store.rb', line 54 def each_session_events(limit: nil, since: nil, until_time: nil, types: nil) unless block_given? return enum_for(:each_session_events, limit: limit, since: since, until_time: until_time, types: types) end cap = limit || limits.analytics_max_scan_sessions sessions = list_sessions(limit: cap, since: since, until_time: until_time) sessions.each do |summary| session = get_session(summary[:session_id]) next unless session windows = session[:windows] || [] summary = summary.merge( window_starts: windows.to_h { |window| [window[:window_id], window[:first_event_at]] } ) windows.each do |window| window_id = window[:window_id] events = get_events(WindowRef.new(summary[:session_id], window_id)) events = events.select { |event| types.include?(event["type"]) } if types yield summary, window_id, events end end end |
#event_counts_by_day(limit: nil, since: nil, until_time: nil) ⇒ Object
(UTC day of the ms timestamp) => count; events without a numeric timestamp are skipped. SQLite overrides with a payload-free aggregate.
96 97 98 99 100 101 102 103 104 105 106 107 108 |
# File 'lib/sentiero/store/session_store.rb', line 96 def event_counts_by_day(limit: nil, since: nil, until_time: nil) counts = {} each_session_events(limit: limit, since: since, until_time: until_time) do |_summary, _window_id, events| events.each do |event| = event["timestamp"] next unless .is_a?(Numeric) day = Time.at( / 1000.0).utc.to_date.to_s counts[day] = (counts[day] || 0) + 1 end end counts end |
#event_type_counts(limit: nil, since: nil, until_time: nil) ⇒ Object
=> count; non-integer client "type" values tally under nil. SQLite overrides with a payload-free aggregate.
81 82 83 84 85 86 87 88 89 90 91 |
# File 'lib/sentiero/store/session_store.rb', line 81 def event_type_counts(limit: nil, since: nil, until_time: nil) counts = {} each_session_events(limit: limit, since: since, until_time: until_time) do |_summary, _window_id, events| events.each do |event| type = event["type"] key = type.is_a?(Integer) ? type : nil counts[key] = (counts[key] || 0) + 1 end end counts end |
#get_events(ref, after: nil, limit: nil) ⇒ Object
28 29 30 |
# File 'lib/sentiero/store/session_store.rb', line 28 def get_events(ref, after: nil, limit: nil) raise NoMethodError, "#{self.class}#get_events not implemented" end |
#get_session(session_id) ⇒ Object
24 25 26 |
# File 'lib/sentiero/store/session_store.rb', line 24 def get_session(session_id) raise NoMethodError, "#{self.class}#get_session not implemented" end |
#list_sessions(limit:, offset: 0, since: nil, until_time: nil, sort_by: nil, search: nil, min_duration_ms: nil, min_events: nil) ⇒ Object
min_duration_ms filters on the event-timestamp span (last_event_at - first_event_at, milliseconds); sessions without event timestamps fail an active duration filter. Both thresholds are inclusive and apply before limit/offset.
19 20 21 22 |
# File 'lib/sentiero/store/session_store.rb', line 19 def list_sessions(limit:, offset: 0, since: nil, until_time: nil, sort_by: nil, search: nil, min_duration_ms: nil, min_events: nil) raise NoMethodError, "#{self.class}#list_sessions not implemented" end |
#purge_older_than(seconds) ⇒ Object
Deletes every session whose updated_at is older than seconds ago,
returning the count. Built from list_sessions + delete_session so every
backend gets it free; stores may override with a direct query.
list_sessions is newest-first, so stale sessions are the last ones reached: we page through the whole store by advancing an offset (not just re-reading the first batch) and delete only after the full scan, so deletions don't shift the pages we're still walking.
118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 |
# File 'lib/sentiero/store/session_store.rb', line 118 def purge_older_than(seconds) cutoff = Time.now.to_f - seconds batch_size = limits.analytics_max_scan_sessions stale = [] offset = 0 loop do summaries = list_sessions(limit: batch_size, offset: offset) break if summaries.empty? stale.concat( summaries .select { |summary| summary[:updated_at] < cutoff } .map { |summary| summary[:session_id] } ) break if summaries.size < batch_size offset += batch_size end stale.each { |session_id| delete_session(session_id) } stale.size end |
#save_events(ref, events) ⇒ Object
11 12 13 |
# File 'lib/sentiero/store/session_store.rb', line 11 def save_events(ref, events) raise NoMethodError, "#{self.class}#save_events not implemented" end |
#save_metadata(session_id, metadata) ⇒ Object
Optional; default is a no-op so custom stores keep working without it.
41 42 43 |
# File 'lib/sentiero/store/session_store.rb', line 41 def (session_id, ) nil end |