Class: Lich::InternalAPI::ActiveSessions::Registry

Inherits:
Object
  • Object
show all
Defined in:
documented/internal_api/active_sessions/registry.rb

Overview

Manages active sessions for the Lich application.

This class is responsible for tracking and managing the state of active sessions.

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(time_source: -> { Time.now.to_i }, process_checker: self.class.method(:process_alive?)) ⇒ void

Initializes a new session registry.

Parameters:

  • time_source (Proc) (defaults to: -> { Time.now.to_i })

    a callable that returns the current time in seconds.

  • process_checker (Proc) (defaults to: self.class.method(:process_alive?))

    a callable that checks if a process is alive.



19
20
21
22
23
24
# File 'documented/internal_api/active_sessions/registry.rb', line 19

def initialize(time_source: -> { Time.now.to_i }, process_checker: self.class.method(:process_alive?))
  @time_source = time_source
  @process_checker = process_checker
  @sessions = {}
  @mutex = Mutex.new
end

Class Method Details

.process_alive?(pid) ⇒ Boolean

Checks if a process with the given ID is alive.

Parameters:

  • pid (Integer)

    the process ID to check.

Returns:

  • (Boolean)

    true if the process is alive, false otherwise.



124
125
126
127
128
129
130
131
132
133
# File 'documented/internal_api/active_sessions/registry.rb', line 124

def self.process_alive?(pid)
  Process.kill(0, pid.to_i)
  true
rescue Errno::ESRCH
  false
rescue Errno::EPERM
  true
rescue StandardError
  false
end

Instance Method Details

#empty_snapshot(error: nil) ⇒ Hash

Returns an empty snapshot of sessions, optionally including an error message.

Parameters:

  • error (String, nil) (defaults to: nil)

    an optional error message to include in the snapshot.

Returns:

  • (Hash)

    an empty snapshot structure.



109
110
111
112
113
114
115
116
117
118
# File 'documented/internal_api/active_sessions/registry.rb', line 109

def empty_snapshot(error: nil)
  {
    source: 'ActiveSessionsAPI',
    total: 0,
    connected: 0,
    detachable: 0,
    sessions: [],
    error: error
  }.compact
end

#remove(pid) ⇒ Boolean

Removes a session by its process ID.

Parameters:

  • pid (Integer)

    the process ID of the session to remove.

Returns:

  • (Boolean)

    true if the session was removed, false otherwise.



54
55
56
# File 'documented/internal_api/active_sessions/registry.rb', line 54

def remove(pid)
  @mutex.synchronize { !@sessions.delete(pid.to_i).nil? }
end

#session(pid) ⇒ Hash?

Retrieves a session by its process ID.

Parameters:

  • pid (Integer)

    the process ID of the session to retrieve.

Returns:

  • (Hash, nil)

    the session data if found, nil otherwise.



62
63
64
65
66
67
# File 'documented/internal_api/active_sessions/registry.rb', line 62

def session(pid)
  @mutex.synchronize do
    record = @sessions[pid.to_i]
    record ? record.dup : nil
  end
end

#snapshotHash

Takes a snapshot of all active sessions.

Returns:

  • (Hash)

    a hash containing the total number of sessions, connected sessions, and the session details.



72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
# File 'documented/internal_api/active_sessions/registry.rb', line 72

def snapshot
  sweep_dead_sessions!
  now = @time_source.call
  sessions = @mutex.synchronize { @sessions.values.map(&:dup) }
  normalized = sessions.sort_by { |session| session[:pid] }.map do |session|
    session.merge(
      uptime_seconds: [0, now - session[:started_at].to_i].max,
      listener: listener_hash(session)
    )
  end

  {
    source: 'ActiveSessionsAPI',
    total: normalized.length,
    connected: normalized.count { |session| session[:connected] },
    detachable: normalized.count { |session| session[:listener] },
    sessions: normalized
  }
end

#sweep_dead_sessions!void

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.

This method returns an undefined value.

Cleans up sessions that are no longer associated with active processes.



96
97
98
99
100
101
102
103
# File 'documented/internal_api/active_sessions/registry.rb', line 96

def sweep_dead_sessions!
  dead_pids = @mutex.synchronize { @sessions.keys }.reject { |pid| @process_checker.call(pid) }
  return if dead_pids.empty?

  @mutex.synchronize do
    dead_pids.each { |pid| @sessions.delete(pid) }
  end
end

#upsert(payload) ⇒ Hash

Inserts or updates a session with the given payload.

Parameters:

  • payload (Hash)

    the session data to be upserted, including :pid and :started_at.

Returns:

  • (Hash)

    the session data after upserting.



30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
# File 'documented/internal_api/active_sessions/registry.rb', line 30

def upsert(payload)
  data = symbolize_keys(payload)
  pid = Integer(data.fetch(:pid))
  now = @time_source.call

  @mutex.synchronize do
    current = @sessions[pid] || {}
    started_at = data[:started_at] || current[:started_at] || now
    merged = current.merge(mergeable_data(data))
    merged[:pid] = pid
    merged[:started_at] = started_at
    merged[:last_seen_at] = now
    merged[:connected] = !!merged[:connected]
    merged[:hidden] = !!merged[:hidden]
    @sessions[pid] = merged
  end

  session(pid)
end