Class: Ticketing::TicketBroker
- Inherits:
-
Object
- Object
- Ticketing::TicketBroker
- Defined in:
- lib/ticketing/broker.rb
Overview
Shared handle to the lock service. Declare once (e.g. a constant) and share it across threads — the client is fully thread-safe, and connection failures self-heal without ever breaking this object. All time values are seconds — the wire protocol is second-granular. 락 서비스 공유 핸들 — 상수로 한 번 선언해 스레드들이 공유한다. 클라이언트는 스레드 안전하며 연결 오류는 자가 치유되고 이 객체를 망가뜨리지 않는다. 시간 값은 전부 초 단위.
BROKER = Ticketing::TicketBroker.connect("127.0.0.1:5225")
ticket = BROKER.acquire("order-42", 5, 30)
# ... critical section — 임계 구역 ...
ticket.release
Constant Summary collapse
- ACQUIRE_RETRY =
0.1- RELEASE_RETRY_WINDOW =
5.0- RELEASE_RETRY =
0.2
Class Method Summary collapse
-
.connect(*addrs) ⇒ Object
Plaintext, empty-token broker.
Instance Method Summary collapse
-
#acquire(key, wait, lease) ⇒ Object
Acquires the lock
key, waiting up towaitseconds (0 = forever). -
#initialize(addrs, token: "", tls: nil, logger: nil) ⇒ TicketBroker
constructor
token/tls/logger are keyword options; tls accepts nil(off) | 'system-roots' | 'insecure-skip-verify' | CA PEM path.
-
#release_forget(granted, key) ⇒ Object
Fire-and-forget release (single attempt) — the lease cleans up if it is lost in a failover.
-
#release_loop(granted, key) ⇒ Object
Prefer the granting connection, else any usable one; retry across failover so a held lock is not stranded.
-
#wait_ready(timeout) ⇒ Object
Waits up to
timeoutseconds for any connection to come up.
Constructor Details
#initialize(addrs, token: "", tls: nil, logger: nil) ⇒ TicketBroker
token/tls/logger are keyword options; tls accepts nil(off) | 'system-roots' | 'insecure-skip-verify' | CA PEM path. 토큰/TLS/로거는 키워드 옵션.
27 28 29 30 31 32 33 34 35 36 |
# File 'lib/ticketing/broker.rb', line 27 def initialize(addrs, token: "", tls: nil, logger: nil) ssl_context, verify_host = Tls.build(tls) list = addrs.map(&:to_s) list *= 2 if list.size == 1 # one address held with two connections. 단일 주소는 연결 2개. @topo = Topology.new(list) @rr = 0 @conns = list.each_with_index.map do |addr, index| Conn.new(addr, index, token, ssl_context, verify_host, @topo, logger) end end |
Class Method Details
.connect(*addrs) ⇒ Object
Plaintext, empty-token broker. Never fails. 평문·빈 토큰 — 실패 불가.
22 |
# File 'lib/ticketing/broker.rb', line 22 def self.connect(*addrs) = new(addrs) |
Instance Method Details
#acquire(key, wait, lease) ⇒ Object
Acquires the lock key, waiting up to wait seconds (0 = forever).
lease bounds how long the server holds it if this client dies.
Failovers (dead leader, election, redirect) are retried invisibly.
락 획득 — wait 초(0=무한) 대기, lease는 사망 대비 서버 보유 상한.
장애조치는 내부에서 투명하게 재시도된다.
43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 |
# File 'lib/ticketing/broker.rb', line 43 def acquire(key, wait, lease) Protocol.validate_key!(key) frame = Protocol.acquire_frame(key, wait, lease) deadline = wait.positive? ? monotime + wait : nil loop do conn = pick raise TicketError.new(:not_connected, "no connected server") if conn.nil? reply = conn.request(Protocol::OP_ACQUIRE, key, frame).pop if reply.is_a?(TicketError) raise reply unless reply.aborted? raise TicketError.new(:timeout, "timed out waiting to acquire the lock") if deadline && monotime >= deadline sleep(ACQUIRE_RETRY) next end kind, token = reply case kind when :acquired then return Ticket.new(self, conn, key, token) when :timed_out then raise TicketError.new(:timeout, "timed out waiting to acquire the lock") else raise TicketError.new(:protocol, "unexpected reply for acquire") end end end |
#release_forget(granted, key) ⇒ Object
Fire-and-forget release (single attempt) — the lease cleans up if it is lost in a failover. 단발 백그라운드 해제 — 유실 시 lease가 정리.
109 110 111 112 113 |
# File 'lib/ticketing/broker.rb', line 109 def release_forget(granted, key) conn = granted.usable? ? granted : pick conn&.request(Protocol::OP_RELEASE, key, Protocol.release_frame(key)) nil end |
#release_loop(granted, key) ⇒ Object
Prefer the granting connection, else any usable one; retry across failover so a held lock is not stranded. 부여 연결 우선 — 장애조치를 넘겨 재시도해 보유 락이 방치되지 않게 한다.
84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 |
# File 'lib/ticketing/broker.rb', line 84 def release_loop(granted, key) frame = Protocol.release_frame(key) deadline = monotime + RELEASE_RETRY_WINDOW loop do conn = granted.usable? ? granted : pick unless conn.nil? reply = conn.request(Protocol::OP_RELEASE, key, frame).pop if reply.is_a?(TicketError) raise reply unless reply.aborted? else case reply.first when :released then return true when :not_found then return false else raise TicketError.new(:protocol, "unexpected reply for release") end end end raise TicketError.new(:not_connected, "no connected server") if monotime >= deadline sleep(RELEASE_RETRY) end end |
#wait_ready(timeout) ⇒ Object
Waits up to timeout seconds for any connection to come up.
연결 1개가 올라올 때까지 대기(기동 편의).
71 72 73 74 75 76 77 78 79 |
# File 'lib/ticketing/broker.rb', line 71 def wait_ready(timeout) deadline = monotime + timeout loop do return true unless pick.nil? return false if monotime >= deadline sleep(0.02) end end |