Class: Beachcomber::Client

Inherits:
Object
  • Object
show all
Defined in:
lib/beachcomber/client.rb

Overview

Client sends individual requests, opening a fresh socket connection for each call. For workloads that issue many queries per invocation, use #session to reuse a persistent connection.

Examples:

client = Beachcomber::Client.new
result = client.get('git.branch', path: '/repo')
puts result.data if result.hit?

Constant Summary collapse

RETRY_BACKOFFS =
[0.250, 0.500, 1.000].freeze

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(socket_path: nil, timeout: DEFAULT_TIMEOUT) ⇒ Client

Returns a new instance of Client.

Parameters:

  • socket_path (String, nil) (defaults to: nil)

    explicit socket path; auto-discovered when nil

  • timeout (Numeric) (defaults to: DEFAULT_TIMEOUT)

    connect/read timeout in seconds (default 0.1)



234
235
236
237
# File 'lib/beachcomber/client.rb', line 234

def initialize(socket_path: nil, timeout: DEFAULT_TIMEOUT)
  @socket_path = socket_path || Discovery.socket_path
  @timeout     = timeout
end

Class Method Details

._connect_with_retry(sock_path) ⇒ UNIXSocket

Connect to a Unix socket with 3 retries (250ms/500ms/1s exponential). Retries on ECONNREFUSED and ENOENT only — other errors surface immediately. Intended to cover the brief restart window when the daemon is restarting.

Parameters:

  • sock_path (String)

    absolute path to the Unix domain socket

Returns:

  • (UNIXSocket)

Raises:

  • (Errno::ECONNREFUSED, Errno::ENOENT)

    after all retries are exhausted



346
347
348
349
350
351
352
353
354
355
356
357
358
# File 'lib/beachcomber/client.rb', line 346

def self._connect_with_retry(sock_path)
  last_error = nil
  RETRY_BACKOFFS.each do |backoff|
    begin
      return UNIXSocket.new(sock_path)
    rescue Errno::ECONNREFUSED, Errno::ENOENT => e
      last_error = e
      sleep backoff
    end
  end
  # Final attempt — raises if still failing.
  UNIXSocket.new(sock_path)
end

Instance Method Details

#get(key, path: nil) ⇒ Result

Reads a cached value.

Parameters:

  • key (String)

    e.g. “git.branch” or “git”

  • path (String, nil) (defaults to: nil)

    optional working-directory context

Returns:

Raises:



246
247
248
249
250
# File 'lib/beachcomber/client.rb', line 246

def get(key, path: nil)
  req = { op: 'get', key: key }
  req[:path] = path if path
  with_session { |s| s.send(:roundtrip, req) }
end

#get_with_flags(key, path: nil, force: false, wait: false) ⇒ Result

Reads a cached value with protocol flags.

Parameters:

  • key (String)
  • path (String, nil) (defaults to: nil)
  • force (Boolean) (defaults to: false)

    bypass cache and recompute

  • wait (Boolean) (defaults to: false)

    block until a fresh value is available

Returns:



259
260
261
# File 'lib/beachcomber/client.rb', line 259

def get_with_flags(key, path: nil, force: false, wait: false)
  with_session { |s| s.get_with_flags(key, path: path, force: force, wait: wait) }
end

#helloHelloInfo

Sends a hello handshake and returns server info.

Returns:



286
287
288
# File 'lib/beachcomber/client.rb', line 286

def hello
  with_session { |s| s.hello }
end

#introspect(subject, duration_secs: nil) ⇒ IntrospectResponse

Introspects a daemon subsystem.

Parameters:

  • subject (String)

    one of the IntrospectSubject constants

  • duration_secs (Numeric, nil) (defaults to: nil)

Returns:



306
307
308
# File 'lib/beachcomber/client.rb', line 306

def introspect(subject, duration_secs: nil)
  with_session { |s| s.introspect(subject, duration_secs: duration_secs) }
end

#put(key, data = nil, ttl: nil, path: nil) ⇒ nil

Writes a value into the daemon cache.

Parameters:

  • key (String)
  • data (Object, nil) (defaults to: nil)
  • ttl (Numeric, nil) (defaults to: nil)

    time-to-live in seconds

  • path (String, nil) (defaults to: nil)

Returns:

  • (nil)


297
298
299
# File 'lib/beachcomber/client.rb', line 297

def put(key, data = nil, ttl: nil, path: nil)
  with_session { |s| s.put(key, data, ttl: ttl, path: path) }
end

#refresh(key, path: nil) ⇒ Object

Forces the daemon to recompute a provider/key.

Parameters:

  • key (String)
  • path (String, nil) (defaults to: nil)

Raises:



269
270
271
272
273
274
# File 'lib/beachcomber/client.rb', line 269

def refresh(key, path: nil)
  req = { op: 'refresh', key: key }
  req[:path] = path if path
  with_session { |s| s.send(:roundtrip, req) }
  nil
end

#session {|Session| ... } ⇒ Object

Opens a persistent session and yields it to the block. The connection is closed automatically when the block returns (even on exception).

Yields:

Returns:

  • the block’s return value



329
330
331
332
333
334
335
# File 'lib/beachcomber/client.rb', line 329

def session
  sock = open_socket
  sess = Session.new(sock, @timeout)
  yield sess
ensure
  sess&.close
end

#statusArray<CacheRow>

Returns cache rows from the daemon.

Returns:



279
280
281
# File 'lib/beachcomber/client.rb', line 279

def status
  with_session { |s| s.status }
end

#watch(key, path: nil) ⇒ WatchStream

Opens a persistent watch subscription. Returns a WatchStream (Enumerable). The caller is responsible for closing the stream.

Parameters:

  • key (String)
  • path (String, nil) (defaults to: nil)

Returns:



316
317
318
319
320
321
322
# File 'lib/beachcomber/client.rb', line 316

def watch(key, path: nil)
  sock = open_socket
  req  = { op: 'watch', key: key }
  req[:path] = path if path
  sock.write(JSON.generate(req) + "\n")
  WatchStream.new(sock)
end