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:
Diamond Lewis
2021-03-16 16:05:36 -05:00
committed by GitHub
parent 32fc45d2d2
commit a02014f557
38 changed files with 673 additions and 937 deletions

View File

@@ -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);
});
});