Class: Igniter::Store::TCPAdapter

Inherits:
Object
  • Object
show all
Includes:
WireProtocol
Defined in:
lib/igniter/store/tcp_adapter.rb

Overview

TCP transport adapter for the Igniter Store Open Protocol.

Exposes Protocol::Interpreter over a framed TCP (or Unix socket) connection using the same WireProtocol CRC32 framing as the legacy StoreServer path. Each request frame carries a WireEnvelope JSON object; each response frame carries the WireEnvelope response JSON object.

This is the new envelope dispatch path (default port 7401). The legacy StoreServer path (port 7400) is separate and unchanged.

Usage:

adapter = TCPAdapter.new(interpreter: interpreter, port: 7401)
adapter.start_async
adapter.wait_until_ready
adapter.stop

Constant Summary

Constants included from WireProtocol

WireProtocol::FRAME_CRC_SIZE, WireProtocol::FRAME_HEADER_SIZE

Instance Method Summary collapse

Methods included from WireProtocol

#encode_frame, #read_frame

Constructor Details

#initialize(interpreter:, port: 7401, host: "127.0.0.1", transport: :tcp) ⇒ TCPAdapter

Returns a new instance of TCPAdapter.



27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
# File 'lib/igniter/store/tcp_adapter.rb', line 27

def initialize(interpreter:, port: 7401, host: "127.0.0.1", transport: :tcp)
  @interpreter    = interpreter
  @port           = port
  @host           = host
  @transport      = transport
  @stopped        = false
  @threads        = []
  @threads_mutex  = Mutex.new
  @ready_mutex    = Mutex.new
  @ready_cond     = ConditionVariable.new
  @ready          = false
  @server         = build_server
  # Socket is bound during initialize — signal ready immediately so that
  # wait_until_ready is race-free even before start is called.
  signal_ready
end

Instance Method Details

#bind_addressObject



88
89
90
# File 'lib/igniter/store/tcp_adapter.rb', line 88

def bind_address
  @transport == :unix ? @host : "#{@host}:#{@port}"
end

#startObject

Runs the accept loop in the current thread (blocks until #stop).



45
46
47
48
49
50
51
52
53
54
55
# File 'lib/igniter/store/tcp_adapter.rb', line 45

def start
  until @stopped
    begin
      socket = @server.accept
    rescue IOError, Errno::EBADF
      break
    end
    t = Thread.new(socket) { |s| handle_client(s) }
    @threads_mutex.synchronize { @threads << t }
  end
end

#start_asyncObject

Starts the accept loop in a background thread. Returns self.



58
59
60
61
62
63
64
65
# File 'lib/igniter/store/tcp_adapter.rb', line 58

def start_async
  @thread = Thread.new do
    Thread.current.abort_on_exception = false
    start
  end
  wait_until_ready
  self
end

#stopObject



80
81
82
83
84
85
86
# File 'lib/igniter/store/tcp_adapter.rb', line 80

def stop
  @stopped = true
  @server&.close rescue nil
  @thread&.join(2) rescue nil
  @threads_mutex.synchronize { @threads.each { |t| t.join(1) rescue nil } }
  self
end

#wait_until_ready(timeout: 2) ⇒ Object

Blocks until the server socket is bound and ready.



68
69
70
71
72
73
74
75
76
77
78
# File 'lib/igniter/store/tcp_adapter.rb', line 68

def wait_until_ready(timeout: 2)
  @ready_mutex.synchronize do
    deadline = Time.now + timeout
    until @ready
      remaining = deadline - Time.now
      raise "TCPAdapter did not become ready within #{timeout}s" if remaining <= 0
      @ready_cond.wait(@ready_mutex, remaining)
    end
  end
  self
end