@google-cloud/spanner
Version:
Cloud Spanner Client Library for Node.js
301 lines • 10.9 kB
JavaScript
"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