npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2024 – Pkg Stats / Ryan Hefner

startle

v1.3.3

Published

Client and Server agents for starting and stopping remote scripts.

Downloads

10

Readme

npmBuild StatusCoverage Status

startle

Client and Server agents for starting, stopping and messaging remote scripts.

Intended primarily for use in capacity testing distributed applications where processes spanning multiple servers are required. Remote processes can be started, stopped, instrumented and communicated with via a single test script running on one developer workstation.

See also netrix for easily collecting metrics from the running test.

npm install startle --save-dev

Startle Server

Each host upon which scripts are to be run needs a running Startle Server. This can be found at node_modules/.bin/startle if not installed globally.

// on hosts 1 through 10
startle run-server --path . --token sEcrEt --group servers --debug

// on hosts 11 through x
startle run-server --path . --token sEcrEt --group clients --debug
  • path Must point to the root of the clone of the repo housing the scripts to be called upon remotely.
  • token A security token for the websocket/https server.
  • group Used by the controlling process to direct specific processes to only spawn at specific servers.

The server runs a websocket (socket.io) over an https server. A self-signed ssl keys and certs are auto generated unless paths for the keys are provided.

See startle —help and startle run-server —help.

The cli wraps an instance of the StartleServer class.

Remote Script API

const startle = require('startle');

These methods are used in the scripts installed on each host running the Startle Server. They will be called upon remotely to start/stop by the controlling process.

startle.onStart(handler)

  • handler <Function> Handler to run when this script is called to start.

startle.onStop(handler)

  • handler <Function> Handler to run when this script is called to stop.

startle.on(eventName, handler)

  • eventName <string> Event name as sent from the controlling process.
  • handler <Function>

Subscribe to receive events from the controlling process.

startle.send(eventName[, …args])

  • eventName <string> Event name to send to the controlling process.
  • …args

Remote scripts can be instrumented by incrementing counters and setting gauges. These metrics are aggregated (per second) and emitted at the Controlling Process.

startle.increment(counterName[, value])

  • counterName <string> Increment an instrumentation counter.
  • value <integer> Default 1

startle.gauge(gaugeName, value)

  • gaugeName <string> The gauge to set.
  • value <number> The value
Example1
startle.onStart((opts, done) => {
  // start the service and call done once it's up.
  done();
});

startle.onStop((opts, done) => {
  // stop the service and call done once it's down.
  done();
});

startle.on('ping', function (timbre) { 
  startle.send('pong', {assentionRate: 777});
});
// anywhere in the remote script
const {increment, gauge} = require('startle');

increment('counter_name');
gauge('gauge_name', 3.14);

Controlling Process API

These methods are used in the test script running on your workstation. They allow for spawning, terminating and interacting with all the remote script processes at all the hosts running the StartleServer.

startle.createAgent(connections[, defaults])

  • connections <Object> Connection parameters (or Array of connection parameters) of StartleServer(s) to connect to.
    • token <string> Required. The security token with which the server was initialised.
    • host <string> Hostname or ipaddress where the server is listening.
    • port <number> Server's port.
    • rejectUnauthorized <boolean> Set true if the server is using self-signed cert.
  • defaults <Object> Defaults to apply across connections array.
  • Returns <Promise> Resolves with a connected instance of class StartleAgent

Create an agent connected to all StartleServers for remote controlling processes at each.

Example2
// (mocha, ES6)
var agent;

before('start agent', async function () {
  var connections = [{
    host: '172.34.1.32',
    // token: 'sEcrEt',
    // rejectUnauthorized: false,
  }, {
    host: '172.34.1.32',
    // token: 'sEcrEt',
    // rejectUnauthorized: false,
  }, {
    host: '172.34.1.32',
    // token: 'sEcrEt',
    // rejectUnauthorized: false
  }];
  
  var defaults = {
    token: 'sEcrEt',
    rejectUnauthorized: false
  }
  
  agent = await startle.createAgent(connections, defaults);
  
  agent.on('metrics', (timestamp, metrics) => {
    // use metrics collected from remote processes
  });
});

after('stop agent', async function () {
  if (agent) await agent.destroy();
});
Example3
// (mocha, ES5)
var agent;

before('start agent', function() {
  // var...
  return startle.createAgent(connections, defaults)
    .then(function (_agent) {
      agent = _agent;
    });
});

after('stop agent', function () {
  if (!agent) return;
  return agent.destroy();
});

class StartleAgent

const { StartleAgent } = require('startle');

Agent interface for connecting to multiple Startle Servers and spawning processes on each.

new StartleAgent(connections[, defaults])

  • connections <Object> Same as startle.createAgent()
  • defaults <Object> Same as startle.createAgent()
  • Returns <StartleAgent>

Used internally. See startle.createAgent(connections[, defaults]).

StartleAgent is an EventEmitter and emits the following events:

Event: 'metrics'

  • timestamp <number> EPOCH Milliseconds.
  • metrics <Object>
    • counters <Object> Aggregated counter values.
    • gauges <Object> Aggregated guage values including count or processes by group name.

