UNPKG

cassandra-driver

Version:

DataStax Node.js Driver for Apache Cassandra

349 lines (338 loc) 12.6 kB
/* * 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 utils = require('../utils'); const VersionNumber = require('./version-number'); const v200 = VersionNumber.parse('2.0.0'); const v210 = VersionNumber.parse('2.1.0'); const v220 = VersionNumber.parse('2.2.0'); const v300 = VersionNumber.parse('3.0.0'); const v510 = VersionNumber.parse('5.1.0'); const v600 = VersionNumber.parse('6.0.0'); /** * Contains information for the different protocol versions supported by the driver. * @type {Object} * @property {Number} v1 Cassandra protocol v1, supported in Apache Cassandra 1.2-->2.2. * @property {Number} v2 Cassandra protocol v2, supported in Apache Cassandra 2.0-->2.2. * @property {Number} v3 Cassandra protocol v3, supported in Apache Cassandra 2.1-->3.x. * @property {Number} v4 Cassandra protocol v4, supported in Apache Cassandra 2.2-->3.x. * @property {Number} v5 Cassandra protocol v5, in beta from Apache Cassandra 3.x+. Currently not supported by the * driver. * @property {Number} dseV1 DataStax Enterprise protocol v1, DSE 5.1+ * @property {Number} dseV2 DataStax Enterprise protocol v2, DSE 6.0+ * @property {Number} maxSupported Returns the higher protocol version that is supported by this driver. * @property {Number} minSupported Returns the lower protocol version that is supported by this driver. * @property {Function} isSupported A function that returns a boolean determining whether a given protocol version * is supported. * @alias module:types~protocolVersion */ const protocolVersion = { // Strict equality operators to compare versions are allowed, other comparison operators are discouraged. Instead, // use a function that checks if a functionality is present on a certain version, for maintainability purposes. v1: 0x01, v2: 0x02, v3: 0x03, v4: 0x04, v5: 0x05, v6: 0x06, dseV1: 0x41, dseV2: 0x42, maxSupported: 0x42, minSupported: 0x01, /** * Determines whether the protocol version is a DSE-specific protocol version. * @param {Number} version * @returns {Boolean} * @ignore */ isDse: function(version) { return ((version >= this.dseV1 && version <= this.dseV2)); }, /** * Returns true if the protocol version represents a version of Cassandra * supported by this driver, false otherwise * @param {Number} version * @returns {Boolean} * @ignore */ isSupportedCassandra: function(version) { return (version <= 0x04 && version >= 0x01); }, /** * Determines whether the protocol version is supported by this driver. * @param {Number} version * @returns {Boolean} * @ignore */ isSupported: function (version) { return (this.isDse(version) || this.isSupportedCassandra(version)); }, /** * Determines whether the protocol includes flags for PREPARE messages. * @param {Number} version * @returns {Boolean} * @ignore */ supportsPrepareFlags: function (version) { return (version === this.dseV2); }, /** * Determines whether the protocol supports sending the keyspace as part of PREPARE, QUERY, EXECUTE, and BATCH. * @param {Number} version * @returns {Boolean} * @ignore */ supportsKeyspaceInRequest: function (version) { return (version === this.dseV2); }, /** * Determines whether the protocol supports result_metadata_id on `prepared` response and * and `execute` request. * @param {Number} version * @returns {Boolean} * @ignore */ supportsResultMetadataId: function (version) { return (version === this.dseV2); }, /** * Determines whether the protocol supports partition key indexes in the `prepared` RESULT responses. * @param {Number} version * @returns {Boolean} * @ignore */ supportsPreparedPartitionKey: function (version) { return (version >= this.v4); }, /** * Determines whether the protocol supports up to 4 strings (ie: change_type, target, keyspace and table) in the * schema change responses. * @param version * @return {boolean} * @ignore */ supportsSchemaChangeFullMetadata: function (version) { return (version >= this.v3); }, /** * Determines whether the protocol supports continuous paging. * @param version * @return {boolean} * @ignore */ supportsContinuousPaging: function (version) { return (this.isDse(version)); }, /** * Determines whether the protocol supports paging state and serial consistency parameters in QUERY and EXECUTE * requests. * @param version * @return {boolean} * @ignore */ supportsPaging: function (version) { return (version >= this.v2); }, /** * Determines whether the protocol supports timestamps parameters in BATCH, QUERY and EXECUTE requests. * @param {Number} version * @return {boolean} * @ignore */ supportsTimestamp: function (version) { return (version >= this.v3); }, /** * Determines whether the protocol supports named parameters in QUERY and EXECUTE requests. * @param {Number} version * @return {boolean} * @ignore */ supportsNamedParameters: function (version) { return (version >= this.v3); }, /** * Determines whether the protocol supports unset parameters. * @param {Number} version * @return {boolean} * @ignore */ supportsUnset: function (version) { return (version >= this.v4); }, /** * Determines whether the protocol provides a reason map for read and write failure errors. * @param version * @return {boolean} * @ignore */ supportsFailureReasonMap: function (version) { return (version >= this.v5); }, /** * Determines whether the protocol supports timestamp and serial consistency parameters in BATCH requests. * @param {Number} version * @return {boolean} * @ignore */ uses2BytesStreamIds: function (version) { return (version >= this.v3); }, /** * Determines whether the collection length is encoded using 32 bits. * @param {Number} version * @return {boolean} * @ignore */ uses4BytesCollectionLength: function (version) { return (version >= this.v3); }, /** * Determines whether the QUERY, EXECUTE and BATCH flags are encoded using 32 bits. * @param {Number} version * @return {boolean} * @ignore */ uses4BytesQueryFlags: function (version) { return (this.isDse(version)); }, /** * Startup responses using protocol v4+ can be a SERVER_ERROR wrapping a ProtocolException, this method returns true * when is possible to receive such error. * @param {Number} version * @return {boolean} * @ignore */ canStartupResponseErrorBeWrapped: function (version) { return (version >= this.v4); }, /** * Gets the first version number that is supported, lower than the one provided. * Returns zero when there isn't a lower supported version. * @param {Number} version * @return {Number} * @ignore */ getLowerSupported: function (version) { if (version >= this.v5) { return this.v4; } if (version <= this.v1) { return 0; } return version - 1; }, /** * Computes the highest supported protocol version collectively by the given hosts. * * Considers the cassandra_version of the input hosts to determine what protocol versions * are supported and uses the highest common protocol version among them. * * If hosts >= C* 3.0 are detected, any hosts older than C* 2.1 will not be considered * as those cannot be connected to. In general this will not be a problem as C* does * not support clusters with nodes that have versions that are more than one major * version away from each other. * @param {Connection} connection Connection hosts were discovered from. * @param {Array.<Host>} hosts The hosts to determine highest protocol version from. * @return {Number} Highest supported protocol version among hosts. */ getHighestCommon: function(connection, hosts) { const log = connection.log ? connection.log.bind(connection) : utils.noop; let maxVersion = connection.protocolVersion; // whether or not protocol v3 is required (nodes detected that don't support < 3). let v3Requirement = false; // track the common protocol version >= v3 in case we encounter older versions. let maxVersionWith3OrMore = maxVersion; hosts.forEach(h => { let dseVersion = null; if (h.dseVersion) { // As of DSE 5.1, DSE has it's own specific protocol versions. If we detect 5.1+ // consider those protocol versions. dseVersion = VersionNumber.parse(h.dseVersion); log('verbose', `Encountered host ${h.address} with dse version ${dseVersion}`); if (dseVersion.compare(v510) >= 0) { v3Requirement = true; if (dseVersion.compare(v600) >= 0) { maxVersion = Math.min(this.dseV2, maxVersion); } else { maxVersion = Math.min(this.dseV1, maxVersion); } maxVersionWith3OrMore = maxVersion; return; } // If DSE < 5.1, we fall back on the cassandra protocol logic. } if (!h.cassandraVersion || h.cassandraVersion.length === 0) { log('warning', 'Encountered host ' + h.address + ' with no cassandra version,' + ' skipping as part of protocol version evaluation'); return; } try { const cassandraVersion = VersionNumber.parse(h.cassandraVersion); if (!dseVersion) { log('verbose', 'Encountered host ' + h.address + ' with cassandra version ' + cassandraVersion); } if (cassandraVersion.compare(v300) >= 0) { // Anything 3.0.0+ has a max protocol version of V4 and requires at least V3. v3Requirement = true; maxVersion = Math.min(this.v4, maxVersion); maxVersionWith3OrMore = maxVersion; } else if (cassandraVersion.compare(v220) >= 0) { // Cassandra 2.2.x has a max protocol version of V4. maxVersion = Math.min(this.v4, maxVersion); maxVersionWith3OrMore = maxVersion; } else if (cassandraVersion.compare(v210) >= 0) { // Cassandra 2.1.x has a max protocol version of V3. maxVersion = Math.min(this.v3, maxVersion); maxVersionWith3OrMore = maxVersion; } else if (cassandraVersion.compare(v200) >= 0) { // Cassandra 2.0.x has a max protocol version of V2. maxVersion = Math.min(this.v2, maxVersion); } else { // Anything else is < 2.x and requires protocol version V1. maxVersion = this.v1; } } catch (e) { log('warning', 'Encountered host ' + h.address + ' with unparseable cassandra version ' + h.cassandraVersion + ' skipping as part of protocol version evaluation'); } }); if (v3Requirement && maxVersion < this.v3) { const addendum = '. This should not be possible as nodes within a cluster can\'t be separated by more than one major version'; if (maxVersionWith3OrMore < this.v3) { log('error', 'Detected hosts that require at least protocol version 0x3, but currently connected to ' + connection.address + ':' + connection.port + ' using protocol version 0x' + maxVersionWith3OrMore + '. Will not be able to connect to these hosts' + addendum); } else { log('error', 'Detected hosts with maximum protocol version of 0x' + maxVersion.toString(16) + ' but there are some hosts that require at least version 0x3. Will not be able to connect to these older hosts' + addendum); } maxVersion = maxVersionWith3OrMore; } log('verbose', 'Resolved protocol version 0x' + maxVersion.toString(16) + ' as the highest common protocol version among hosts'); return maxVersion; }, /** * Determines if the protocol is a BETA version of the protocol. * @param {Number} version * @return {Number} */ isBeta: function (version) { return version === this.v5; } }; module.exports = protocolVersion;