docs: function MorayClient(options) {
      var self = this;
      var coptions, cueballOptions, resolverInput;
      var resolver;
  
      EventEmitter.call(this);
  
      assert.object(options, 'options');
      assert.optionalObject(options.collector, 'options.collector');
      assert.object(options.log, 'options.log');
      assert.optionalBool(options.unwrapErrors, 'options.unwrapErrors');
      assert.optionalBool(options.failFast, 'options.failFast');
      assert.optionalBool(options.requireIndexes, 'options.requireIndexes');
      assert.optionalBool(options.requireOnlineReindexing,
          'options.requireOnlineReindexing');
  
      coptions = parseMorayParameters(options);
      cueballOptions = coptions.cueballOptions;
  
      /* Read-only metadata used for toString() and the like. */
      this.hostLabel = coptions.label;
  
      this.unwrapErrors = options.unwrapErrors ? true : false;
      this.failFast = options.failFast ? true : false;
      this.requireIndexes = options.requireIndexes ? true : false;
      this.requireOnlineReindexing =
          options.requireOnlineReindexing ? true : false;
  
      /* Helper objects. */
      this.log = options.log.child({
          component: 'MorayClient',
          domain: cueballOptions.domain
      }, true);
  
      this.log.debug(coptions, 'init');
  
      /* Optional artedi metrics collector that we'll pass to fast, if set. */
      if (options.collector) {
          this.collector = options.collector;
      }
  
      if (coptions.mode == 'srv') {
          resolverInput = cueballOptions.domain;
      } else {
          resolverInput = cueballOptions.domain + ':' +
              cueballOptions.defaultPort;
      }
  
      resolver = cueball.resolverForIpOrDomain({
          'input': resolverInput,
          'resolverConfig': {
              'resolvers': cueballOptions.resolvers,
              'recovery': cueballOptions.recovery,
              'service': cueballOptions.service,
              'defaultPort': cueballOptions.defaultPort,
              'maxDNSConcurrency': cueballOptions.maxDNSConcurrency,
              'log': this.log.child({ 'component': 'CueballResolver' }, true)
          }
      });
      if (resolver instanceof Error) {
          throw new VError(resolver, 'invalid moray client configuration');
      }
      resolver.start();
      this.cueballResolver = resolver;
  
      this.cueball = new cueball.ConnectionSet({
          'constructor': function cueballConstructor(backend) {
              return (self.createFastConnection(backend));
          },
          'log': this.log.child({ 'component': 'CueballSet' }, true),
          'resolver': resolver,
          'recovery': cueballOptions.recovery,
          'target': cueballOptions.target,
          'maximum': cueballOptions.maximum,
          'connectionHandlesError': true
      });
  
      /* Internal state. */
      this.nactive = 0;           /* count of outstanding RPCs */
      this.timeConnected = null;  /* time when first cueball conn established */
      this.ncontexts = 0;         /* counter of contexts ever created */
      this.activeContexts = {};   /* active RPC contexts (requests) */
      this.timeCueballInitFailed = null;   /* cueball entered "failed" */
  
      /*
       * State recorded when close() is invoked.  The closeState is one of:
       *
       *     MORAY_CS_OPEN        close() has never been invoked
       *
       *     MORAY_CS_CLOSING     close() has been invoked, but we have not
       *                          finished closing (presumably because outstanding
       *                          requests have not yet aborted)
       *
       *     MORAY_CS_CLOSED      close process has completed and there are no
       *                          connections in use any more.
       */
      this.closeState = MORAY_CS_OPEN;    /* see above */
      this.nactiveAtClose = null;         /* value of "nactive" at close() */
  
      /*
       * If requested, add a handler to ensure that the process does not exit
       * without this client being closed.
       */
      if (options.mustCloseBeforeNormalProcessExit) {
          this.onprocexit = function processExitCheck(code) {
              if (code === 0) {
                  throw (new Error('process exiting before moray client closed'));
              }
          };
          process.on('exit', this.onprocexit);
      } else {
          this.onprocexit = null;
      }
  
      this.pool = new MorayConnectionPool({
          'log': this.log,
          'cueballResolver': this.cueballResolver,
          'cueballSet': this.cueball
      });
  
      this.cueballOnStateChange = function (st) {
          self.onCueballStateChange(st);
      };
  
      this.cueball.on('stateChanged', this.cueballOnStateChange);
  }