Skip to content

Service discovery

.px.sd. addCallbacks add callbacks for logon and logoff events checkRunning whether a service is running executeLogon execute new logon callback for all running services getClass get services based on a class getHostPort get host and port for a service getHostPorts get hosts and ports for services getService get a cached service getServices get cached services getTemplate get services based on a table init initiate the module state logoff set the logoff callback for a service logon set the logon callback for a service

This module provides the ability to do service discovery. Solutions can use it to subscribe to updates of processes starting and stopping. The Control process acts as the discovery service by publishing messages when services logon or logoff.

This module is instantiated by a call to .px.sd.init. This subscribes to Control for service updates and caches the current state locally in the process.

For simple clients who only need to periodically check the service state, they can use the APIs described in this module to check the current state.

Supported operations are

  • get cached services table
  • get details for single or multiple services
  • get host and port for single or multiple services
  • get services by class or template

Other clients may need to actively know when the service list changes. In this mode clients should implement handlers for logon and logoff events. For processes that take actions on logoff, please note the warning below.

Service logoff

Broadcast messages only indicate the status of the service to Control connection. A service may disconnect from Control but still be running. In this case it is up to the client to decide how to handle this scenario.

The suggested approach is to treat the process as disconnected only if a logoff broacast is received and the connection from the client to this service is disconnected. Therefore, the client should implement logoff and disconnect handlers that check both the Control and the handle status.

Active client

An example service client is implemented to illustrate how this works. The example is going to create a module to connect to running services. This module creates a table to maintain the online state of each service and connection to each of them. The state of each service is updated through logon/off event handlers.

  • Define a table which will store running services
  • Define a timer function to connect to each service and cache the handle
  • Define the disconnect function
  • this disconnects services if still they are still running
  • if not running, it removes them from the table
// Store running services 
.sol.runningServices:1!flip `process`class`handle!"ssi"$\:()

// Define the logon callback analytic 
.sol.connect:{[]
  data:select process, handle:@[hopen; ; 0Ni] 
  each .px.sd.getHostPorts[process] from .px.sd.running;
  `.sol.runningServices upsert data; }

// Add connect function to timer
.d.prcl.addFunctToTimer[`.sol.connect; (); 0Nz; 0Wz; 1000i; 1b]

// Define the logoff callback analytic which takes the handle 
.sol.serviceDisconnect:{[hn]
  cl:0!select from .sol.runningServices where handle=hn;
  if[not count cl; :()];    
  .log.out[.z.h;"Service disconnect for process "; pname:first cl`process];

  $[.px.sd.checkRunning[pname];
    update handle:0Ni from `.sol.runningServices where process=pname;
    delete from `.sol.runningServices where process=pname
   ]; }

The below code defines the logon and logoff callbacks. These will be triggered whenever the list of running services changes.

.sol.serv.logon:{[x]
  .log.out[.z.h;"New service available";`process`port!(x`process;x`port)];
  `.sol.runningServices upsert select process, class from x; }

.sol.serv.logoff:{[x]  
  if[not null .sol.runningServices[x`process]`handle; :()];

  .log.out[.z.h;"Service logged off";`process`port!(x`process;x`port)];
  delete from `.sol.runningServices where process=x`process; }

After the above functions are defined, the module can be initialized by defining the callbacks and running the init method.

.px.sd.addCallbacks[`.sol.serv.logon; `.sol.serv.logoff]
.px.sd.init[]

Passive client

For a passive client, services details can be requested using a set of public APIs.

.px.sd.checkRunning     Check if a service is running
.px.sd.getClass         Get services based on their class
.px.sd.getHostPort/s    Get the hostport of a services 
.px.sd.getServices      Get the cached services
.px.sd.getTemplate      Get all running services based on their template

.px.sd.addCallbacks

Add callbacks for logon and logoff events

.px.sd.addCallbacks[x;y]

Where x and y are symbol atoms naming a logon API, sets corresponding callbacks – null for a null symbol.

.px.sd.addCallbacks[`.qr.serv.logon; `.qr.serv.logoff]

No logoff handler:

.px.sd.addCallbacks[`.qr.serv.logon; `]

.px.sd.checkRunning

Check if a service is running

.px.sd.checkRunning x

Where x is the name of a process as a symbol atom, returns as a boolean whether the process is running.

q).px.sd.checkRunning `fx_rdb_143
1b

