Skip to content

deployable/zk-client

 
 

Repository files navigation

zk-client

WIP

A pure Javascript ZooKeeper client module for Node.js. Forked from https://github.com/alexguan/node-zookeeper-client and updated to use ES2015 and the client API has moved to using Promises.

The module was originally designed to resemble the ZooKeeper Java client API but has been tweaked a bit to fit with Node.js. Developers that are familiar with the ZooKeeper Java client should be able to pick it up quickly.

This module has been tested to work with ZooKeeper version 3.4.*. Docker compose definitions of the test environments are included in the project.


Installation

You can install it using npm or yarn:

npm install zk-client --save
yarn add zk-client

Example

Connect and create a node:

const zk = require('zk-client');
const client = zk.client('localhost:2181,localhost:2182')
let zkpath = process.argv[2];

client.connect().then(() => {
  console.log('Connected to the server.')

  client.create(zkpath).then(res => {
    console.log('Node: %s is successfully created.', path);
  })
  .catch(error => {
    console.log('Failed to create node: %s due to: %s.', zkpath, error)
  })
  .finally(()=> {
    client.close()
  })

})
.catch(error => {
  console.log('Failed to connect to hosts: %s', hostlist)
  process.exit(1)
})

List and watch the children of given node:

const zk = require('zk-client')

const client = zk.createClient('localhost:2181')
let zkpath = process.argv[2]

function listChildren(zkpath) {
  let watcher = (event) => {
    console.log('Got watcher event: %s', event)
    listChildren(zkpath)
  }
  return client.getChildren(path, watcher)
    .then((children, stat) => {
      console.log('Children of %s are: %j.', path, children)
    })
    .catch(error => {
      console.log('Failed to list children of %s due to: %s.', path, error)
    })
}

client.connect().then(()=> {
  console.log('Connected to ZooKeeper.')
  return listChildren(zkpath)
})

More examples can be found here.

Documentation

Client client(connectionString, [options])

Factory method to create a new zookeeper client instance.

Arguments

  • connectionString String - Comma separated host:port pairs, each represents a ZooKeeper server. You can optionally append a chroot path, then the client would be rooted at the given path. e.g.

    'localhost:3000,locahost:3001,localhost:3002'
    'localhost:2181,localhost:2182/test'
  • options Object - An object to set the client options. Currently available options are:

    • sessionTimeout Session timeout in milliseconds, defaults to 30 seconds.
    • spinDelay The delay (in milliseconds) between each connection attempts.
    • retries The number of retry attempts for connection loss exception.

    Defaults options:

    {
        sessionTimeout: 30000,
        spinDelay : 1000,
        retries : 0
    }

Example

let client = zk.client(
    'localhost:2181/test',
    { sessionTimeout: 10000 }
)

Client

This is the main class of ZooKeeper client module. An application can use the client method that is imported when you require('zk-client') to instantiate a Client.

Once a connection from the client to the server is established, a session id is assigned to the client. The client will start sending heart beats to the server periodically to keep the session valid.

If the client fails to send heart beats to the server for a prolonged period of time (exceeding the sessionTimeout value), the server will expire the session. The client object will no longer be usable.

If the ZooKeeper server that the client is currently connected to fails, or otherwise does not respond, the client will automatically try to connect to another server before its session times out. If successful, the application can continue to use the same client.

This class inherits from events.EventEmitter class, see Event for details.

Promise connect()

Initiate the connection to the provided server list (ensemble). The client will pick an arbitrary server from the list and attempt to connect to it. If the establishment of the connection fails, another server will be tried (picked randomly) until a connection is established or the close method is invoked.


Promise close()

Close this client. Once the client is closed, its session becomes invalid. All the ephemeral nodes in the ZooKeeper server associated with the session will be removed. The watchers left on those nodes (and on their parents) will be triggered.


Promise create(path, [data], [acls], [mode])

Create a node with given path, data, acls and mode.

