Class: Parse::Agent::MCPRackApp::ListeningStreamBody Private
- Inherits:
-
Object
- Object
- Parse::Agent::MCPRackApp::ListeningStreamBody
- Defined in:
- lib/parse/agent/mcp_rack_app.rb
Overview
This class is part of a private API. You should avoid using this class if possible, as it may be removed or be changed in the future.
Rack body for the long-lived GET listening stream that carries
notifications/resources/updated to a subscribing client.
On #each it registers a delivery callback with the Parse::Agent::MCPSubscriptions::Manager keyed by the session id, then blocks reading from an internal queue and yields SSE-formatted notification events as they are published by the LiveQuery bridge. A periodic SSE comment heartbeat keeps the connection warm and surfaces a dead socket as a write error so the Rack server invokes #close.
#close detaches the listener and tears down every LiveQuery
subscription bound to the session — so a dropped stream leaves no
LiveQuery sockets behind. Re-opening the stream requires the client to
re-issue its resources/subscribe calls (subscriptions do not survive a
listening-stream disconnect in this single-process implementation).
The publish callback runs on a LiveQuery dispatcher / debounce thread
and only pushes to the thread-safe queue; all yields happen on the
Rack I/O thread driving #each, mirroring SSEBody's threading model.
Constant Summary collapse
- DONE =
This constant is part of a private API. You should avoid using this constant if possible, as it may be removed or be changed in the future.
:__listening_done__
Instance Method Summary collapse
-
#close ⇒ Object
private
Terminate the stream: stop heartbeats, detach the listener, and tear down the session's LiveQuery subscriptions.
-
#each {|String| ... } ⇒ Object
private
Rack body interface — called once by the Rack server.
-
#initialize(manager, session_id, heartbeat_interval, logger) ⇒ ListeningStreamBody
constructor
private
A new instance of ListeningStreamBody.
Constructor Details
#initialize(manager, session_id, heartbeat_interval, logger) ⇒ ListeningStreamBody
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.
Returns a new instance of ListeningStreamBody.
1685 1686 1687 1688 1689 1690 1691 1692 1693 1694 1695 |
# File 'lib/parse/agent/mcp_rack_app.rb', line 1685 def initialize(manager, session_id, heartbeat_interval, logger) @manager = manager @session_id = session_id @heartbeat_interval = heartbeat_interval @logger = logger @queue = Queue.new @heartbeat = nil @closed = false @counted = false @close_mutex = Mutex.new end |
Instance Method Details
#close ⇒ Object
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.
Terminate the stream: stop heartbeats, detach the listener, and tear down the session's LiveQuery subscriptions. Idempotent.
1725 1726 1727 1728 1729 1730 1731 1732 1733 1734 1735 1736 1737 1738 1739 1740 1741 1742 |
# File 'lib/parse/agent/mcp_rack_app.rb', line 1725 def close @close_mutex.synchronize do return if @closed @closed = true end # Balance the #each increment exactly once (close is idempotent via # @closed, and only #each sets @counted). MCPRackApp.adjust_listening_stream_count(-1) if @counted @heartbeat&.kill @heartbeat = nil begin @manager.detach_listener(@session_id) rescue StandardError => e line = "[Parse::Agent::MCPRackApp::ListeningStreamBody] detach error: #{e.class}: #{e.}" @logger ? @logger.warn(line) : warn(line) end @queue << DONE rescue nil end |
#each {|String| ... } ⇒ Object
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.
Rack body interface — called once by the Rack server.
1699 1700 1701 1702 1703 1704 1705 1706 1707 1708 1709 1710 1711 1712 1713 1714 1715 1716 1717 1718 1719 1720 1721 |
# File 'lib/parse/agent/mcp_rack_app.rb', line 1699 def each queue = @queue # Count this stream against the concurrent-listening-stream soft cap. # Incrementing here (in #each, not the constructor) means a body the # Rack server never iterates — or a client that disconnects before # iteration — never inflates the counter; the matching decrement is in # #close, which #each's `ensure` always runs. MCPRackApp.adjust_listening_stream_count(1) @counted = true @manager.attach_listener(@session_id) do |notification| queue << format_event(notification) end # Initial comment flushes response headers and confirms the stream. yield ": connected\n\n" start_heartbeat loop do msg = @queue.pop break if msg == DONE yield msg end ensure close end |