Class: Ticketing::Conn
- Inherits:
-
Object
- Object
- Ticketing::Conn
- Defined in:
- lib/ticketing/connection.rb
Overview
One persistent TCP connection to a lock server, managed on a background thread. A dropped connection is retried automatically without disturbing others. Requests are pipelined: a reply is matched back to its waiter by (op, key).
Constant Summary collapse
- RECONNECT_INTERVAL =
3.0
Instance Attribute Summary collapse
-
#addr ⇒ Object
readonly
Returns the value of attribute addr.
Instance Method Summary collapse
-
#initialize(addr, logger) ⇒ Conn
constructor
A new instance of Conn.
-
#request(op, key, frame) ⇒ Object
Enqueues a waiter under (op, key), writes the frame, then blocks until the reader thread resolves it.
-
#start_connector ⇒ Object
Starts the background connect/read/reconnect loop.
-
#stop ⇒ Object
Stops the background thread, closes the socket, and fails all waiters.
-
#usable? ⇒ Boolean
True while at least one reply can currently be sent and received.
Constructor Details
#initialize(addr, logger) ⇒ Conn
Returns a new instance of Conn.
54 55 56 57 58 59 60 61 62 63 64 65 66 67 |
# File 'lib/ticketing/connection.rb', line 54 def initialize(addr, logger) @addr = addr @logger = logger @host, @port = parse_addr(addr) @status = :down @writer = nil @socket = nil @running = true @thread = nil @writer_mutex = Mutex.new @pending_mutex = Mutex.new @acquire_waiters = {} @release_waiters = {} end |
Instance Attribute Details
#addr ⇒ Object (readonly)
Returns the value of attribute addr.
52 53 54 |
# File 'lib/ticketing/connection.rb', line 52 def addr @addr end |
Instance Method Details
#request(op, key, frame) ⇒ Object
Enqueues a waiter under (op, key), writes the frame, then blocks until the reader thread resolves it. Raises IoError if the write fails or the connection is not up.
77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 |
# File 'lib/ticketing/connection.rb', line 77 def request(op, key, frame) waiter = Waiter.new @writer_mutex.synchronize do w = @writer or raise Ticketing.closed_error @pending_mutex.synchronize do (waiters(op)[key] ||= []).push(waiter) end begin w.write(frame) w.flush rescue IOError, SystemCallError => e @pending_mutex.synchronize do queue = waiters(op)[key] if queue queue.pop waiters(op).delete(key) if queue.empty? end end raise IoError.new(e) end end waiter.await end |
#start_connector ⇒ Object
Starts the background connect/read/reconnect loop.
104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 |
# File 'lib/ticketing/connection.rb', line 104 def start_connector @thread = Thread.new do ever_connected = false while @running sock = connect_socket next if sock.nil? @socket = sock @writer = sock @status = :up @logger.warn("reconnected: #{@addr}") if ever_connected ever_connected = true begin read_loop(sock) rescue StandardError nil end teardown(sock) @logger.warn("connection lost: #{@addr} — reconnecting every #{RECONNECT_INTERVAL}s") sleep RECONNECT_INTERVAL if @running end end end |
#stop ⇒ Object
Stops the background thread, closes the socket, and fails all waiters.
131 132 133 134 135 136 137 138 139 140 141 142 |
# File 'lib/ticketing/connection.rb', line 131 def stop @running = false @status = :down thread = @thread begin @socket&.close rescue StandardError nil end thread&.kill clear_pending end |
#usable? ⇒ Boolean
True while at least one reply can currently be sent and received.
70 71 72 |
# File 'lib/ticketing/connection.rb', line 70 def usable? @status == :up end |