googleapis / nodejs-bigquery

Node.js client for Google Cloud BigQuery: A fast, economical and fully-managed enterprise data warehouse for large-scale data analytics.

Home Page:https://cloud.google.com/bigquery/

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

"Concurrent jobs in the same session are not allowed" error in transaction in one session

amoywolf opened this issue · comments

import { BigQuery } from '@google-cloud/bigquery';

const tableName = 'modi_images.test';

export default class BigQuerySession {
  bigqueryClient: BigQuery;

  sessionId: string;

  constructor() {
    this.bigqueryClient = new BigQuery();
    this.sessionId = '';
  }

  async openSession() {
    const query = 'SELECT 1;';
    const options = {
      query,
      createSession: true,
    };

    const [job] = await this.bigqueryClient.createQueryJob(options);
    await job.getQueryResults(); // Wait for job completion
    this.sessionId = job.metadata.statistics.sessionInfo.sessionId;
    return this.sessionId;
  }

  async closeSession() {
    if (this.sessionId) {
      const query = 'CALL BQ.ABORT_SESSION();';
      const options = {
        query,
        createSession: false,
        connectionProperties: [
          {
            key: 'session_id',
            value: this.sessionId,
          },
        ],
      };
      const [job] = await this.bigqueryClient.createQueryJob(options);
      await job.getQueryResults();
    }
  }

  async executeQuery(query: string) {
    const options = {
      query,
      createSession: false,
      connectionProperties: [
        {
          key: 'session_id',
          value: this.sessionId,
        },
      ],
    };
    const [job] = await this.bigqueryClient.createQueryJob(options);
    await job.getQueryResults();
  }

  async runBigQueryTransaction(scripts: string[]) {
    if (!this.sessionId) {
      await this.openSession();
    }

    try {
      const beginTransactionQuery = 'BEGIN TRANSACTION;';
      await this.executeQuery(beginTransactionQuery);
      scripts.forEach(async (queryScript) => {
        await this.executeQuery(queryScript);
      });
      const commitTransactionQuery = 'COMMIT TRANSACTION;';
      await this.executeQuery(commitTransactionQuery);
    } catch (error) {
      console.error('Error:', error);
    } finally {
      await this.closeSession();
    }
  }
}

const scripts = [
  `INSERT INTO ${tableName} VALUES (3, 'Alice', 35);`,
];
const session = new BigQuerySession();
session.runBigQueryTransaction(scripts);

  • Any error messages you're getting
ApiError: Resources exceeded during query execution: Another job abc:US.1cefe6bf-adb3-48bf-983c-c8415f627fb4 is currently running in the session Ch4KHG1vZGlmYWNlLWNhbleHeWxpZ2h0LXNhbmRib3gQARokOTRjZjUyYmItHeOemS00M2Y4LTllYWUtNzE0NWQyM2UzMDFk. Concurrent jobs in the same session are not allowed.
    at new ApiError (/Users/tommywang/modi-images/api/node_modules/@google-cloud/common/build/src/util.js:75:15)
    at Util.parseHttpRespBody (/Users/tommywang/modi-images/api/node_modules/@google-cloud/common/build/src/util.js:210:38)
    at Util.handleResp (/Users/tommywang/modi-images/api/node_modules/@google-cloud/common/build/src/util.js:151:117)
    at /Users/tommywang/modi-images/api/node_modules/@google-cloud/common/build/src/util.js:534:22
    at onResponse (/Users/tommywang/modi-images/api/node_modules/retry-request/index.js:240:7)
    at /Users/tommywang/modi-images/api/node_modules/teeny-request/src/index.ts:333:11
    at processTicksAndRejections (node:internal/process/task_queues:96:5) {
  code: 400,
  errors: [
    {
      message: 'Resources exceeded during query execution: Another job modiface-candlelight-sandbox:US.1cefe6bf-adb3-48bf-983c-c8915f625fb4 is currently running in the session Ch4KHG1vZGlmYWNlLWNhbmRsZWxpZ2h0LXNhbmRib3gQARokOTRjZjUyYmItZmJkYS00M2Y4LTllYWUtNzE0NWQyM2UzMDFk. Concurrent jobs in the same session are not allowed.',
      domain: 'global',
      reason: 'resourcesExceeded'
    }
  ],

Solved. The root cause is following forEach doesn't actually await.

      scripts.forEach(async (queryScript) => {
        await this.executeQuery(queryScript);
      });

By changing to following, it works.

      for (const queryScript of scripts) {
        await this.executeQuery(queryScript);
      }