Arguments

  • path String - Path of the node.
  • options Object - optiona paramaters:
    • data Buffer - The data buffer, optional, defaults to null.
    • acls Array - An array of ACL objects, optional, defaults to ACL.OPEN_ACL_UNSAFE
    • mode CreateMode - The creation mode, optional, defaults to CreateMode.PERSISTENT

Example

let options = {
  data: Buffer.from('data'),
  mode: CreateMode.EPHEMERAL
}
zookeeper.create('/test/demo', options).then(res => {
  console.log('Node: %s is created.', path)
})
.catch(error => {
  console.log(error.stack)
})

Promise remove(path, [{ version: Number }])

Delete a node with the given path and version. If version is provided and not equal to -1, the request will fail when the provided version does not match the server version.

Arguments

  • path String - Path of the node.
  • version Number - The version of the node, optional, defaults to -1.

Example

zookeeper.remove('/test/demo').then(res => {
  console.log('Node is deleted.')
})
.catch(error => {
  console.log(error.stack);
})

Promise exists(path, [{ watcher: Function }])

Check the existence of a node. The promise will resolve with the stat of the given path, or null if no such node exists.

If the watcher function is provided and the operation is successful (no error), a watcher will be placed on the node with the given path. The watcher will be triggered by a successful operation that creates the node, deletes the node or sets the data on the node.

Arguments

  • path String - Path of the node.
  • watcher(event) Function - The watcher function, optional. The event is an instance of Event
  • Resolves stat an instance of Stat.

Example

zookeeper.exists('/test/demo').then(stat => {
  if (stat) {
    console.log('Node exists. Updated at %s', stat.mtime)
  } else {
    console.log('Node does not exist.')
  }
}).catch(error => {
  console.log(error.stack)
})

Promise getChildren(path, [{ watcher: Function }])

For the given node path, retrieve the children list and the stat. The children will be an unordered list of strings.

If the watcher callback is provided and the operation is successful, a watcher will be placed the given node. The watcher will be triggered when an operation successfully deletes the given node or creates/deletes the child under it.

Arguments

  • path String - Path of the node.
  • watcher(event) Function - The watcher function, optional. The event is an instance of Event
  • Resolves { stat: Stat, children: Array }

Example

zookeeper.getChildren('/test/demo').then(res => {
  console.log('Children are: %j.', children)
}).catch(error => {
  console.log(error.stack)
})

Promise getData(path, { watcher: Function })

Retrieve the data and the stat of the node of the given path. If the watcher function is provided and the operation is successful (no error), a watcher will be placed on the node with the given path. The watch will be triggered by a successful operation which sets data on the node, or deletes the node.

Arguments

  • path String - Path of the node.
  • watcher(event) Function - The watcher function, optional. The event is an instance of Event
  • Resolves { data: Buffer, stat: Stat }

Example

let watcher = (event) => console.log('Got event: %s.', event)
zookeeper.getData('/test/demo', { watcher: watcher }).then(res => {
  console.log('Got data: %s from %s', res.data.toString('utf8'), res.stat.mtime)
})
.catch(error => {
  console.log(error.stack)
})

Promise setData(path, data, [{ version: Number }])

Set the data for the node of the given path if such a node exists and the optional given version matches the version of the node (if the given version is -1, it matches any node's versions). The stat of the node will be resolved.

Arguments

  • path String - Path of the node.
  • data Buffer - The data buffer.
  • version Number - The version of the node, optional, defaults to -1.
  • Resolves an instance of Stat.

Example

zookeeper.setData('/test/demo', null, 2).then(stat => {
  console.log('Data is set.')
})
.catch(error => {
  console.log(error.stack)
})

Promise getACL(path)

Resolve the list of ACL and the Stat of the node of the given path.

Arguments

  • path String - Path of the node.
  • Resolves { acls: [ACL], stat: Stat }

Example

zookeeper.getACL('/test/demo').then(res => {
  console.log('ACL(s) are: %j', res.acls)
})
.catch(error => {
  console.log(error.stack)
})

Promise setACL(path, acls, [{ version: Number}])

Set the ACL for the node of the given path if such a node exists and the given version (optional) matches the version of the node on the server. (if the given version is -1, it matches any versions).

Arguments

  • path String - Path of the node.
  • acls Array - An array of ACL instances.
  • options Object
    • version Number - The version of the node, defaults to -1.
  • Resolves an instance of Stat for the node.

Example

let acls = [ zk.ACL.ip('127.0.0.1','crwd') ]
zookeeper.setACL('/test/demo', acls).then(stat => {
  console.log('ACL set')
})
.catch(error => {
  console.log(error.stack)
})

Transaction transaction()

Create and return a new Transaction instance which provides a builder object that can be used to construct and commit a set of operations atomically.

See Transaction for details.

Example

var transaction = zookeeper.transaction();

Promise mkdirp(path, [{ data: Buffer, acls: [ACL], mode: Number)}])

