Skip to content

Bug Fix: infinite Loop#1783

Open
willianmarquess wants to merge 2 commits into
oracle:mainfrom
willianmarquess:main
Open

Bug Fix: infinite Loop#1783
willianmarquess wants to merge 2 commits into
oracle:mainfrom
willianmarquess:main

Conversation

@willianmarquess

@willianmarquess willianmarquess commented Jul 16, 2026

Copy link
Copy Markdown

Hi, there is an infinite loop bug that occurs in the bgThreadFunc() method:
I encountered this issue while analyzing a problem in a production environment where pods were freezing and restarting, this issue only occurs in services that handle high volumes of message processing via queues.

Mode: Thin

Problem:

When we configure a low number of connections in the pool (1, 4, 8, etc.) and the application experiences high concurrency (queue/event processing), it enters an infinite loop due to this part of code:
pool.js

async bgThreadFunc() {
    while (!this._poolCloseWaiter) {
      const numToCreate = this._getNumConnsToCreate(); //when here returns 0
      
      for (let i = 0; i < numToCreate; i++) {
        ...
      }

      // and here has this._pendingRequests.length > 0
      //then we have an infinite loop
      if ((this._pendingRequests.length == 0 || this._bgErr) &&
        this.getConnectionsOpen() >= this._poolMin) {
        await new Promise((resolve) => {
          this.bgWaiter = resolve;
        });
        this.bgWaiter = null;
      }
      ...
    }
  }

How to reproduce:

This scenario only occurs in situations of high concurrency; I’ll leave an example here for you to reproduce the problem.
(this code is only trying to simulate an queue/event concurrent processing)

'use strict';
const oracledb = require('oracledb');
const EventEmitter = require('events');
oracledb.outFormat = oracledb.OUT_FORMAT_OBJECT;
const {
    DB_USER = 'your_user',
    DB_PASSWORD = 'your_pass',
    DB_CONNECT_STRING = 'your_connection_string',
} = process.env;
class Broker extends EventEmitter { }
let pool;
async function createTableIfNotExists(connection) {
  const plsql = `
    BEGIN
      EXECUTE IMMEDIATE '
        CREATE TABLE accounts (
          id            NUMBER PRIMARY KEY,
          balance       NUMBER NOT NULL,
          updated_at    TIMESTAMP DEFAULT SYSTIMESTAMP,
          version       NUMBER DEFAULT 0
        )
      ';
    EXCEPTION
      WHEN OTHERS THEN
        IF SQLCODE != -955 THEN
          RAISE;
        END IF;
    END;
  `;
  await connection.execute(plsql);
}

async function processMessage(id) {
    let connection;
    try {
        connection = await pool.getConnection();
        await connection.execute(
            `   UPDATE accounts
                SET balance = balance + :delta,
                    version = version + 1
                WHERE id = :id
                `,
            { delta: 10, id: 1 }
        );
    } catch (err) {
        console.error(`[${id}] error:`, err.message);
    } finally {
        if (connection) {
            try {
                await connection.close();
                console.log(`[${id}] connection released`);
            } catch (error) {
                console.error(`[${id}] error when trying to close connection:`, error.message);
            }
        }
    }
}

async function main() {
    const broker = new Broker();
    broker.on('message', (id) => {
        processMessage(id);
    });
    pool = await oracledb.createPool({
        user: DB_USER,
        password: DB_PASSWORD,
        connectString: DB_CONNECT_STRING,
        poolMin: 4,
        poolMax: 4,
        queueMax: 100000,
        poolIncrement: 1,
        enableStatistics: true,
    });
    await createTableIfNotExists(await pool.getConnection());
    setInterval(() => {
        console.log(JSON.stringify(pool.getStatistics()));
    }, 1000);
    setInterval(() => {
        for (let j = 0; j < 5; j++) {
            broker.emit('message', j);
        }
    }, 2);
}
main().catch(console.error);

Solution:

I left a suggested three-part solution in the code:

1 - In bgThreadFunc, check only for cases where numToCreate <= 0 to perform the await and avoid entering sync infinite loop.
2 - When calling release(), check for any pending tasks and attempt to reuse and resolve them immediately.
3 - When there is a pending task, we must wake up bgWaiter().

@oracle-contributor-agreement

Copy link
Copy Markdown

Thank you for your pull request and welcome to our community! To contribute, please sign the Oracle Contributor Agreement (OCA).
The following contributors of this PR have not signed the OCA:

To sign the OCA, please create an Oracle account and sign the OCA in Oracle's Contributor Agreement Application.

When signing the OCA, please provide your GitHub username. After signing the OCA and getting an OCA approval from Oracle, this PR will be automatically updated.

If you are an Oracle employee, please make sure that you are a member of the main Oracle GitHub organization, and your membership in this organization is public.

@oracle-contributor-agreement oracle-contributor-agreement Bot added the OCA Required At least one contributor does not have an approved Oracle Contributor Agreement. label Jul 16, 2026
@sharadraju

Copy link
Copy Markdown
Member

@willianmarquess Thank you for bringing this to our attention and the detailed analysis.

Please note that we will evaluate the PR and then include the code changes from our end in an upcoming release (while giving you the due credit in the release notes :) ), based on our analysis and testing.

We would also request you to sign the OCA agreement.

To sign the OCA, please create an Oracle account and sign the OCA in Oracle's Contributor Agreement Application

@willianmarquess

willianmarquess commented Jul 17, 2026

Copy link
Copy Markdown
Author

@willianmarquess Thank you for bringing this to our attention and the detailed analysis.

Please note that we will evaluate the PR and then include the code changes from our end in an upcoming release (while giving you the due credit in the release notes :) ), based on our analysis and testing.

We would also request you to sign the OCA agreement.

To sign the OCA, please create an Oracle account and sign the OCA in Oracle's Contributor Agreement Application

I am trying to sign, but I am encountering an error when trying to download the OCA document (I am from Brazil).

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

OCA Required At least one contributor does not have an approved Oracle Contributor Agreement.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants