UNPKG

@google-cloud/spanner

Version:
301 lines 10.9 kB
"use strict"; /*! * Copyright 2018 Google Inc. All Rights Reserved. * * 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. */ Object.defineProperty(exports, "__esModule", { value: true }); exports.BatchTransaction = void 0; const precise_date_1 = require("@google-cloud/precise-date"); const promisify_1 = require("@google-cloud/promisify"); const extend = require("extend"); const is = require("is"); const transaction_1 = require("./transaction"); const common_1 = require("../src/common"); const instrument_1 = require("./instrument"); const request_id_header_1 = require("./request_id_header"); /** * Use a BatchTransaction object to create partitions and read/query against * your Cloud Spanner database. * * @class * @extends Snapshot * * @param {TimestampBounds} [options] [Timestamp Bounds](https://cloud.google.com/spanner/docs/timestamp-bounds). */ class BatchTransaction extends transaction_1.Snapshot { /** * Closes all open resources. * * When the transaction is no longer needed, you should call this method to * free up resources allocated by the Batch client. * * Calling this method would render the transaction unusable everywhere. In * particular if this transaction object was being used across multiple * machines, calling this method on any of the machine would make the * transaction unusable on all the machines. This should only be called when * the transaction is no longer needed anywhere * * @param {BasicCallback} [callback] Callback function. * @returns {Promise<BasicResponse>} * * @example * ``` * const {Spanner} = require('@google-cloud/spanner'); * const spanner = new Spanner(); * * const instance = spanner.instance('my-instance'); * const database = instance.database('my-database'); * * database.createBatchTransaction(function(err, transaction) { * if (err) { * // Error handling omitted. * } * * transaction.close(function(err, apiResponse) {}); * }); * * //- * // If the callback is omitted, we'll return a Promise. * //- * database.createBatchTransaction().then(function(data) { * const transaction = data[0]; * return transaction.close(); * }); * ``` */ close(callback) { this.end(); if (callback) { callback(); } } createQueryPartitions(query, cb) { const request = typeof query === 'string' ? { sql: query } : query; const reqOpts = Object.assign({}, request, transaction_1.Snapshot.encodeParams(request)); delete reqOpts.gaxOptions; delete reqOpts.types; const traceConfig = { sql: request.sql, opts: this._observabilityOptions, dbName: this.getDBName(), }; return (0, instrument_1.startTrace)('BatchTransaction.createQueryPartitions', traceConfig, span => { const headers = {}; if (this._getSpanner().routeToLeaderEnabled) { (0, common_1.addLeaderAwareRoutingHeader)(headers); } this.createPartitions_({ client: 'SpannerClient', method: 'partitionQuery', reqOpts, gaxOpts: request.gaxOptions, headers: (0, request_id_header_1.injectRequestIDIntoHeaders)(headers, this.session), }, (err, partitions, resp) => { if (err) { (0, instrument_1.setSpanError)(span, err); } span.end(); cb(err, partitions, resp); }); }); } getDBName() { return this.session.parent.formattedName_; } /** * Generic create partition method. Handles common parameters used in both * {@link BatchTransaction#createQueryPartitions} and {@link * BatchTransaction#createReadPartitions} * * @private * * @param {object} config The request config. * @param {function} callback Callback function. */ createPartitions_(config, callback) { const traceConfig = { opts: this._observabilityOptions, dbName: this.getDBName(), }; return (0, instrument_1.startTrace)('BatchTransaction.createPartitions_', traceConfig, span => { const query = extend({}, config.reqOpts, { session: this.session.formattedName_, transaction: { id: this.id }, }); config.reqOpts = extend({}, query); const headers = { [common_1.CLOUD_RESOURCE_HEADER]: this.session.parent .formattedName_, }; config.headers = (0, request_id_header_1.injectRequestIDIntoHeaders)(headers, this.session); delete query.partitionOptions; this.session.request(config, (err, resp) => { if (err) { (0, instrument_1.setSpanError)(span, err); span.end(); callback(err, null, resp); return; } const partitions = resp.partitions.map(partition => { return extend({}, query, partition); }); if (resp.transaction) { const { id, readTimestamp } = resp.transaction; this.id = id; if (readTimestamp) { this.readTimestampProto = readTimestamp; this.readTimestamp = new precise_date_1.PreciseDate(readTimestamp); } } span.end(); callback(null, partitions, resp); }); }); } createReadPartitions(options, cb) { const traceConfig = { opts: this._observabilityOptions, dbName: this.getDBName(), }; return (0, instrument_1.startTrace)('BatchTransaction.createReadPartitions', traceConfig, span => { const reqOpts = Object.assign({}, options, { keySet: transaction_1.Snapshot.encodeKeySet(options), }); delete reqOpts.gaxOptions; delete reqOpts.keys; delete reqOpts.ranges; const headers = {}; if (this._getSpanner().routeToLeaderEnabled) { (0, common_1.addLeaderAwareRoutingHeader)(headers); } this.createPartitions_({ client: 'SpannerClient', method: 'partitionRead', reqOpts, gaxOpts: options.gaxOptions, headers: (0, request_id_header_1.injectRequestIDIntoHeaders)(headers, this.session), }, (err, partitions, resp) => { if (err) { (0, instrument_1.setSpanError)(span, err); } span.end(); cb(err, partitions, resp); }); }); } execute(partition, cb) { const isRead = typeof partition.table === 'string'; if (isRead) { this.read(partition.table, partition, cb); return; } this.run(partition, cb); } /** * Executes partition in streaming mode. * * @see {@link Transaction#createReadStream} when using {@link ReadPartition}. * @see {@link Transaction#runStream} when using {@link QueryPartition}. * * @param {ReadPartition|QueryPartition} partition The partition object. * @returns {ReadableStream} A readable stream that emits rows. * * @example * ``` * const {Spanner} = require('@google-cloud/spanner'); * const spanner = new Spanner(); * * const instance = spanner.instance('my-instance'); * const database = instance.database('my-database'); * * database.createBatchTransaction(function(err, transaction) { * if (err) { * // Error handling omitted. * } * * transaction.createReadPartitions(options, function(err, partitions) { * const partition = partitions[0]; * * transaction * .executeStream(partition) * .on('error', function(err) {}) * .on('data', function(row) { * // row = [ * // { * // name: 'SingerId', * // value: '1' * // }, * // { * // name: 'Name', * // value: 'Eddie Wilson' * // } * // ] * }) * .on('end', function() { * // All results retrieved * }); * }); * }); * ``` */ executeStream(partition) { // TODO: Instrument the streams with Otel. if (is.string(partition.table)) { return this.createReadStream(partition.table, partition); } return this.runStream(partition); } /** * @typedef {object} TransactionIdentifier * @property {string|Session} session The full session name. * @property {string} transaction The transaction ID. * @property {string|Date} readTimestamp The transaction read timestamp. */ /** * Creates a transaction identifier used to reference the transaction in * workers. * * @returns {TransactionIdentifier} * * @example * ``` * const {Spanner} = require('@google-cloud/spanner'); * const spanner = new Spanner(); * * const instance = spanner.instance('my-instance'); * const database = instance.database('my-database'); * * database.createBatchTransaction(function(err, transaction) { * const identifier = transaction.identifier(); * }); * ``` */ identifier() { return { transaction: this.id.toString('base64'), session: this.session.id, timestamp: this.readTimestampProto, }; } } exports.BatchTransaction = BatchTransaction; /*! Developer Documentation * * All async methods (except for streams) will return a Promise in the event * that a callback is omitted. */ (0, promisify_1.promisifyAll)(BatchTransaction, { exclude: ['identifier'], }); //# sourceMappingURL=batch-transaction.js.map