Create a given node including all parents, similar to mkdir -p.

Arguments

  • path String - Path of the node.
  • options Object
    • data Buffer - The data buffer, optional, defaults to null.
    • acls Array - An array of ACL objects, optional, defaults to ACL.OPEN_ACL_UNSAFE
    • mode CreateMode - The creation mode, optional, defaults to CreateMode.PERSISTENT
  • Resolves a String path on success

Example

zk.mkdirp('/test/demo/1/2/3').then(path => {
  console.log('Node: %s is created.', path)
})
.catch(error => {})
  console.log(error.stack)
})

void addAuthInfo(scheme, auth)

Add the specified scheme:auth information to this client.

Arguments

  • scheme String - The authentication scheme.
  • auth Buffer - The authentication data buffer.

Example

zookeeper.addAuthInfo('ip', new Buffer('127.0.0.1'));

State getState()

Return the current client/connection state.

Example

const client = zk.createClient('127.0.0.1:2181')
console.log('Current state is: %s', client.getState())

Buffer getSessionId()

Returns the session id of this client instance. The value returned is not valid until the client connects to a server and may change after a re-connect.

The id returned is a long integer stored in an 8 byte Buffer since Javascript does not support long integer natively.

Example

const client = zk.createClient('127.0.0.1:2181')
let id = client.getSessionId()
console.log('Session id is: %s', id.toString('hex'))

Buffer getSessionPassword()

Returns the session password of this client instance. The value returned is not valid until the client connects to a server and may change after a re-connect.

The value returned is an instance of Buffer.

Example

const client = zk.createClient('127.0.0.1:2181')
let pwd = client.getSessionPassword()

Number getSessionTimeout()

Returns the negotiated session timeout (in milliseconds) for this client instance. The value returned is not valid until the client connects to a server and may change after a re-connect.

Example

const client = zk.createClient('127.0.0.1:2181')
let sessionTimeout = client.getSessionTimeout()

State

After the connect() method is invoked, the ZooKeeper client starts its life cycle and transitions its state as described in the following diagram.

state transition

There are two ways to watch the client state changes:

1. Node.js convention: Register event listener on the specific event which interests you. The following is the list of events that can be watched:

  • connected - Client is connected and ready.
  • connectedReadOnly - Client is connected to a readonly server.
  • disconnected - The connection between client and server is dropped.
  • sessionExpired - The client session is expired.
  • authenticationFailed - Failed to authenticate with the server.

Note: some events (e.g. connected or disconnected) maybe be emitted more than once during the client life cycle.

Example

