UNPKG

@google-cloud/spanner

Version:
200 lines 8.08 kB
"use strict"; /*! * Copyright 2024 Google LLC. 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.MultiplexedSession = exports.MUX_SESSION_CREATE_ERROR = exports.MUX_SESSION_AVAILABLE = void 0; const events_1 = require("events"); const instrument_1 = require("./instrument"); exports.MUX_SESSION_AVAILABLE = 'mux-session-available'; exports.MUX_SESSION_CREATE_ERROR = 'mux-session-create-error'; /** * Class used to manage connections to Spanner using multiplexed session. * * **You don't need to use this class directly, connections will be handled for * you.** * * @class * @extends {EventEmitter} */ class MultiplexedSession extends events_1.EventEmitter { database; // frequency to create new mux session refreshRate; _multiplexedSession; _refreshHandle; _observabilityOptions; constructor(database) { super(); this.database = database; // default frequency is 7 days this.refreshRate = 7; this._multiplexedSession = null; this._observabilityOptions = database._observabilityOptions; } /** * Creates a new multiplexed session and manages its maintenance. * * This method initiates the session creation process by calling the `_createSession` method, which returns a Promise. */ createSession() { this._createSession() .then(() => { this._maintain(); }) .catch(err => { this.emit('error', err); }); } /** * Creates a new multiplexed session. * * This method sends a request to the database to create a new session with multiplexing enabled. * The response from the database would be an array, the first value of the array will be containing the multiplexed session. * * @returns {Promise<void>} A Promise that resolves when the session has been successfully created and assigned, an event * `mux-session-available` will be emitted to signal that the session is ready. * * In case of error, an error will get emitted along with the error event. * * @private */ async _createSession() { const traceConfig = { opts: this._observabilityOptions, dbName: this.database.formattedName_, }; return (0, instrument_1.startTrace)('MultiplexedSession.createSession', traceConfig, async (span) => { span.addEvent('Requesting a multiplexed session'); try { const [createSessionResponse] = await this.database.createSession({ multiplexed: true, }); this._multiplexedSession = createSessionResponse; span.addEvent(`Created multiplexed session ${this._multiplexedSession.id}`); this.emit(exports.MUX_SESSION_AVAILABLE); } catch (e) { (0, instrument_1.setSpanError)(span, e); this.emit(exports.MUX_SESSION_CREATE_ERROR, e); throw e; } finally { span.end(); } }); } /** * Maintains the multiplexed session by periodically refreshing it. * * This method sets up a periodic refresh interval for maintaining the session. The interval duration * is determined by the @param refreshRate option, which is provided in days. * The default value is 7 days. * * @throws {Error} If the multiplexed session creation fails in `_createSession`, the error is caught * and ignored. This is because the currently active multiplexed session has a 30-day expiry, providing * the maintainer with four opportunities (one every 7 days) to refresh the active session. * * @returns {void} This method does not return any value. * */ _maintain() { const refreshRate = this.refreshRate * 24 * 60 * 60000; this._refreshHandle = setInterval(async () => { try { await this._createSession(); } catch (err) { return; } }, refreshRate); this._refreshHandle.unref(); } /** * Retrieves a session asynchronously and invokes a callback with the session details. * * @param {GetSessionCallback} callback - The callback to be invoked once the session is acquired or an error occurs. * * @returns {void} This method does not return any value, as it operates asynchronously and relies on the callback. * */ getSession(callback) { this._acquire().then(session => callback(null, session, session?.txn), callback); } /** * Acquires a session asynchronously, and prepares the transaction for the session. * * Once a session is successfully acquired, it returns the session object (which may be `null` if unsuccessful). * * @returns {Promise<Session | null>} * A Promise that resolves with the acquired session (or `null` if no session is available after retries). * */ async _acquire() { const session = await this._getSession(); // Prepare a transaction for a session session.txn = session.transaction(session.parent.queryOptions_); return session; } /** * Attempts to get a session, waiting for it to become available if necessary. * * Waits for the `MUX_SESSION_AVAILABLE` event or for the `MUX_SESSION_CREATE_ERROR` * to be emitted if the multiplexed session is not yet available. The method listens * for these events, and once `mux-session-available` is emitted, it resolves and returns * the session. * * In case of an error, the promise will get rejected and the error will get bubble up to the parent method. * * @returns {Promise<Session | null>} A promise that resolves with the current multiplexed session if available, * or `null` if the session is not available. * * @private * */ async _getSession() { const span = (0, instrument_1.getActiveOrNoopSpan)(); // Check if the multiplexed session is already available if (this._multiplexedSession !== null) { span.addEvent('Cache hit: has usable multiplexed session'); return this._multiplexedSession; } // Define event and promises to wait for the session to become available or for the error span.addEvent('Waiting for a multiplexed session to become available'); let removeAvailableListener; let removeErrorListener; const promises = [ new Promise((_, reject) => { this.once(exports.MUX_SESSION_CREATE_ERROR, reject); removeErrorListener = this.removeListener.bind(this, exports.MUX_SESSION_CREATE_ERROR, reject); }), new Promise(resolve => { this.once(exports.MUX_SESSION_AVAILABLE, resolve); removeAvailableListener = this.removeListener.bind(this, exports.MUX_SESSION_AVAILABLE, resolve); }), ]; try { await Promise.race(promises); } finally { removeAvailableListener(); removeErrorListener(); } // Return the multiplexed session after it becomes available return this._multiplexedSession; } } exports.MultiplexedSession = MultiplexedSession; //# sourceMappingURL=multiplexed-session.js.map