Class: Msnav::WindowRegistry
- Inherits:
-
Object
- Object
- Msnav::WindowRegistry
- Defined in:
- lib/msnav/windows.rb
Overview
DB-backed registry of live editor windows for bridge-mode open routing —
the Ruby side of coderag/daemon/windows.py, byte-compatible on the shared
windows / window_commands tables so msnav daemons in Ruby containers
and coderag daemons elsewhere route opens to each other's windows.
Each window registers and then short-polls for commands. When /api/open targets a file another live window owns, the command is enqueued in the DB (the message bus between containers) and that window's poll delivers it. A window counts as live only while it keeps polling.
Defined Under Namespace
Classes: WindowReg
Constant Summary collapse
- ALIVE_SECONDS =
a registration is live only if it polled this recently
6.0- PURGE_SECONDS =
rows this stale are garbage-collected on registration
3600.0- PARK_SECONDS =
A parked open-command (no owning window yet — a new window is being opened for it) waits this long for that window's extension to register. Generous: a first-time "Reopen in Container" builds the image first.
600.0- PARKED =
window_id marker for parked commands.
""- COLS =
"window_id, authority, roots, path_mappings, host_roots, seq, last_poll"
Instance Method Summary collapse
- #all ⇒ Object
- #count ⇒ Object
-
#drain(window_id) ⇒ Object
Return and clear the window's pending commands — including ones enqueued by a different daemon sharing this DB.
-
#enqueue_open(host_path, line) ⇒ Object
Queue an open-command on the live window that owns HOST_PATH; returns the matched registration, or nil when no live window claims it.
-
#initialize(store) ⇒ WindowRegistry
constructor
A new instance of WindowRegistry.
-
#park_open(host_path, line) ⇒ Object
Queue an open for a window that does not exist yet: the caller is opening a new editor window for this service, and its extension will claim the command when it registers.
-
#register(window_id, authority, roots, path_mappings) ⇒ Object
Record (or refresh) a window.
-
#touch(window_id) ⇒ Object
Mark a window as still-alive on poll; nil if it isn't registered.
Constructor Details
#initialize(store) ⇒ WindowRegistry
Returns a new instance of WindowRegistry.
34 35 36 |
# File 'lib/msnav/windows.rb', line 34 def initialize(store) @store = store end |
Instance Method Details
#all ⇒ Object
184 185 186 187 188 189 |
# File 'lib/msnav/windows.rb', line 184 def all rows = @store.with_conn do |db| db.execute("SELECT #{COLS} FROM windows ORDER BY seq") end rows.map { |r| row_to_reg(r) } end |
#count ⇒ Object
38 39 40 |
# File 'lib/msnav/windows.rb', line 38 def count @store.with_conn { |db| db.get_first_value("SELECT COUNT(*) FROM windows") } end |
#drain(window_id) ⇒ Object
Return and clear the window's pending commands — including ones enqueued by a different daemon sharing this DB.
111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 |
# File 'lib/msnav/windows.rb', line 111 def drain(window_id) rows = nil @store.with_conn do |db| db.transaction(:immediate) do rows = db.execute( "SELECT id, payload FROM window_commands " \ "WHERE window_id = ? ORDER BY id", [window_id]) unless rows.empty? db.execute( "DELETE FROM window_commands WHERE id <= ? AND window_id = ?", [rows[-1][0], window_id]) end db.execute("UPDATE windows SET last_poll = ? WHERE window_id = ?", [Time.now.to_f, window_id]) end end rows.map { |_id, payload| JSON.parse(payload) } end |
#enqueue_open(host_path, line) ⇒ Object
Queue an open-command on the live window that owns HOST_PATH; returns the matched registration, or nil when no live window claims it.
132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 |
# File 'lib/msnav/windows.rb', line 132 def enqueue_open(host_path, line) now = Time.now.to_f reg = nil @store.with_conn do |db| db.transaction(:immediate) do rows = db.execute( "SELECT #{COLS} FROM windows WHERE last_poll >= ? " \ "ORDER BY seq DESC", [now - ALIVE_SECONDS]) rows.each do |row| candidate = row_to_reg(row) if candidate.host_roots.any? { |hr| under?(hr, host_path) } reg = candidate break end end unless reg.nil? db.execute( "INSERT INTO window_commands (window_id, payload, created_at) " \ "VALUES (?, ?, ?)", [reg.window_id, JSON.generate("type" => "open", "path" => host_path, "line" => line), now]) end end end reg end |
#park_open(host_path, line) ⇒ Object
Queue an open for a window that does not exist yet: the caller is opening a new editor window for this service, and its extension will claim the command when it registers.
163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 |
# File 'lib/msnav/windows.rb', line 163 def park_open(host_path, line) now = Time.now.to_f @store.with_conn do |db| db.transaction(:immediate) do # re-clicking must not stack duplicate opens for the same file db.execute( "SELECT id, payload FROM window_commands WHERE window_id = ?", [PARKED]).each do |cmd_id, payload| next unless JSON.parse(payload)["path"] == host_path db.execute("DELETE FROM window_commands WHERE id = ?", [cmd_id]) end db.execute( "INSERT INTO window_commands (window_id, payload, created_at) " \ "VALUES (?, ?, ?)", [PARKED, JSON.generate("type" => "open", "path" => host_path, "line" => line), now]) end end nil end |
#register(window_id, authority, roots, path_mappings) ⇒ Object
Record (or refresh) a window. A reload re-registers with a fresh window_id but identical roots — stale entries with the same mapped (daemon-space) roots are dropped so only the live window remains.
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 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 |
# File 'lib/msnav/windows.rb', line 45 def register(window_id, , roots, path_mappings) roots = Array(roots) pmaps = Array(path_mappings) host_roots = roots.map { |r| root_to_host(pmaps, r) }.uniq.sort hr_json = JSON.generate(host_roots) now = Time.now.to_f reg = nil @store.with_conn do |db| db.transaction(:immediate) do db.execute( "DELETE FROM window_commands WHERE window_id IN " \ "(SELECT window_id FROM windows WHERE last_poll < ?)", [now - PURGE_SECONDS]) db.execute("DELETE FROM windows WHERE last_poll < ?", [now - PURGE_SECONDS]) # expired parked commands: the window they waited for never came db.execute( "DELETE FROM window_commands WHERE window_id = ? AND created_at < ?", [PARKED, now - PARK_SECONDS]) db.execute( "DELETE FROM windows WHERE host_roots = ? AND window_id != ?", [hr_json, window_id]) seq = db.get_first_value( "SELECT COALESCE(MAX(seq), 0) + 1 FROM windows") db.execute( "INSERT INTO windows (window_id, authority, roots, " \ "path_mappings, host_roots, seq, last_poll) " \ "VALUES (?, ?, ?, ?, ?, ?, ?) " \ "ON CONFLICT(window_id) DO UPDATE SET authority = excluded.authority, " \ "roots = excluded.roots, path_mappings = excluded.path_mappings, " \ "host_roots = excluded.host_roots, seq = excluded.seq, " \ "last_poll = excluded.last_poll", [window_id, || "", JSON.generate(roots), JSON.generate(pmaps), hr_json, seq, now]) # hand parked commands to the window that was opened FOR them: the # first registration whose roots cover a parked path claims it db.execute( "SELECT id, payload FROM window_commands " \ "WHERE window_id = ? ORDER BY id", [PARKED]).each do |cmd_id, payload| target = JSON.parse(payload)["path"] || "" next unless host_roots.any? { |hr| under?(hr, target) } db.execute("UPDATE window_commands SET window_id = ? WHERE id = ?", [window_id, cmd_id]) end reg = WindowReg.new(window_id: window_id, authority: || "", roots: roots, path_mappings: pmaps, host_roots: host_roots, seq: seq, last_poll: now) end end reg end |
#touch(window_id) ⇒ Object
Mark a window as still-alive on poll; nil if it isn't registered.
98 99 100 101 102 103 104 105 106 107 |
# File 'lib/msnav/windows.rb', line 98 def touch(window_id) @store.with_conn do |db| db.execute("UPDATE windows SET last_poll = ? WHERE window_id = ?", [Time.now.to_f, window_id]) return nil if db.changes.zero? row = db.get_first_row( "SELECT #{COLS} FROM windows WHERE window_id = ?", [window_id]) row && row_to_reg(row) end end |