.px.sd.executeLogon

Execute new logon callback for all running services

.px.sd.executeLogon x

Where x is the name of a logon API as a symbol atom, executes the callback for all running services.

.px.sd.getClass

Get services based on class

.px.sd.getClass x

Where x is a class or list of classes as a symbol atom or vector, returns a table of running services of those classes.

q).px.sd.getClass[`fx_qp`fx_hdb]
process   host                             port  tls template class
--------------------------------------------------------------------
fx_qp_142  dev-ubuntu.firstderivatives.com 45737 off DS_QP    fx_qp

.px.sd.getHostPort

Get host and port for a service

.px.sd.getHostPort x

Where x is the name of a process as a symbol atom, returns as a symbol the corresponding host and port.

q).px.sd.getHostPort `fx_rdb_143
`:dev-ubuntu.firstderivatives.com:33715

.px.sd.getHostPorts

Get hosts and ports for a list of services

.px.sd.getHostPorts x

Where x is the names of processes as a symbol vector, returns as a symbol list the corresponding hosts and ports.

q).px.sd.getHostPorts `fx_rdb_143`fx_qp_142
`:dev-ubuntu.firstderivatives.com:33715`:dev-ubuntu.firstderivatives.com:45737

.px.sd.getService

Get a cached service

.px.sd.getService x

Where x is a process name as a symbol atom, returns a dictionary of information about it.

q).px.sd.getService[`fx_rdb_143]
process | fx_rdb_143
class   | fx_rdb
host    | dev-ubuntu.firstderivatives.com
port    | 33715
tls     | off
template| DS_RDB

.px.sd.getServices

Get the cached services

.px.sd.getServices x

Where x is a list of process names as a symbol vector, returns a table of information about them. The generic null denotes all processes.

q).px.sd.getServices (::)
process    class  host                            port  tls template
--------------------------------------------------------------------
fx_rdb_140 fx_rdb dev-ubuntu.firstderivatives.com 38040 off DS_RDB  
fx_rdb_141 fx_rdb dev-ubuntu.firstderivatives.com 46576 off DS_RDB  
fx_qp_142  fx_qp  dev-ubuntu.firstderivatives.com 45737 off DS_QP   
fx_rdb_143 fx_rdb dev-ubuntu.firstderivatives.com 33715 off DS_RDB 

q)q_.px.sd.getServices `fx_rdb_143`fx_qp_142
process    class  host                            port  tls template
--------------------------------------------------------------------
fx_qp_142  fx_qp  dev-ubuntu.firstderivatives.com 45737 off DS_QP   
fx_rdb_143 fx_rdb dev-ubuntu.firstderivatives.com 33715 off DS_RDB  

.px.sd.getTemplate

Get services based on templates

.px.sd.getTemplate x

Where x is the template name of a process as a symbol atom, returns a table of corresponding processes.

q).px.sd.getTemplate[`DS_RDB]
process    class  host                            port  tls template
--------------------------------------------------------------------
fx_rdb_140 fx_rdb dev-ubuntu.firstderivatives.com 38040 off DS_RDB
fx_rdb_141 fx_rdb dev-ubuntu.firstderivatives.com 46576 off DS_RDB
fx_rdb_143 fx_rdb dev-ubuntu.firstderivatives.com 33715 off DS_RDB

.px.sd.init

Initiate the module state

.px.sd.init[]

Initiates the module state and returns null.

.px.sd.logoff

Set the callback for when a service logs off

.px.sd.logoff[x;y]

Where

  • x is a broadcast topic as a symbol atom
  • y is a dictionary of service details

sets the callback for when the service logs off.

q)data
process | `fx_rdb_a
class   | `fx_rdb
host    | `localhost
port    | 3000
tls     | `
template| `DS_RDB

q).px.sd.logoff[`Service.Logoff; data]

.px.sd.logon

Set the callback for when a new service comes online

.px.sd.logon[x;y]
  • x is a broadcast topic as a symbol atom
  • y is a dictionary of service details

sets the callback for when the service logs on.

q)data
process | `fx_rdb_a
class   | `fx_rdb
host    | `localhost
port    | 3000
tls     | `
template| `DS_RDB

q).px.sd.logon[`Service.Logon; data]