'Migrating sequelize-imbi to new sequelize: unable to establish database connection
I am tasked with migrating the legacy package sequelize-ibmi to the latest sequelize release as the side branch has been merged into the main lib.
PR can be found here: https://github.com/sequelize/sequelize/pull/14009
I am running into the following problem:
error: Unable to connect to the database: [odbc] Error connecting to the database
More specifically, the error object thrown in the sequelize connection-manager.connect:
{
"state": "08004",
"code": 10061,
"message": "[IBM][System i Access ODBC Driver]Communication link failure. comm rc=10061 - CWBCO1049 - The IBM i server application is not started, or the connection was blocked by a firewall"
}
My findings:
As this entails migrating a legacy package using sequelize-ibmi, I debugged the sequelize-ibmi/lib/dialects/imbi/connection-manager.js (for the legacy project), comparing it to sequelize/lib/dialects/imbi/connection-manager.js (for the 'new' project).
The sequelize-imbi package:
connect(config) {
return new Promise((resolve, reject) => {
try {
const connection = this.lib.connect(`${config.odbcConnectionString}`);
debug('Connection successful.');
resolve(connection);
} catch (err) {
debug('Failed to connect: Error in "odbc" package in "new Connection"');
const error = err.toString();
if (error.includes('IM002')) {
throw new SequelizeErrors.ConnectionRefusedError(error);
}
}
});
}
The sequelize package:
async connect(config) {
const connectionKeywords = [];
if (config.dialectOptions && config.dialectOptions.odbcConnectionString) {
connectionKeywords.push(config.dialectOptions.odbcConnectionString);
}
if (config.database) {
connectionKeywords.push(`DSN=${config.database}`);
}
if (config.username) {
connectionKeywords.push(`UID=${config.username}`);
}
if (config.password) {
connectionKeywords.push(`PWD=${config.password}`);
}
if (config.host) {
connectionKeywords.push(`SYSTEM=${config.host}`);
}
const connectionString = connectionKeywords.join(";");
if (connectionString.charAt(connectionString.length - 1) !== ";") {
connectionString.concat(";");
}
let connection;
try {
connection = await this.lib.connect(connectionString);
} catch (error) {
if (error.toString().includes("Error connecting to the database")) {
const err = new SequelizeErrors.ConnectionRefusedError(error);
throw err;
}
}
return connection;
}
In essence, they both boil down to:
await this.lib.connect(connectionString);
The legacy implementation only used the raw connectionString (which includes db details, see below), while the new implementation uses config parameters being passed which are used to create the connectionString.
I debugged and made sure both resulted in the same connectionString.
In both cases the connectionString boils down to:
DSN=****;CurrentSchema=****;Persist Security Info=True;UID=****;PWD=****;DATABASE=****;ccsid=1208
I am unsure why certain parameters are added (Persistent Securiy Info, ccsid e.g.), but this is the currently used & working connectionString in production. (obviously censored)
However, I still receive the error stated above. The exact same config, same dependencies (odbc 4.2). I followed the provided documentation (https://github.com/sequelize/sequelize/blob/main/docs/manual/other-topics/dialect-specific-things.md#ibm-i).
The working code (legacy, sequelize-ibmi package):
const sequelize = new Sequelize({
dialect: 'ibmi',
dialectModule: require('odbc'),
odbcConnectionString: odbcConnectionString,
dialectOptions: {
multipleStatements: true,
},
multipleStatements: true,
logging: (msg) => Logger.debug(msg),
isolationLevel: Transaction.ISOLATION_LEVELS.READ_COMMITTED,
pool: {
max: 100,
min: 0,
idle: 200000,
// @note https://github.com/sequelize/sequelize/issues/8133#issuecomment-359993057
acquire: 1000 * 200,
},
});
Not working code, sequelize package:
const sequelize = new Sequelize(dbConfig.dbDatabaseName, dbConfig.dbUsername, dbConfig.dbPassword, {
dialect: 'ibmi' as any, // have to `as any` here, imbi addition isn't fully typed in @Sequelize/core yet.
dialectOptions: {
odbcConnectionString: dbConfig.odbcConnectionString,
},
logging: (...msg: any) => Logger.info(msg),
});
Attempt to port over legacy settings..
const sequelize = new Sequelize({
dialect: 'ibmi' as any, // have to `as any` here, imbi addition isn't fully typed in @Sequelize/core yet.
dialectModule: require('odbc'),
dialectOptions: {
multipleStatements: true,
odbcConnectionString: odbcConnectionString,
},
isolationLevel: Transaction.ISOLATION_LEVELS.READ_COMMITTED,
pool: {
max: 100,
min: 0,
idle: 200000,
// @note https://github.com/sequelize/sequelize/issues/8133#issuecomment-359993057
acquire: 1000 * 200,
},
logging: (...msg: any) => Logger.info(msg),
});
Is there something I am overlooking? The database I am trying to connect to is a remote one behind a firewall. However, the connection is established in the legacy package without any issues (working on a company vpn, so firewall shouldn't be an issue).
Solution 1:[1]
I managed to identify and resolve my issues. I had two things wrong:
- Wrong Database Name variable. DSN needs to be used, not DATABASE. I had these mixed up.
- The new Sequelize uses Hostname but is not documented. This one was important in my case.
The working code:
const sequelize = new Sequelize(dbConfig.dbDatabaseName, dbConfig.dbUsername, dbConfig.dbPassword, {
dialect: 'ibmi' as any, // have to `as any` here, imbi addition isn't fully typed in @Sequelize/core yet.
dialectOptions: {
odbcConnectionString: dbConfig.odbcConnectionString,
},
host: dbConfig.dbHost,
logging: (...msg: any) => Logger.info(msg),
});
Sources
This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.
Source: Stack Overflow
| Solution | Source |
|---|---|
| Solution 1 | Rhino |
