strongloop/loopback - Change connection string based on route value
Asked Answered
L

1

1

My application's users are geographically dispersed and data is stored in various regions. Each region has it's own data center and database server.

I would like to include a route value to indicate the region that the user wants to access and connect to, as follows:

/api/region/1/locations/

/api/region/2/locations/

/api/region/3/locations/

Depending on the region passed in, I would like to change the connection string being used. I assume this can be performed somewhere in the middleware chain, but don't know where/how. Any help is appreciated!

Le answered 6/10, 2016 at 3:34 Comment(2)
By connection string do you mean change the datasource where the request will be persisted ? Like if the user does POST api/region/1/locations/ you want to persist to some datasource in say the US, and with id 2, persist to the datasource located in say France ?Coiffeur
@Coiffeur Yes, that is precisely correctLe
C
4

What should not be done

Loopback provides a method MyModel.attachTo (doesnt seem to be documented, but a reference to it is made there ).

But since it is a static method, it affects the entire Model, not a single instance.

So for this to work on a per-request basis, you must switch the DB right before the call to the datasource method, to make sure nothing async starts in between. I don't think this is possible.

This is an example using an operation hook (and define all datasources, include dbRegion1 below in datasources.json)

Bad, don't that below. Just for reference

Region.observe('loaded', function filterProperties(ctx, next) {
   app.models.Region.attachTo(app.dataSources.dbRegion1);
}

But then you will most likely face concurrency issues when your API receives multiple requests in a short time.

(Another way to see it is that the server is no longer truly stateless, execution will not depend only on inputs but also on a shared state).

The hook may set region2 for request 2 while the method called after the hook was expecting to use region1 for request 1. This will be the case if something async is triggered between the hook and the actual call to the datasource method.

So ultimately, I don't think you should do that. I'm just putting it there because some people have recommended it in other SO posts, but it's just bad.

Potential option 1

Build an external re-routing server, that will re-route the requests from the API server to the appropriate region database.

Use the loopback-connector-rest in your API server to consume this microservice, and use it as a single datasource for all your models. This provides abstraction over database selection.

Then of course there is still the matter of implementing the microservice, but maybe you can find some other ORM than loopback's that will support database sharding, and use it in that microservice.

Potential option 2

Create a custom loopback connector that will act as router for MySQL queries. Depending on region value passed inside the query, re-route the query to the appropriate DB.

Option 3

Use a more distributed architecture. Write a region-specific server to persist region-specific data. Run for instance 3 different servers, each one configured for a region. + 1 common server for routing

Then build a routing middleware for your single user-facing REST api server. Basic example:

var express = require('express');
var request = require('request');

var ips = ['127.0.0.1', '127.0.0.2'];

app.all('/api/region/:id', function (req, res, next) {
  console.log('Reroute to region server ' + req.params.id);
  request(ips[req.params.id], function (error, response, body) {
    if (err) return next(err);
    next(null, body);
  });
});

Maybe this option is the easiest to do

Coiffeur answered 7/10, 2016 at 15:16 Comment(2)
Thanks for the awesome answer. I've upvoted but have not yet decided on which approach to take (and will likely analyze all 3 options). I'll leave the question 'unanswered' until I have reviewed some more. Thanks again!Le
Sure, please keep me posted I am interested with the feedbackCoiffeur

© 2022 - 2024 — McMap. All rights reserved.