Skip to content

Commit

Permalink
sample timeout redis
Browse files Browse the repository at this point in the history
  • Loading branch information
pallavi2209 committed Mar 8, 2017
1 parent 9a3d25f commit c8e9147
Show file tree
Hide file tree
Showing 4 changed files with 347 additions and 1 deletion.
2 changes: 1 addition & 1 deletion cache/models/samples.js
Original file line number Diff line number Diff line change
Expand Up @@ -512,12 +512,12 @@ module.exports = {
*/
findSamples(logObject, method, params) {
const opts = getOptionsFromReq(params);
const commands = [];
const response = [];

// get all Samples sorted lexicographically
return redisClient.sortAsync(constants.indexKey.sample, 'alpha')
.then((allSampKeys) => {
const commands = [];
const filteredSampKeys = applyFiltersOnSampKeys(allSampKeys, opts);

// add to commands
Expand Down
94 changes: 94 additions & 0 deletions cache/sampleStoreTimeout.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
/**
* Copyright (c) 2017, salesforce.com, inc.
* All rights reserved.
* Licensed under the BSD 3-Clause license.
* For full license text, see LICENSE.txt file in the repo root or
* https://opensource.org/licenses/BSD-3-Clause
*/

/**
* ./cache/sampleStoreTimeout.js
*
* Timeout samples
*/
'use strict'; // eslint-disable-line strict
const sampleStore = require('./sampleStore');
const redisClient = require('./redisCache').client.sampleStore;
const isTimedOut = require('../db/helpers/sampleUtils').isTimedOut;
const constants = require('../api/v1/constants');
const ONE = 1;
const TWO = 2;

module.exports = {

/**
* Invalidates samples which were last updated before the "timeout" specified
* by the aspect. Get all samples and corresponding aspects. If the sample
* should be timed out, set sample value, status, previous status, status
* changed at and updated at fields.
* @param {Date} now - Date object
* @returns {Promise} - Resolves to the number of evaluated and timed out
* samples
*/
doTimeout(now) {
const curr = now || new Date();
let numberTimedOut = 0;
let numberEvaluated = 0;

return new Promise((resolve, reject) => {
redisClient.smembersAsync(sampleStore.constants.indexKey.sample)
.then((allSamples) => {
const commands = [];
const aspectType = sampleStore.constants.objectType.aspect;

allSamples.forEach((sampKey) => {
const aspectName = sampKey.split('|')[ONE];
commands.push(['hgetall', sampKey]); // get sample
commands.push(
['hgetall', sampleStore.toKey(aspectType, aspectName)]
);
});

return redisClient.batch(commands).execAsync();
})
.then((redisResponses) => {
const samples = [];
const aspects = [];
const sampCmds = [];

for (let num = 0; num < redisResponses.length; num += TWO) {
samples.push(redisResponses[num]);
aspects.push(redisResponses[num + ONE]);
}

for (let num = 0; num < samples.length; num++) {
const samp = samples[num];
const asp = aspects[num];
const sampUpdDateTime = new Date(samp.updatedAt);
if (asp && isTimedOut(asp.timeout, curr, sampUpdDateTime)) {
const sampType = sampleStore.constants.objectType.sample;

const objToUpdate = {
value: constants.statuses.Timeout,
status: constants.statuses.Timeout,
previousStatus: samp.status,
statusChangedAt: new Date().toString(),
updatedAt: new Date().toString(),
};
sampCmds.push([
'hmset',
sampleStore.toKey(sampType, samp.name),
objToUpdate,
]);
numberTimedOut++;
}
}

numberEvaluated = samples.length;
return redisClient.batch(sampCmds).execAsync();
})
.then(() => resolve({ numberEvaluated, numberTimedOut }))
.catch(reject);
});
},
};
5 changes: 5 additions & 0 deletions clock/scheduledJobs/sampleTimeoutJob.js
Original file line number Diff line number Diff line change
Expand Up @@ -14,13 +14,18 @@
*/
const featureToggles = require('feature-toggles');
const dbSample = require('../../db/index').Sample;
const sampleStoreTimeout = require('../../cache/sampleStoreTimeout');

/**
* Execute the call to check for sample timeouts.
*
* @returns {Promise}
*/
function execute() {
if (featureToggles.isFeatureEnabled('enableRedisSampleStore')) {
return sampleStoreTimeout.doTimeout();
}

return dbSample.doTimeout();
} // execute

Expand Down
247 changes: 247 additions & 0 deletions tests/cache/models/samples/timeout.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,247 @@
/**
* Copyright (c) 2017, salesforce.com, inc.
* All rights reserved.
* Licensed under the BSD 3-Clause license.
* For full license text, see LICENSE.txt file in the repo root or
* https://opensource.org/licenses/BSD-3-Clause
*/

/**
* tests/cache/models/samples/timeout.js
*/
'use strict'; // eslint-disable-line strict

const tu = require('../../../testUtils');
const rtu = require('../redisTestUtil');
const samstoinit = require('../../../../cache/sampleStoreInit');
const doTimeout = require('../../../../cache/sampleStoreTimeout').doTimeout;
const redisClient = require('../../../../cache/redisCache').client.sampleStore;
const expect = require('chai').expect;
const Sample = tu.db.Sample;
const Aspect = tu.db.Aspect;
const Subject = tu.db.Subject;

describe(`api::cache::timeout`, () => {
let updatedAt;
const defaultForStatus = 'Timeout';
const twentyFourhours = 24;
const hundredDays = 100;
const tenSeconds = 10;
const fiveMinutes = 5;

before(() => tu.toggleOverride('enableRedisSampleStore', true));
beforeEach((done) => {
Aspect.create({
isPublished: true,
name: `${tu.namePrefix}OneSecond`,
timeout: '1s',
valueType: 'NUMERIC',
criticalRange: [0, 0],
warningRange: [1, 1],
infoRange: [2, 2],
okRange: [3, 3],
})
.then(() => Aspect.create({
isPublished: true,
name: `${tu.namePrefix}TwoMinutes`,
timeout: '2m',
valueType: 'NUMERIC',
criticalRange: [0, 0],
warningRange: [1, 1],
infoRange: [2, 2],
okRange: [3, 3],
}))
.then(() => Aspect.create({
isPublished: true,
name: `${tu.namePrefix}ThreeHours`,
timeout: '3H',
valueType: 'NUMERIC',
criticalRange: [0, 0],
warningRange: [1, 1],
infoRange: [2, 2],
okRange: [3, 3],
}))
.then(() => Aspect.create({
isPublished: true,
name: `${tu.namePrefix}NinetyDays`,
timeout: '90D',
valueType: 'NUMERIC',
criticalRange: [0, 0],
warningRange: [1, 1],
infoRange: [2, 2],
okRange: [3, 3],
}))
.then(() => Subject.create({
isPublished: true,
name: `${tu.namePrefix}Subject`,
}))
.then(() => Sample.bulkUpsertByName([
{ name: `${tu.namePrefix}Subject|${tu.namePrefix}OneSecond`, value: 1 },
{ name: `${tu.namePrefix}Subject|${tu.namePrefix}TwoMinutes`, value: 1 },
{ name: `${tu.namePrefix}Subject|${tu.namePrefix}ThreeHours`, value: 2 },
{ name: `${tu.namePrefix}Subject|${tu.namePrefix}NinetyDays`, value: 3 },
]))
.then(() => Sample.findAll({
attributes: ['name', 'updatedAt'],
where: {
name: {
$ilike: `${tu.namePrefix}Subject|%`,
},
},
})
.each((s) => {
updatedAt = s.updatedAt;
}))
.then(() => samstoinit.eradicate())
.then(() => samstoinit.init())
.then(() => done())
.catch(done);
});

afterEach(rtu.forceDelete);
after(() => tu.toggleOverride('enableRedisSampleStore', false));

it('simulate 100 days in the future', (done) => {
const mockUpdatedAt = updatedAt;
mockUpdatedAt.setHours(updatedAt.getHours() +
(twentyFourhours * hundredDays));
doTimeout(mockUpdatedAt)
.then((res) => {
expect(res).to.eql({ numberEvaluated: 4, numberTimedOut: 4 });
})
.then(() => redisClient.keysAsync(
`samsto:sample:${tu.namePrefix}Subject|*`.toLowerCase())
)
.then((sNames) => {
const commands = [];
sNames.forEach((s) => {
commands.push(['hgetall', s]);
});
return redisClient.batch(commands).execAsync();
})
.then((samples) => {
samples.forEach((s) => {
expect(s.status).to.equal(defaultForStatus);
});
done();
})
.catch(done);
});

it('simulate 1 day in the future', (done) => {
const mockUpdatedAt = updatedAt;
mockUpdatedAt.setHours(updatedAt.getHours() + twentyFourhours);
doTimeout(mockUpdatedAt)
.then((res) => {
expect(res).to.eql({ numberEvaluated: 4, numberTimedOut: 3 });
})
.then(() => redisClient.keysAsync(
`samsto:sample:${tu.namePrefix}Subject|*`.toLowerCase())
)
.then((sNames) => {
const commands = [];
sNames.forEach((s) => {
commands.push(['hgetall', s]);
});
return redisClient.batch(commands).execAsync();
})
.then((samples) => {
samples.forEach((s) => {
switch (s.name) {
case `${tu.namePrefix}Subject|${tu.namePrefix}OneSecond`:
expect(s.status).to.equal(defaultForStatus);
break;
case `${tu.namePrefix}Subject|${tu.namePrefix}TwoMinutes`:
expect(s.status).to.equal(defaultForStatus);
break;
case `${tu.namePrefix}Subject|${tu.namePrefix}ThreeHours`:
expect(s.status).to.equal(defaultForStatus);
break;
case `${tu.namePrefix}Subject|${tu.namePrefix}NinetyDays`:
expect(s.status).to.not.equal(defaultForStatus);
break;
}
});
})
.then(() => done())
.catch(done);
});

it('simulate 5 minutes in the future', (done) => {
const mockUpdatedAt = updatedAt;
mockUpdatedAt.setMinutes(updatedAt.getMinutes() + fiveMinutes);
doTimeout(mockUpdatedAt)
.then((res) => {
expect(res).to.eql({ numberEvaluated: 4, numberTimedOut: 2 });
})
.then(() => redisClient.keysAsync(
`samsto:sample:${tu.namePrefix}Subject|*`.toLowerCase())
)
.then((sNames) => {
const commands = [];
sNames.forEach((s) => {
commands.push(['hgetall', s]);
});
return redisClient.batch(commands).execAsync();
})
.then((samples) => {
samples.forEach((s) => {
switch (s.name) {
case `${tu.namePrefix}Subject|${tu.namePrefix}OneSecond`:
expect(s.status).to.equal(defaultForStatus);
break;
case `${tu.namePrefix}Subject|${tu.namePrefix}TwoMinutes`:
expect(s.status).to.equal(defaultForStatus);
break;
case `${tu.namePrefix}Subject|${tu.namePrefix}ThreeHours`:
expect(s.status).to.not.equal(null);
break;
case `${tu.namePrefix}Subject|${tu.namePrefix}NinetyDays`:
expect(s.status).to.not.equal(null);
break;
}
});
})
.then(() => done())
.catch(done);
});

it('simulate 10 seconds in the past', (done) => {
const mockUpdatedAt = updatedAt;
mockUpdatedAt.setSeconds(updatedAt.getSeconds() - tenSeconds);
doTimeout(mockUpdatedAt)
.then((res) => {
expect(res).to.eql({ numberEvaluated: 4, numberTimedOut: 0 });
})
.then(() => redisClient.keysAsync(
`samsto:sample:${tu.namePrefix}Subject|*`.toLowerCase())
)
.then((sNames) => {
const commands = [];
sNames.forEach((s) => {
commands.push(['hgetall', s]);
});
return redisClient.batch(commands).execAsync();
})
.then((samples) => {
samples.forEach((s) => {
switch (s.name) {
case `${tu.namePrefix}Subject|${tu.namePrefix}OneSecond`:
expect(s.status).to.not.equal(null);
break;
case `${tu.namePrefix}Subject|${tu.namePrefix}TwoMinutes`:
expect(s.status).to.not.equal(null);
break;
case `${tu.namePrefix}Subject|${tu.namePrefix}ThreeHours`:
expect(s.status).to.not.equal(null);
break;
case `${tu.namePrefix}Subject|${tu.namePrefix}NinetyDays`:
expect(s.status).to.not.equal(null);
break;
}
});
})
.then(() => done())
.catch(done);
});
});

0 comments on commit c8e9147

Please sign in to comment.