You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

164 lines
4.7 KiB
JavaScript

/*
* Copyright DataStax, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
'use strict';
const util = require('util');
const utils = require('./utils');
const errors = require('./errors');
const requests = require('./requests');
const ExecuteRequest = requests.ExecuteRequest;
const QueryRequest = requests.QueryRequest;
const state = {
init: 0,
completed: 1,
timedOut: 2,
cancelled: 3
};
/**
* Maintains the state information of a request inside a Connection.
*/
class OperationState {
/**
* Creates a new instance of OperationState.
* @param {Request} request
* @param {Function} rowCallback
* @param {Function} callback
*/
constructor(request, rowCallback, callback) {
this.request = request;
this._rowCallback = rowCallback;
this._callback = callback;
this._timeout = null;
this._state = state.init;
this._rowIndex = 0;
/**
* Stream id that is set right before being written.
* @type {number}
*/
this.streamId = -1;
}
/**
* Marks the operation as cancelled, clearing all callbacks and timeouts.
*/
cancel() {
if (this._state !== state.init) {
return;
}
if (this._timeout !== null) {
clearTimeout(this._timeout);
}
this._state = state.cancelled;
this._callback = utils.noop;
}
/**
* Determines if the operation can be written to the wire (when it hasn't been cancelled or it hasn't timed out).
*/
canBeWritten() {
return this._state === state.init;
}
/**
* Determines if the response is going to be yielded by row.
* @return {boolean}
*/
isByRow() {
return this._rowCallback && (this.request instanceof ExecuteRequest || this.request instanceof QueryRequest);
}
/**
* Creates the timeout for the request.
* @param {ExecutionOptions} execOptions
* @param {Number} defaultReadTimeout
* @param {String} address
* @param {Function} onTimeout The callback to be invoked when it times out.
* @param {Function} onResponse The callback to be invoked if a response is obtained after it timed out.
*/
setRequestTimeout(execOptions, defaultReadTimeout, address, onTimeout, onResponse) {
if (this._state !== state.init) {
// No need to set the timeout
return;
}
const millis = execOptions.getReadTimeout() !== undefined ? execOptions.getReadTimeout() : defaultReadTimeout;
if (!(millis > 0)) {
// Read timeout disabled
return;
}
const self = this;
this._timeout = setTimeout(function requestTimedOut() {
onTimeout();
const message = util.format('The host %s did not reply before timeout %d ms', address, millis);
self._markAsTimedOut(new errors.OperationTimedOutError(message, address), onResponse);
}, millis);
}
setResultRow(row, meta, rowLength, flags, header) {
this._markAsCompleted();
if (!this._rowCallback) {
return this.setResult(new errors.DriverInternalError('RowCallback not found for streaming frame handler'));
}
this._rowCallback(this._rowIndex++, row, rowLength);
if (this._rowIndex === rowLength) {
this._swapCallbackAndInvoke(null, { rowLength: rowLength, meta: meta, flags: flags }, header.bodyLength);
}
}
/**
* Marks the current operation as timed out.
* @param {Error} err
* @param {Function} onResponse
* @private
*/
_markAsTimedOut(err, onResponse) {
if (this._state !== state.init) {
return;
}
this._state = state.timedOut;
this._swapCallbackAndInvoke(err, null, null, onResponse);
}
_markAsCompleted() {
if (this._state !== state.init) {
return;
}
if (this._timeout !== null) {
clearTimeout(this._timeout);
}
this._state = state.completed;
}
/**
* Sets the result of this operation, declaring that no further input will be processed for this operation.
* @param {Error} err
* @param {Object} [result]
* @param {Number} [length]
*/
setResult(err, result, length) {
this._markAsCompleted();
this._swapCallbackAndInvoke(err, result, length);
}
_swapCallbackAndInvoke(err, result, length, newCallback) {
const callback = this._callback;
this._callback = newCallback || utils.noop;
callback(err, result, length);
}
}
module.exports = OperationState;