Emitted every second with instrumentaion data collected from all remote processes using the startle.increment() and startle.gauge() functions.

agent.connect()

  • Returns <Promise> Resolves with the agent instance.

Connects to all Startle Servers passes into the connections parameter of the constructor.

Used internally. See startle.createAgent(connections[, defaults]).

agent.destroy()

  • Returns <Promise>

Disconnects from all Startle Servers.

See example in mocha after hook.

This method is typically run in an "after hook" and should only be run once all remote processes have been stopped. See [startleProcess.stop([localOpts][, opts])](#startleprocessstoplocalopts-opts). If it runs when remote processes are still busy an error is logged to console. Note that the Startle Server will terminate any stray processes on the disconnection anyway.

agent.reset()

Resets metrics. Removes all counters and gauges.

agent.start(script[, opts])

  • script <string || Object> Path of the remote script to run. Or object containing:
    • script <string> Required. Path of the remote script to run.
    • group <string> The server group within which to run the script.
    • timeout <number> Timeout waiting for script to start. Default 10 seconds.
  • opts <Object || Function> Options to pass to the remote script's onStart handler.
  • Returns <Promise> Resolves with running instance if class StartleProcess

The script parameter must contain the script's path relative to the remote repo root as passed to the Startle Server.

Example5
// at remote
startle run-server --path /home/you/git_repos/repo-name
// in local test will start remote script at path:
// /home/you/git_repos/repo-name/test/procs/some-script.js
agent.start('test/procs/some-script').then...

Specifying the group allows for the starting of multiple scripts that will be spread evenly across all Startle Servers that were started with that group name.

Important: In hooks and tests the mocha timeout should be disabled because the agent.start() method will not know when the test of hook times out and will continue in the background. Instead the

Example6
var servers;

before('start 10 servers', async function () {
  this.timeout(0); // !must! allow agent.start() to handle timeout
  
  var promises = [];
  for (var i = 0; i < 10; i++) {
    promises.push(agent.start({
      script: 'test/procs/cluster-server',
      group: 'servers', // spread across all servers in this group
      timeout: 10000    // the default timeout
    }));
  }
  
  servers = await Promise.all(promises)
});

after('stop servers', async function () {
  this.timeout(0);
  await Promise.all(servers.map(server => server.stop()));
});

The opts are passed to the remote script's onStart handler (Example1). opts can either be an Object or a Function. The function will be run locally to generate the opts that will be passed to the remote server. The function is passed a state containing details of all the processes already started arranged by group as well as the count of processes already present in the given group and the target server where the process will be started.

Example7
// opts as object

agent.start('test/procs/cluster-client', {opt: 'ions'})


// opts as function

agent.start('test/procs/cluster-server', function (state) {
  // return an object to become the [opts]
  return {
    // eg. only the first server started seeds the cluster
    seedTheCluster: state.count == 0
  }
})

class StartleProcess

new StartleProcess(opts)

Used internally.

startleProcess.hostname

The hostname of the server where this process was started.

startleProcess.address

The first public IP address of the server where this process was started.

startleProcess.run

The run options passed to this process.

startleProcess.opts

The script start opts passed to this process.

startleProcess.state

starting, running, stopping, killing, ended

startleProcess.timestamp

The state's timestamp.

startleProcess.stop([localOpts][, opts])

  • localOpts
    • timeout <number> Timeout for stop. Default 10 seconds.
  • Returns <Promise> Waits for stop confirmation from the remote server.

Stop the remote process cleanly. This calls then onStop() handler in the remote script and passesopts to it (Example1). The handler is expected to tear down the process and it should then exit gracefully. If it does not then some or other resources are not being relinquished. Try startleProcess.kill() if all else fails.

See after hook in Example6

Important: For stops that take long the mocha timeout should be disabled. Use localOpts.timeout argument instead.

startleProcess.kill([localOpts])

  • localOpts
    • timeout <number> Timeout for stop. Default 10 seconds.
  • Returns <Promise> Waits for kill confirmation from the remote server.

Kill the remote process.

startleProcess.send(eventName[, …args])

Send event to the remote process.

startleProcess.on(eventName, handler)

Subscribe to receive event from the remote process.

Example 8

Assume we already have an agent per Example3 and spawning process in Example1

var remoteProcess; // becomes instance of startleProcess

before('start remote process', function () {
  this.timeout(0);
  return agent.start('test/procs/process1', {op: 'tions'})
    .then(function (rp) {
      remoteProcess= rp;
    })
});

after('stop remote process', function () {
  if (!remoteProcess) return;
  this.timeout(0);
  return remoteProcess.stop({op: 'tions'});
});

it('e.g. sends message to process', function (done) {
  remoteProcess.send('ping', 23);
  remoteProcess.on('pong', function (natureOfPong) {
    // 
  });
});

class StartleServer

const { StartleServer } = require('startle');

Used internally.

class StartleClient

  const { StartleClient } = require('startle');

Used internally.

##Remote Script API supports async/await

Example9
const startle = require('startle');
const Server = require('...');
var server;

startle.onStart(async opts => server = await Server.create(opts));

startle.onStop(async opts => await server.stop(opts));