Solution 4 - ProspectOne/flexbalancer-js-docs GitHub Wiki

Solution 4: Multi Geo-Random with Monitor Overrides

This one is the 'full' version of the script that we used in our Tutorial.

The goal is:

  • to define specific answers (and even answer sets) for particular countries
  • to provide random selection logic in case if there is more than one answer candidate for the country
  • to implement boolean property requireMonitorData that validates only answers with Monitors online (if set to true)

If country is not in our list - we will use a random answer from our list. In this case, if Monitors validation is 'on' and there are no answers with Monitors online at all - we should fall back.

Yes, it may sound complicated, but in fact it's not that bad. Our configuration section goes first:

const configuration = {
    providers: [
        {
            name: 'foo', // candidate name
            cname: 'www.foo.com', // cname to pick as a result for the response Addr
            monitor: (304 as TMonitor) // Monitor ID that is created by user to monitor hostname
        },
        {
            name: 'bar',
            cname: 'www.bar.com',
            monitor: (305 as TMonitor)
        },
        {
            name: 'baz',
            cname: 'www.baz.com'
        }
    ],
    countriesAnswersSets: {
        'PL': ['bar', 'baz'],
        'JP': ['foo']
    },
    defaultTtl: 20,
    requireMonitorData: false // in this case answer monitor being online is not required
};

We have defined three answer providers, created sets for two countries (Poland and Japan), at countriesAnswersSets assigned two of our answer candidate 'names' to Poland and one - to Japan. We have added that mentioned above boolean property requireMonitorData and set it to 'false' for our example.

Now we add one our 'common' function for random element:

/**
 * Picks random item from array of items
 */
const getRandomElement = <T>(items: T[]): T => {
    return items[Math.floor(Math.random() * items.length)];
};

And we also create function that validates answer candidate. It returns either answer Monitor online status (if requireMonitorData is set to true) or just true if we don't care about Monitors statuses:

/**
 * If monitor is set for candidate - returns its availability, else returns true if monitor is not required
 */
const isProperCandidate = (candidate, requireMonitorData) => {
    if (candidate.monitor) {
        return isMonitorOnline(candidate.monitor)
    }
    return !requireMonitorData;
};

Now let's implement our logic inside onResponse (Main) function.

First, we parse our configuration and try to determine the user country:

    const {countriesAnswersSets, providers, defaultTtl, requireMonitorData} = configuration;

    // Country where request was made from
    let requestCountry = req.location.country as TCountry;

Now we check if we were able to get the country, and if it is one of the countries from our list (Poland or Japan) - check if there is answers set for it and if the answer candidates are valid. In our particular example all of them are valid, because requireMonitorData is set to false. But if you enable it - it won't validate any candidates with Monitors offline.

    // Checking if we were able to detect country, and if our country is listed in countriesAnswersSets list
    if (requestCountry && countriesAnswersSets[requestCountry]) {
        // Choose candidates that are listed in countriesAnswersSets and are proper candidates
        let geoFilteredCandidates = providers.filter(
            (provider) => countriesAnswersSets[requestCountry].includes(provider.name)
                && isProperCandidate(provider, requireMonitorData)
        );

If we get the user from Poland or Japan and the validation of the candidate(candidates) for that country has passed - we randomly pick the answer from the country answers set and use its cname for Response Address:

        // If we found proper geo candidates, pick one of them randomly and use cname for the answer
        if (geoFilteredCandidates.length) {
            res.setCNAMERecord(getRandomElement(geoFilteredCandidates).cname);
            res.setTTL(defaultTtl);
            return;
        }

If the user is from any other country, or country is 'unknown' - we pick random candidate and use its cname for Response. If requireMonitorData is set to true - we pick the candidate only from those with Monitors online.

    //If there was no geo candidates, we choose new ones from whole list by monitor filter
    const properCandidates = providers.filter(item => isProperCandidate(item, requireMonitorData));

    //Choose random candidate cname as response Addr (if we have any)
    if (properCandidates.length) {
        res.setCNAMERecord(getRandomElement(properCandidates).cname);
        res.setTTL(defaultTtl);
        return;
    }

In case all monitors are offline - we use the fallback:

    // If not - set fallback 
    res.setCNAMERecord('our.fallback.com');
    res.setTTL(defaultTtl);
    return;

Here we go! And here is our script:

const configuration = {
    providers: [
        {
            name: 'foo', // candidate name
            cname: 'www.foo.com', // cname to pick as a result for the response Addr
            monitor: (304 as TMonitor) // Monitor ID that is created by user to monitor hostname
        },
        {
            name: 'bar',
            cname: 'www.bar.com',
            monitor: (305 as TMonitor)
        },
        {
            name: 'baz',
            cname: 'www.baz.com'
        }
    ],
    countriesAnswersSets: {
        'PL': ['bar', 'baz'],
        'JP': ['foo']
    },
    defaultTtl: 20,
    requireMonitorData: false // in this case answer monitor being online is not required
};

/**
 * Picks random item from array of items
 */
const getRandomElement = <T>(items: T[]): T => {
    return items[Math.floor(Math.random() * items.length)];
};

/**
 * If monitor is set for candidate - returns its availability, else returns true if monitor is not required
 */
const isProperCandidate = (candidate, requireMonitorData) => {
    if (candidate.monitor) {
        return isMonitorOnline(candidate.monitor)
    }
    return !requireMonitorData;
};

function onRequest(req: IRequest, res: IResponse) {
    const {countriesAnswersSets, providers, defaultTtl, requireMonitorData} = configuration;

    // Country where request was made from
    let requestCountry = req.location.country as TCountry;

    // Checking if we were able to detect country, and if our country is listed in countriesAnswersSets list
    if (requestCountry && countriesAnswersSets[requestCountry]) {
        // Choose candidates that are listed in countriesAnswersSets and are proper candidates
        let geoFilteredCandidates = providers.filter(
            (provider) => countriesAnswersSets[requestCountry].includes(provider.name)
                && isProperCandidate(provider, requireMonitorData)
        );
        // If we found proper geo candidates, pick one of them randomly and use cname for the answer
        if (geoFilteredCandidates.length) {
            res.setCNAMERecord(getRandomElement(geoFilteredCandidates).cname);
            res.setTTL(defaultTtl);
            return;
        }
    }

    //If there was no geo candidates, we choose new ones from whole list by monitor filter
    const properCandidates = providers.filter(item => isProperCandidate(item, requireMonitorData));

    //Choose random candidate cname as response Addr (if we have any)
    if (properCandidates.length) {
        res.setCNAMERecord(getRandomElement(properCandidates).cname);
        res.setTTL(defaultTtl);
        return;
    }
    // If not - set fallback 
    res.setCNAMERecord('our.fallback.com');
    res.setTTL(defaultTtl);
    return;
}