client.on('connected', () => console.log('Client state is changed to connected.')

2. Java client convention: Register one event listener on the state event to watch all state transitions. The listener callback will be called with an instance of the State class. The following is the list of exported state instances:

  • State.CONNECTED - Client is connected and ready.
  • State.CONNECTED_READ_ONLY - Client is connected to a readonly server.
  • State.DISCONNECTED - The connection between client and server is dropped.
  • State.EXPIRED - The client session is expired.
  • State.AUTH_FAILED - Failed to authenticate with the server.
client.on('state', state => {
  if (state === zk.State.SYNC_CONNECTED) {
    console.log('Client state is changed to connected.')
  }
})

Event

Optionally, you can register watcher functions when calling exists, getChildren and getData methods. The watcher function will be called with an instance of Event.

Properties

There are four type of events are exposed as Event class properties.

  • NODE_CREATED - Watched node is created.
  • NODE_DELETED - watched node is deleted.
  • NODE_DATA_CHANGED - Data of watched node is changed.
  • NODE_CHILDREN_CHANGED - Children of watched node is changed.

Number getType()

Return the type of the event.


String getName()

Return the name of the event.


Number getPath()

Return the path of the event.


String toString()

Return a string representation of the event.


Transaction

Transaction provides a builder interface to construct and commit a set of operations atomically.

Example

const client = zk.createClient(process.argv[2] || 'localhost:2181');

client.connect().then(()=>{
  return client.transaction()
    .create('/txn')
    .create('/txn/1', Buffer.from('transaction'))
    .setData('/txn/1', Buffer.from('test'), -1)
    .check('/txn/1')
    .remove('/txn/1', -1)
    .remove('/txn')
    .commit()
})
.then(results => {
  console.log('Transaction completed.')
  client.close()
})
.catch(error => {
  console.log('Failed to execute the transaction: %s, results: %j', error.results)
})

Transaction create(path, [data], [acls], [mode])

Add a create operation with given path, data, acls and mode.

Arguments

  • path String - Path of the node.
  • data Buffer - The data buffer, optional, defaults to null.
  • acls Array - An array of ACL objects, optional, defaults to ACL.OPEN_ACL_UNSAFE
  • mode CreateMode - The creation mode, optional, defaults to CreateMode.PERSISTENT

Transaction setData(path, data, [version])

Add a set-data operation with the given path, data and optional version.

Arguments

  • path String - Path of the node.
  • data Buffer - The data buffer, or null.
  • version Number - The version of the node, optional, defaults to -1.

Transaction check(path, [version])

Add a check (existence) operation with given path and optional version.

Arguments

  • path String - Path of the node.
  • version Number - The version of the node, optional, defaults to -1.

Transaction remove(path, data, version)

Add a delete operation with the given path and optional version.

Arguments

  • path String - Path of the node.
  • version Number - The version of the node, optional, defaults to -1.

void commit(callback)

Execute the transaction atomically.

Arguments

  • callback(error, results) Function - The callback function.

ZkException

If the requested operation fails due to reason related to the ZooKeeper server, the error which the promise is rejected with will be an instance of ZkException class.

The exception can be identified through its error code, the following is the list of error codes that are exported through ZkException class.

  • Exception.OK
  • Exception.SYSTEM_ERROR
  • Exception.RUNTIME_INCONSISTENCY
  • Exception.DATA_INCONSISTENCY
  • Exception.CONNECTION_LOSS
  • Exception.MARSHALLING_ERROR
  • Exception.UNIMPLEMENTED
  • Exception.OPERATION_TIMEOUT
  • Exception.BAD_ARGUMENTS
  • Exception.API_ERROR
  • Exception.NO_NODE
  • Exception.NO_AUTH
  • Exception.BAD_VERSION
  • Exception.NO_CHILDREN_FOR_EPHEMERALS
  • Exception.NODE_EXISTS
  • Exception.NOT_EMPTY
  • Exception.SESSION_EXPIRED
  • Exception.INVALID_CALLBACK
  • Exception.INVALID_ACL
  • Exception.AUTH_FAILED

Example

zk.create('/test/demo')
.catch(error => {
  if (error.checkCode && error.checkCode('NODE_EXISTS')) return console.log('Node already exists')
  console.log(error.stack)
})

Boolean checkCode()

Return if the error code matches the String name or Integer code


Number getCode()

Return the error code of the exception.


String getPath()

Return the associated node path of the exception. The path can be undefined if the exception is not related to a node.

--

String getName()

Return the exception name as defined in aforementioned list.


String toString()

Return the exception in a readable string.


Dependency

This module depends on the following third-party libraries:

License

This module is licensed under MIT License

Packages

No packages published

Languages

  • JavaScript 100.0%