Help with twin servers v7

Hello,

We have upgraded to forest v7 last week.
We are using twin servers, server B was online and forest was working fine.

We have upgraded server A, which is a copy of server B, and we have this error on our logs when A is online:

We have those errors on several models, and that’s always the property STRING that can’t be read.

We also have a 404 on forest/authentication

When launching server B, everything is fine.

Expected behavior

Be able to connect to forest admin.

Actual behavior

Unable to be connected to app.forestadmin.com

Context

  • Package Version: 7.11.2,
  • Express Version: 4.17.1,
  • Sequelize Version: 5.15.2,
  • Database Dialect: postgres,
  • Project Name: ZACK

Thanks for your help !

Hello @didouche,

Thanks for your feedback and the details you are sharing with us.

With the v7, when you are deploying multiple instances of the server, you need to generate a static client ID that will be used for authentication.

Can you please follow this part of the documentation: Upgrade to v7 - Documentation

You will need to:

  1. Generate a client ID from your FOREST_ENV_SECRET and APPLICATION_URL
  2. Set a new environment variable named FOREST_CLIENT_ID with this value

Please use the same value for all your instances.

Hello @GuillaumeGautreau and thanks for your answer !

Yes, that’s what we have done, and we did have used the same FOREST_CLIENT_ID for server A and B

Are you sure that both servers are identical? The errors that are showing up point out a difference between the sequelize models.

Can you activate the logs on your servers, in order to have more details about the error?

Can you paste here the code that you are using on server B to initialize the agent? It seems that the property STRING is coming from the property objectMapping.

Here is a one of the logs complete

Here is the initialization code from server B:

async function forestInitialization (app) {
    app.use(await Liana.init({
      envSecret: process.env.FOREST_ENV_SECRET,
      authSecret: process.env.FOREST_AUTH_SECRET,
      objectMapping: Sequelize,
      connections: { default: sequelize },
    }))
}
forestInitialization(app)

And so here is the api/services/sequelize file:

const Sequelize =	require ('sequelize')
const config =		require (`${process.env.PWD}/config/sequelize`)
const db =			{}
global.Model =		Sequelize.Model
global.Op =			Sequelize.Op

Sequelize.DataTypes.postgres.DECIMAL.parse = parseFloat

config.logging = config.logging && (message => {
	if (message.includes ('CREATE')) {
		console.info (message.green)
	} else if (message.includes ('DELETE')) {
		console.info (message.red)
	} else if (message.includes ('UPDATE')) {
		console.info (message.blue)
	} else if (message.includes ('INSERT')) {
		console.info (message.green)
	} else {
		console.info (message.grey)
	}
}) || false

const namespace = require ('cls-hooked').createNamespace ('transaction_namespace')
Sequelize.useCLS (namespace)
const sequelize = new Sequelize (config.database, config.username, config.password, {
	...config,
	define: {
		underscored: true,
		paranoid: true,
		deletedAt: 'deleted_at',
		updatedAt: 'updated_at',
		createdAt: 'created_at',
		...(config.define || { }),
		hooks: {
			beforeCreate: async function (instance, { transaction }) { },
			afterCreate: async function (instance, { transaction }) { },
			beforeUpdate: async function (instance, { transaction }) { },
			afterUpdate: async function (instance, { transaction }) { }
		}
	}
})

sequelize.beforeDefine (function (attributes) {
	attributes.remarks = {
		type: data_type.JSON
	}
})

global.data_type = Sequelize
global.Sequelize = Sequelize
global.sequelize = sequelize

fs.readdirSync (`${process.env.PWD}/api/models`).filter (file => /^[a-z_]*.js/.test (file) ).map (file => {
	db[file.replace (/\.js$/, '')] = require (`${process.env.PWD}/api/models/${file}`).init (sequelize, Sequelize)
})

for (let model_name of Object.keys (db).filter (model_name => db[model_name].associate)) {
	db[model_name].associate (db)

	Object.keys (db[model_name].prototype).map (key => {
		if (typeof db[model_name].prototype[key] === 'function') {
			db[model_name].prototype[key.decamelized] = db[model_name].prototype[key]
		}
	})
}

try {
	sequelize.sync ({
		force: config.force,
		logging: config.logging
	}).catch (error => {
		console.error (error)
	})
} catch (error) {
	console.error (error)
}

sequelize.query = function () {
	let args = arguments

	return Sequelize.prototype.query.apply (this, arguments).catch (function (error) {
		throw console.error ({
			sql: (args[0].sql && args[0].sql.query || args[0].query || '').replace (/\$[0-9]+/g, pattern_found => {
				let element = args[0].bind[pattern_found.replace ('$', '') / 1 - 1]

				return typeof element === 'string' && `'${element}'` || element
			}) || args[0].sql || args[0],
			stack: (error.stack || '').split ('\n')[0],
			hint: arguments[0].original.hint
		})
	})
}

Model.prototype.add_remark = async function ({ remark, remarks = [] } = { }, { user = '' } = { }) {
	console.function_called (`[${this[this.constructor.primaryKeyAttributes[0]]}]`)

	await this.update ({
		remarks: {
			...[...remarks, remark].filter (Boolean).reduce ((result, remark) => {
				result[now_minimal ()] = `${user && `[${user}] ` || ''}${remark}`
				return result
			}, { }),
			...this.remarks
		}
	})
}

Sequelize.models = db
db.sequelize = sequelize
db.Sequelize = Sequelize
global.namespace = namespace
global.models = db

module.exports = db

Are you sure that you deployed an updated version of forest-express-sequelize in the node_modules for your server B?

The initialization of your agent seems good, I don’t see how you could get this error otherwise.

Can you dump the file node_modules/forest-express-sequelize/package.json on your server, just to check?

I have dumped and reinstalled and it now works on both servers ! :partying_face:

Thanks a lot guillaume !

1 Like