Improve single schema cache (#7214)
* Initial Commit * fix flaky test * temporary set ci timeout * turn off ci check * fix postgres tests * fix tests * node flaky test * remove improvements * Update SchemaPerformance.spec.js * fix tests * revert ci * Create Singleton Object * properly clear cache testing * Cleanup * remove fit * try PushController.spec * try push test rewrite * try push enqueue time * Increase test timeout * remove pg server creation test * xit push tests * more xit * remove skipped tests * Fix conflicts * reduce ci timeout * fix push tests * Revert "fix push tests" This reverts commit 05aba62f1cbbca7d5d3e80b9444529f59407cb56. * improve initialization * fix flaky tests * xit flaky test * Update CHANGELOG.md * enable debug logs * Update LogsRouter.spec.js * create initial indexes in series * lint * horizontal scaling documentation * Update Changelog * change horizontalScaling db option * Add enableSchemaHooks option * move enableSchemaHooks to databaseOptions
This commit is contained in:
@@ -1,5 +1,4 @@
|
||||
const RedisCacheAdapter = require('../lib/Adapters/Cache/RedisCacheAdapter').default;
|
||||
const Config = require('../lib/Config');
|
||||
|
||||
function wait(sleep) {
|
||||
return new Promise(function (resolve) {
|
||||
@@ -168,356 +167,3 @@ describe_only(() => {
|
||||
.then(done);
|
||||
});
|
||||
});
|
||||
|
||||
describe_only(() => {
|
||||
return process.env.PARSE_SERVER_TEST_CACHE === 'redis';
|
||||
})('Redis Performance', function () {
|
||||
let cacheAdapter;
|
||||
let getSpy;
|
||||
let putSpy;
|
||||
let delSpy;
|
||||
|
||||
beforeEach(async () => {
|
||||
cacheAdapter = new RedisCacheAdapter();
|
||||
await reconfigureServer({
|
||||
cacheAdapter,
|
||||
});
|
||||
await cacheAdapter.clear();
|
||||
|
||||
getSpy = spyOn(cacheAdapter, 'get').and.callThrough();
|
||||
putSpy = spyOn(cacheAdapter, 'put').and.callThrough();
|
||||
delSpy = spyOn(cacheAdapter, 'del').and.callThrough();
|
||||
});
|
||||
|
||||
it('test new object', async () => {
|
||||
const object = new TestObject();
|
||||
object.set('foo', 'bar');
|
||||
await object.save();
|
||||
expect(getSpy.calls.count()).toBe(3);
|
||||
expect(putSpy.calls.count()).toBe(3);
|
||||
expect(delSpy.calls.count()).toBe(1);
|
||||
|
||||
const keys = await cacheAdapter.getAllKeys();
|
||||
expect(keys.length).toBe(0);
|
||||
});
|
||||
|
||||
it('test new object multiple fields', async () => {
|
||||
const container = new Container({
|
||||
dateField: new Date(),
|
||||
arrayField: [],
|
||||
numberField: 1,
|
||||
stringField: 'hello',
|
||||
booleanField: true,
|
||||
});
|
||||
await container.save();
|
||||
expect(getSpy.calls.count()).toBe(3);
|
||||
expect(putSpy.calls.count()).toBe(3);
|
||||
expect(delSpy.calls.count()).toBe(1);
|
||||
|
||||
const keys = await cacheAdapter.getAllKeys();
|
||||
expect(keys.length).toBe(0);
|
||||
});
|
||||
|
||||
it('test update existing fields', async () => {
|
||||
const object = new TestObject();
|
||||
object.set('foo', 'bar');
|
||||
await object.save();
|
||||
|
||||
getSpy.calls.reset();
|
||||
putSpy.calls.reset();
|
||||
|
||||
object.set('foo', 'barz');
|
||||
await object.save();
|
||||
expect(getSpy.calls.count()).toBe(3);
|
||||
expect(putSpy.calls.count()).toBe(1);
|
||||
expect(delSpy.calls.count()).toBe(2);
|
||||
|
||||
const keys = await cacheAdapter.getAllKeys();
|
||||
expect(keys.length).toBe(0);
|
||||
});
|
||||
|
||||
it('test saveAll / destroyAll', async () => {
|
||||
const object = new TestObject();
|
||||
await object.save();
|
||||
|
||||
getSpy.calls.reset();
|
||||
putSpy.calls.reset();
|
||||
|
||||
const objects = [];
|
||||
for (let i = 0; i < 10; i++) {
|
||||
const object = new TestObject();
|
||||
object.set('number', i);
|
||||
objects.push(object);
|
||||
}
|
||||
await Parse.Object.saveAll(objects);
|
||||
expect(getSpy.calls.count()).toBe(21);
|
||||
expect(putSpy.calls.count()).toBe(11);
|
||||
|
||||
getSpy.calls.reset();
|
||||
putSpy.calls.reset();
|
||||
|
||||
await Parse.Object.destroyAll(objects);
|
||||
expect(getSpy.calls.count()).toBe(11);
|
||||
expect(putSpy.calls.count()).toBe(1);
|
||||
expect(delSpy.calls.count()).toBe(3);
|
||||
|
||||
const keys = await cacheAdapter.getAllKeys();
|
||||
expect(keys.length).toBe(0);
|
||||
});
|
||||
|
||||
it('test saveAll / destroyAll batch', async () => {
|
||||
const object = new TestObject();
|
||||
await object.save();
|
||||
|
||||
getSpy.calls.reset();
|
||||
putSpy.calls.reset();
|
||||
|
||||
const objects = [];
|
||||
for (let i = 0; i < 10; i++) {
|
||||
const object = new TestObject();
|
||||
object.set('number', i);
|
||||
objects.push(object);
|
||||
}
|
||||
await Parse.Object.saveAll(objects, { batchSize: 5 });
|
||||
expect(getSpy.calls.count()).toBe(22);
|
||||
expect(putSpy.calls.count()).toBe(7);
|
||||
|
||||
getSpy.calls.reset();
|
||||
putSpy.calls.reset();
|
||||
|
||||
await Parse.Object.destroyAll(objects, { batchSize: 5 });
|
||||
expect(getSpy.calls.count()).toBe(12);
|
||||
expect(putSpy.calls.count()).toBe(2);
|
||||
expect(delSpy.calls.count()).toBe(5);
|
||||
|
||||
const keys = await cacheAdapter.getAllKeys();
|
||||
expect(keys.length).toBe(0);
|
||||
});
|
||||
|
||||
it('test add new field to existing object', async () => {
|
||||
const object = new TestObject();
|
||||
object.set('foo', 'bar');
|
||||
await object.save();
|
||||
|
||||
getSpy.calls.reset();
|
||||
putSpy.calls.reset();
|
||||
|
||||
object.set('new', 'barz');
|
||||
await object.save();
|
||||
expect(getSpy.calls.count()).toBe(3);
|
||||
expect(putSpy.calls.count()).toBe(2);
|
||||
expect(delSpy.calls.count()).toBe(2);
|
||||
|
||||
const keys = await cacheAdapter.getAllKeys();
|
||||
expect(keys.length).toBe(0);
|
||||
});
|
||||
|
||||
it('test add multiple fields to existing object', async () => {
|
||||
const object = new TestObject();
|
||||
object.set('foo', 'bar');
|
||||
await object.save();
|
||||
|
||||
getSpy.calls.reset();
|
||||
putSpy.calls.reset();
|
||||
|
||||
object.set({
|
||||
dateField: new Date(),
|
||||
arrayField: [],
|
||||
numberField: 1,
|
||||
stringField: 'hello',
|
||||
booleanField: true,
|
||||
});
|
||||
await object.save();
|
||||
expect(getSpy.calls.count()).toBe(3);
|
||||
expect(putSpy.calls.count()).toBe(2);
|
||||
expect(delSpy.calls.count()).toBe(2);
|
||||
|
||||
const keys = await cacheAdapter.getAllKeys();
|
||||
expect(keys.length).toBe(0);
|
||||
});
|
||||
|
||||
it('test user', async () => {
|
||||
const user = new Parse.User();
|
||||
user.setUsername('testing');
|
||||
user.setPassword('testing');
|
||||
await user.signUp();
|
||||
|
||||
expect(getSpy.calls.count()).toBe(8);
|
||||
expect(putSpy.calls.count()).toBe(2);
|
||||
expect(delSpy.calls.count()).toBe(1);
|
||||
|
||||
const keys = await cacheAdapter.getAllKeys();
|
||||
expect(keys.length).toBe(0);
|
||||
});
|
||||
|
||||
it('test allowClientCreation false', async () => {
|
||||
const object = new TestObject();
|
||||
await object.save();
|
||||
await reconfigureServer({
|
||||
cacheAdapter,
|
||||
allowClientClassCreation: false,
|
||||
});
|
||||
await cacheAdapter.clear();
|
||||
|
||||
getSpy.calls.reset();
|
||||
putSpy.calls.reset();
|
||||
delSpy.calls.reset();
|
||||
|
||||
object.set('foo', 'bar');
|
||||
await object.save();
|
||||
expect(getSpy.calls.count()).toBe(4);
|
||||
expect(putSpy.calls.count()).toBe(2);
|
||||
|
||||
getSpy.calls.reset();
|
||||
putSpy.calls.reset();
|
||||
|
||||
const query = new Parse.Query(TestObject);
|
||||
await query.get(object.id);
|
||||
expect(getSpy.calls.count()).toBe(3);
|
||||
expect(putSpy.calls.count()).toBe(1);
|
||||
expect(delSpy.calls.count()).toBe(2);
|
||||
|
||||
const keys = await cacheAdapter.getAllKeys();
|
||||
expect(keys.length).toBe(0);
|
||||
});
|
||||
|
||||
it('test query', async () => {
|
||||
const object = new TestObject();
|
||||
object.set('foo', 'bar');
|
||||
await object.save();
|
||||
|
||||
getSpy.calls.reset();
|
||||
putSpy.calls.reset();
|
||||
delSpy.calls.reset();
|
||||
|
||||
const query = new Parse.Query(TestObject);
|
||||
await query.get(object.id);
|
||||
expect(getSpy.calls.count()).toBe(2);
|
||||
expect(putSpy.calls.count()).toBe(1);
|
||||
expect(delSpy.calls.count()).toBe(1);
|
||||
|
||||
const keys = await cacheAdapter.getAllKeys();
|
||||
expect(keys.length).toBe(0);
|
||||
});
|
||||
|
||||
it('test query include', async () => {
|
||||
const child = new TestObject();
|
||||
await child.save();
|
||||
|
||||
const object = new TestObject();
|
||||
object.set('child', child);
|
||||
await object.save();
|
||||
|
||||
getSpy.calls.reset();
|
||||
putSpy.calls.reset();
|
||||
|
||||
const query = new Parse.Query(TestObject);
|
||||
query.include('child');
|
||||
await query.get(object.id);
|
||||
|
||||
expect(getSpy.calls.count()).toBe(4);
|
||||
expect(putSpy.calls.count()).toBe(1);
|
||||
expect(delSpy.calls.count()).toBe(3);
|
||||
|
||||
const keys = await cacheAdapter.getAllKeys();
|
||||
expect(keys.length).toBe(0);
|
||||
});
|
||||
|
||||
it('query relation without schema', async () => {
|
||||
const child = new Parse.Object('ChildObject');
|
||||
await child.save();
|
||||
|
||||
const parent = new Parse.Object('ParentObject');
|
||||
const relation = parent.relation('child');
|
||||
relation.add(child);
|
||||
await parent.save();
|
||||
|
||||
getSpy.calls.reset();
|
||||
putSpy.calls.reset();
|
||||
|
||||
const objects = await relation.query().find();
|
||||
expect(objects.length).toBe(1);
|
||||
expect(objects[0].id).toBe(child.id);
|
||||
|
||||
expect(getSpy.calls.count()).toBe(2);
|
||||
expect(putSpy.calls.count()).toBe(1);
|
||||
expect(delSpy.calls.count()).toBe(3);
|
||||
|
||||
const keys = await cacheAdapter.getAllKeys();
|
||||
expect(keys.length).toBe(0);
|
||||
});
|
||||
|
||||
it('test delete object', async () => {
|
||||
const object = new TestObject();
|
||||
object.set('foo', 'bar');
|
||||
await object.save();
|
||||
|
||||
getSpy.calls.reset();
|
||||
putSpy.calls.reset();
|
||||
delSpy.calls.reset();
|
||||
|
||||
await object.destroy();
|
||||
expect(getSpy.calls.count()).toBe(2);
|
||||
expect(putSpy.calls.count()).toBe(1);
|
||||
expect(delSpy.calls.count()).toBe(1);
|
||||
|
||||
const keys = await cacheAdapter.getAllKeys();
|
||||
expect(keys.length).toBe(0);
|
||||
});
|
||||
|
||||
it('test schema update class', async () => {
|
||||
const container = new Container();
|
||||
await container.save();
|
||||
|
||||
getSpy.calls.reset();
|
||||
putSpy.calls.reset();
|
||||
delSpy.calls.reset();
|
||||
|
||||
const config = Config.get('test');
|
||||
const schema = await config.database.loadSchema();
|
||||
await schema.reloadData();
|
||||
|
||||
const levelPermissions = {
|
||||
find: { '*': true },
|
||||
get: { '*': true },
|
||||
create: { '*': true },
|
||||
update: { '*': true },
|
||||
delete: { '*': true },
|
||||
addField: { '*': true },
|
||||
protectedFields: { '*': [] },
|
||||
};
|
||||
|
||||
await schema.updateClass(
|
||||
'Container',
|
||||
{
|
||||
fooOne: { type: 'Number' },
|
||||
fooTwo: { type: 'Array' },
|
||||
fooThree: { type: 'Date' },
|
||||
fooFour: { type: 'Object' },
|
||||
fooFive: { type: 'Relation', targetClass: '_User' },
|
||||
fooSix: { type: 'String' },
|
||||
fooSeven: { type: 'Object' },
|
||||
fooEight: { type: 'String' },
|
||||
fooNine: { type: 'String' },
|
||||
fooTeen: { type: 'Number' },
|
||||
fooEleven: { type: 'String' },
|
||||
fooTwelve: { type: 'String' },
|
||||
fooThirteen: { type: 'String' },
|
||||
fooFourteen: { type: 'String' },
|
||||
fooFifteen: { type: 'String' },
|
||||
fooSixteen: { type: 'String' },
|
||||
fooEighteen: { type: 'String' },
|
||||
fooNineteen: { type: 'String' },
|
||||
},
|
||||
levelPermissions,
|
||||
{},
|
||||
config.database
|
||||
);
|
||||
expect(getSpy.calls.count()).toBe(3);
|
||||
expect(putSpy.calls.count()).toBe(3);
|
||||
expect(delSpy.calls.count()).toBe(0);
|
||||
|
||||
const keys = await cacheAdapter.getAllKeys();
|
||||
expect(keys.length).toBe(1);
|
||||
});
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user