diff --git a/.kokoro/presubmit/node14/system-test-multiplexed-session.cfg b/.kokoro/presubmit/node14/system-test-multiplexed-session.cfg index 8bf1160cd..aeeace4e8 100644 --- a/.kokoro/presubmit/node14/system-test-multiplexed-session.cfg +++ b/.kokoro/presubmit/node14/system-test-multiplexed-session.cfg @@ -12,6 +12,6 @@ env_vars: { } env_vars: { - key: "GOOGLE_CLOUD_SPANNER_ENABLE_MULTIPLEXED_SESSIONS" + key: "GOOGLE_CLOUD_SPANNER_MULTIPLEXED_SESSIONS" value: "true" } \ No newline at end of file diff --git a/.kokoro/system-test.sh b/.kokoro/system-test.sh index bb20a4220..a90d5cfec 100755 --- a/.kokoro/system-test.sh +++ b/.kokoro/system-test.sh @@ -45,12 +45,6 @@ if [[ $KOKORO_BUILD_ARTIFACTS_SUBDIR = *"continuous"* ]] || [[ $KOKORO_BUILD_ART trap cleanup EXIT HUP fi -# If tests are running with enabled multiplexed session, configure env -# GOOGLE_CLOUD_SPANNER_MULTIPLEXED_SESSIONS -if [[ $GOOGLE_CLOUD_SPANNER_ENABLE_MULTIPLEXED_SESSIONS = *"true"* ]]; then - export GOOGLE_CLOUD_SPANNER_MULTIPLEXED_SESSIONS=true -fi - npm run system-test # codecov combines coverage across integration and unit tests. Include diff --git a/.kokoro/trampoline_v2.sh b/.kokoro/trampoline_v2.sh index 5d6cfcca5..ce072d512 100755 --- a/.kokoro/trampoline_v2.sh +++ b/.kokoro/trampoline_v2.sh @@ -154,17 +154,17 @@ if [[ -n "${KOKORO_BUILD_ID:-}" ]]; then gcloud auth configure-docker --quiet fi pass_down_envvars+=( - # KOKORO dynamic variables. - "KOKORO_BUILD_NUMBER" - "KOKORO_BUILD_ID" - "KOKORO_JOB_NAME" - "KOKORO_GIT_COMMIT" - "KOKORO_GITHUB_COMMIT" - "KOKORO_GITHUB_PULL_REQUEST_NUMBER" - "KOKORO_GITHUB_PULL_REQUEST_COMMIT" - # For flakybot - "KOKORO_GITHUB_COMMIT_URL" - "KOKORO_GITHUB_PULL_REQUEST_URL" + # KOKORO dynamic variables. + "KOKORO_BUILD_NUMBER" + "KOKORO_BUILD_ID" + "KOKORO_JOB_NAME" + "KOKORO_GIT_COMMIT" + "KOKORO_GITHUB_COMMIT" + "KOKORO_GITHUB_PULL_REQUEST_NUMBER" + "KOKORO_GITHUB_PULL_REQUEST_COMMIT" + # For flakybot + "KOKORO_GITHUB_COMMIT_URL" + "KOKORO_GITHUB_PULL_REQUEST_URL" ) elif [[ "${TRAVIS:-}" == "true" ]]; then RUNNING_IN_CI="true" diff --git a/.trampolinerc b/.trampolinerc index 5fc225313..30d86075a 100644 --- a/.trampolinerc +++ b/.trampolinerc @@ -22,6 +22,7 @@ required_envvars+=( pass_down_envvars+=( "AUTORELEASE_PR" "VERSION" + "GOOGLE_CLOUD_SPANNER_MULTIPLEXED_SESSIONS" ) # Prevent unintentional override on the default image. diff --git a/benchmark/benchmarking-multiplexed-session.js b/benchmark/benchmarking-multiplexed-session.js new file mode 100644 index 000000000..9c393c1eb --- /dev/null +++ b/benchmark/benchmarking-multiplexed-session.js @@ -0,0 +1,119 @@ +/*! + * Copyright 2025 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. + */ + +'use strict'; + +const muxEnabledTrue = []; +const muxEnabledFalse = []; +const transaction_times = []; +async function main(instanceId, databaseId, projectId, methodName) { + async function readQuery(database) { + const startTime = Date.now(); + const query = { + sql: 'SELECT * FROM Singers', + }; + await database.run(query); + const operationTime = Date.now() - startTime; + transaction_times.push(operationTime); + } + + async function multiplexedSession(instanceId, databaseId, projectId) { + process.env.GOOGLE_CLOUD_SPANNER_MULTIPLEXED_SESSIONS = 'true'; + + const startTime = Date.now(); + // eslint-disable-next-line + const {Spanner} = require('../build/src/index.js'); + const spanner = new Spanner({ + projectId, + }); + + // Gets a reference to a Cloud Spanner instance and database + const instance = spanner.instance(instanceId); + const database = instance.database(databaseId); + + for (let i = 0; i < 1; i++) { + await readQuery(database); + } + + muxEnabledTrue.push(Date.now() - startTime); + } + + async function regularSession(instanceId, databaseId, projectId) { + process.env.GOOGLE_CLOUD_SPANNER_MULTIPLEXED_SESSIONS = 'false'; + + const startTime = Date.now(); + // eslint-disable-next-line + const {Spanner} = require('../build/src/index.js'); + const spanner = new Spanner({ + projectId, + }); + + // Gets a reference to a Cloud Spanner instance and database + const instance = spanner.instance(instanceId); + const database = instance.database(databaseId); + + for (let i = 0; i < 1; i++) { + await readQuery(database); + } + + muxEnabledFalse.push(Date.now() - startTime); + } + + function calculatePercentiles(latencies) { + // Step 1: Sort the array + const sortedLatencies = latencies.slice().sort((a, b) => a - b); + + // Step 2: Calculate p50 (50th percentile) + const p50Index = Math.floor(0.5 * sortedLatencies.length); + const p50Latency = sortedLatencies[p50Index]; + + // Step 3: Calculate p90 (90th percentile) + const p90Index = Math.floor(0.9 * sortedLatencies.length); + const p90Latency = sortedLatencies[p90Index]; + // Step 3: Calculate p99 (99th percentile) + const p99Index = Math.floor(0.99 * sortedLatencies.length); + const p99Latency = sortedLatencies[p99Index]; + + return { + p50: p50Latency, + p90: p90Latency, + p99: p99Latency, + }; + } + + async function runSequentially(method) { + for (let i = 0; i < 1; i++) { + // change function and options as per requirement + await method(instanceId, databaseId, projectId); + } + } + + let method = multiplexedSession; + if (methodName === 'regularSession') { + method = regularSession; + } + await runSequentially(method); + const percentiles = calculatePercentiles(transaction_times); + console.log(`p50 Latency: ${percentiles.p50}`); + console.log(`p90 Latency: ${percentiles.p90}`); + console.log(`p99 Latency: ${percentiles.p99}`); +} + +process.on('unhandledRejection', err => { + console.error(err.message); + process.exitCode = 1; +}); +main(...process.argv.slice(2)); diff --git a/src/session-factory.ts b/src/session-factory.ts index 1559176bc..138b4f66d 100644 --- a/src/session-factory.ts +++ b/src/session-factory.ts @@ -117,6 +117,8 @@ export class SessionFactory process.env.GOOGLE_CLOUD_SPANNER_MULTIPLEXED_SESSIONS === 'true' ? (this.isMultiplexed = true) : (this.isMultiplexed = false); + + console.log('mux enabled?: ', this.isMultiplexed); // Multiplexed sessions should only be created if its enabled. if (this.isMultiplexed) { this.multiplexedSession_.on('error', this.emit.bind(database, 'error'));