Adds redis cache for distributed environments (#2691)
* Makes schemaCache clearning promise-based * Adds redis cache adapter for distributed systems * Adds redis service to travis * allow pg to fail
This commit is contained in:
69
src/Adapters/Cache/RedisCacheAdapter.js
Normal file
69
src/Adapters/Cache/RedisCacheAdapter.js
Normal file
@@ -0,0 +1,69 @@
|
||||
import redis from 'redis';
|
||||
import logger from '../../logger';
|
||||
|
||||
function debug() {
|
||||
logger.debug.apply(logger, ['RedisCacheAdapter', ...arguments]);
|
||||
}
|
||||
|
||||
export class RedisCacheAdapter {
|
||||
|
||||
constructor(ctx) {
|
||||
this.client = redis.createClient(ctx);
|
||||
this.p = Promise.resolve();
|
||||
}
|
||||
|
||||
get(key) {
|
||||
debug('get', key);
|
||||
this.p = this.p.then(() => {
|
||||
return new Promise((resolve, _) => {
|
||||
this.client.get(key, function(err, res) {
|
||||
debug('-> get', key, res);
|
||||
if(!res) {
|
||||
return resolve(null);
|
||||
}
|
||||
resolve(JSON.parse(res));
|
||||
});
|
||||
});
|
||||
});
|
||||
return this.p;
|
||||
}
|
||||
|
||||
put(key, value, ttl) {
|
||||
value = JSON.stringify(value);
|
||||
debug('put', key, value, ttl);
|
||||
this.p = this.p.then(() => {
|
||||
return new Promise((resolve, _) => {
|
||||
this.client.set(key, value, function(err, res) {
|
||||
resolve();
|
||||
});
|
||||
});
|
||||
});
|
||||
return this.p;
|
||||
}
|
||||
|
||||
del(key) {
|
||||
debug('del', key);
|
||||
this.p = this.p.then(() => {
|
||||
return new Promise((resolve, _) => {
|
||||
this.client.del(key, function(err, res) {
|
||||
resolve();
|
||||
});
|
||||
});
|
||||
});
|
||||
return this.p;
|
||||
}
|
||||
|
||||
clear() {
|
||||
debug('clear');
|
||||
this.p = this.p.then(() => {
|
||||
return new Promise((resolve, _) => {
|
||||
this.client.flushall(function(err, res) {
|
||||
resolve();
|
||||
});
|
||||
});
|
||||
});
|
||||
return this.p;
|
||||
}
|
||||
}
|
||||
|
||||
export default RedisCacheAdapter;
|
||||
@@ -323,15 +323,20 @@ export default class SchemaController {
|
||||
}
|
||||
|
||||
reloadData(options = {clearCache: false}) {
|
||||
let promise = Promise.resolve();
|
||||
if (options.clearCache) {
|
||||
this._cache.clear();
|
||||
promise = promise.then(() => {
|
||||
return this._cache.clear();
|
||||
});
|
||||
}
|
||||
if (this.reloadDataPromise && !options.clearCache) {
|
||||
return this.reloadDataPromise;
|
||||
}
|
||||
this.data = {};
|
||||
this.perms = {};
|
||||
this.reloadDataPromise = this.getAllClasses(options)
|
||||
this.reloadDataPromise = promise.then(() => {
|
||||
return this.getAllClasses(options);
|
||||
})
|
||||
.then(allSchemas => {
|
||||
allSchemas.forEach(schema => {
|
||||
this.data[schema.className] = injectDefaultSchema(schema).fields;
|
||||
@@ -355,10 +360,13 @@ export default class SchemaController {
|
||||
}
|
||||
|
||||
getAllClasses(options = {clearCache: false}) {
|
||||
let promise = Promise.resolve();
|
||||
if (options.clearCache) {
|
||||
this._cache.clear();
|
||||
promise = this._cache.clear();
|
||||
}
|
||||
return this._cache.getAllClasses().then((allClasses) => {
|
||||
return promise.then(() => {
|
||||
return this._cache.getAllClasses()
|
||||
}).then((allClasses) => {
|
||||
if (allClasses && allClasses.length && !options.clearCache) {
|
||||
return Promise.resolve(allClasses);
|
||||
}
|
||||
@@ -373,22 +381,25 @@ export default class SchemaController {
|
||||
}
|
||||
|
||||
getOneSchema(className, allowVolatileClasses = false, options = {clearCache: false}) {
|
||||
let promise = Promise.resolve();
|
||||
if (options.clearCache) {
|
||||
this._cache.clear();
|
||||
promise = this._cache.clear();
|
||||
}
|
||||
if (allowVolatileClasses && volatileClasses.indexOf(className) > -1) {
|
||||
return Promise.resolve(this.data[className]);
|
||||
}
|
||||
return this._cache.getOneSchema(className).then((cached) => {
|
||||
if (cached && !options.clearCache) {
|
||||
return Promise.resolve(cached);
|
||||
return promise.then(() => {
|
||||
if (allowVolatileClasses && volatileClasses.indexOf(className) > -1) {
|
||||
return Promise.resolve(this.data[className]);
|
||||
}
|
||||
return this._dbAdapter.getClass(className)
|
||||
.then(injectDefaultSchema)
|
||||
.then((result) => {
|
||||
return this._cache.setOneSchema(className, result).then(() => {
|
||||
return result;
|
||||
})
|
||||
return this._cache.getOneSchema(className).then((cached) => {
|
||||
if (cached && !options.clearCache) {
|
||||
return Promise.resolve(cached);
|
||||
}
|
||||
return this._dbAdapter.getClass(className)
|
||||
.then(injectDefaultSchema)
|
||||
.then((result) => {
|
||||
return this._cache.setOneSchema(className, result).then(() => {
|
||||
return result;
|
||||
})
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
@@ -409,8 +420,9 @@ export default class SchemaController {
|
||||
return this._dbAdapter.createClass(className, convertSchemaToAdapterSchema({ fields, classLevelPermissions, className }))
|
||||
.then(convertAdapterSchemaToParseSchema)
|
||||
.then((res) => {
|
||||
this._cache.clear();
|
||||
return res;
|
||||
return this._cache.clear().then(() => {
|
||||
return Promise.resolve(res);
|
||||
});
|
||||
})
|
||||
.catch(error => {
|
||||
if (error && error.code === Parse.Error.DUPLICATE_VALUE) {
|
||||
@@ -508,6 +520,7 @@ export default class SchemaController {
|
||||
}
|
||||
})
|
||||
.catch(error => {
|
||||
console.error(error);
|
||||
// The schema still doesn't validate. Give up
|
||||
throw new Parse.Error(Parse.Error.INVALID_JSON, 'schema class name does not revalidate');
|
||||
});
|
||||
|
||||
@@ -3,6 +3,7 @@ import S3Adapter from 'parse-server-s3-adapter'
|
||||
import FileSystemAdapter from 'parse-server-fs-adapter'
|
||||
import InMemoryCacheAdapter from './Adapters/Cache/InMemoryCacheAdapter'
|
||||
import NullCacheAdapter from './Adapters/Cache/NullCacheAdapter'
|
||||
import RedisCacheAdapter from './Adapters/Cache/RedisCacheAdapter'
|
||||
import TestUtils from './TestUtils';
|
||||
import { useExternal } from './deprecated';
|
||||
import { getLogger } from './logger';
|
||||
@@ -22,4 +23,4 @@ Object.defineProperty(module.exports, 'logger', {
|
||||
});
|
||||
|
||||
export default ParseServer;
|
||||
export { S3Adapter, GCSAdapter, FileSystemAdapter, InMemoryCacheAdapter, NullCacheAdapter, TestUtils, _ParseServer as ParseServer };
|
||||
export { S3Adapter, GCSAdapter, FileSystemAdapter, InMemoryCacheAdapter, NullCacheAdapter, RedisCacheAdapter, TestUtils, _ParseServer as ParseServer };
|
||||
|
||||
Reference in New Issue
Block a user