Multi-tenant with mongoose and Node.js. One or multiple connection?

Viewed 1396

We are building an Saas application where we need, for technical reasons (backup, security), adopt a multi-tenants architecture (aka one DB per customer). We are using Node.js (with Typescript) and MongoDB (with mongoose driver).

For a first test, we are doing as below. Please note that it is a simple draft easily reproductible.

We start a new connection in index.ts, the "entry-point":

try {
        //Define Mongo connection options
        const mongoOptions = {
            useNewUrlParser: true,
            useCreateIndex: true,
            useUnifiedTopology: true,
            useFindAndModify: false,
            autoIndex: true,
            poolSize: 10,
            bufferMaxEntries: 0,
            connectTimeoutMS: 10000,
            socketTimeoutMS: 30000,
        };
        // Creating mongo connection
        const connection = await mongoose.connect(
            `mongodb://${process.env.MONGO_HOSTNAME}:${process.env.MONGO_PORT}`,
            mongoOptions
        );
        // connectToMongoDB;
        console.log('Successfuly connected to mongo database !');
    } catch (err) {
        console.log(err);
    }

Then in a separate file, we have some logic for selecting the appropriate Database and return the Model :

import mongoose from 'mongoose';

export class ClientDbProvider {

    static async getTenantDb(
        tenantId: string,
        modelName: string,
        schema: mongoose.Schema
    ) {
        const dbName = `spearateDB_${tenantId}`;
        const db = mongoose.connection.useDb(dbName, { useCache: true });
        db.model(modelName, schema);
        return db;
    }

    static async getModelForDb(databaseName: string, 
                               model: mongoose.Model<mongoose.Document>, schema: mongoose.Schema
    ) {
        const connection = await ClientDbProvider.getTenantDb(
            databaseName,
            model.modelName,
            schema
        );

        return connection.model(model.modelName);
    }
}

Then, in any route, we include clientID for using the appropriate DB.

router.post('/api/data/:clientID', async (req: Request, res: Response) => {
    const { name, value } = req.body;
    const data = Data.build({
        name: name,
        value: value,
    });
    try {
        const dataModel = await ClientDbProvider.getModelForDb(
            req.params.clientID,
            Data,
            DataSchema
        );
        const doc = await dataModel.create(data);
    } catch (err) {
        console.log(err);
    }
    res.send({});
});

Basically we call ClientDbProvider.getModelForDb for getting a Model. The getModelForDb switches to a different database using the same connection pool and return a model.

Note on the app:

  • the database will be continuously fills by data as it will store telemetry data from several sensors (some can generate data every second, some every 10 minutes...)
  • The api will be mostly for reading data (send it as JSON).
  • Some queries could be long as it will depends on own many data the client ask (even if we will put some defaults limits).
  • We will never have a huge amount of customers (DB). We plan to have, for the next two years, 20 to 40 customers (we will upgrade to another architecture if needed such as Sharded cluster). In any cases, the number of customer will never be like 1 000 000 or so...
  • All DB are on the same server/mongo instance (for now)

Questions:

  • Does this 'draft' code could cause some trouble or performance issue ?
  • Would that make sense to create a connection per DB (customer) with mongoose.createConnection function and then cache the connection as describe here (in a global variable for example) ?
  • As all of our DB are on the same server, does increasing the pool of the connections isn't sufficient ?
0 Answers
Related