From 3b88753c8e67b9f50df347bbf80f3c2e534e04da Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Miroslav=20Bajto=C5=A1?= Date: Fri, 12 Aug 2016 13:39:47 +0200 Subject: [PATCH] Cache remoting descriptions to speed up tests Calling strong-globalize is relatively expensive (about 300 microseconds for each call). When using per-app local registry in unit-tests, each app creation requires about 500 calls of `g.t` just for remoting metadata, i.e. about 150ms. In this commit, we introduce `g.s` that caches the results from strong-globalize to speed up creation of remoting metadata. --- lib/persisted-model.js | 62 +++++++++++++++++++++++++----------------- 1 file changed, 37 insertions(+), 25 deletions(-) diff --git a/lib/persisted-model.js b/lib/persisted-model.js index 4ecfa05f..537448b5 100644 --- a/lib/persisted-model.js +++ b/lib/persisted-model.js @@ -16,6 +16,18 @@ var debug = require('debug')('loopback:persisted-model'); var PassThrough = require('stream').PassThrough; var utils = require('./utils'); +// workaround for low performance of strong-globalize +// see https://github.com/strongloop/strong-globalize/issues/66 +var stringCache = Object.create(null); +g.s = function(str) { + assert.equal(1, arguments.length, 'g.s() does not support parameters'); + if (str in stringCache) + return stringCache[str]; + var result = g.t(str); + stringCache[str] = result; + return result; +}; + module.exports = function(registry) { var Model = registry.getModel('Model'); @@ -560,7 +572,7 @@ module.exports = function(registry) { } setRemoting(PersistedModel, 'create', { - description: g.f('Create a new instance of the model and persist it into the data source.'), + description: g.s('Create a new instance of the model and persist it into the data source.'), accessType: 'WRITE', accepts: {arg: 'data', type: 'object', description: 'Model instance data', http: {source: 'body'}}, returns: {arg: 'data', type: typeName, root: true}, @@ -569,7 +581,7 @@ module.exports = function(registry) { setRemoting(PersistedModel, 'upsert', { aliases: ['updateOrCreate'], - description: g.f('Update an existing model instance or insert a new one ' + + description: g.s('Update an existing model instance or insert a new one ' + 'into the data source.'), accessType: 'WRITE', accepts: {arg: 'data', type: 'object', description: 'Model instance data', http: {source: 'body'}}, @@ -578,7 +590,7 @@ module.exports = function(registry) { }); setRemoting(PersistedModel, 'exists', { - description: g.f('Check whether a model instance exists in the data source.'), + description: g.s('Check whether a model instance exists in the data source.'), accessType: 'READ', accepts: {arg: 'id', type: 'any', description: 'Model id', required: true}, returns: {arg: 'exists', type: 'boolean'}, @@ -609,13 +621,13 @@ module.exports = function(registry) { }); setRemoting(PersistedModel, 'findById', { - description: g.f('Find a model instance by {{id}} from the data source.'), + description: g.s('Find a model instance by {{id}} from the data source.'), accessType: 'READ', accepts: [ { arg: 'id', type: 'any', description: 'Model id', required: true, http: {source: 'path'}}, { arg: 'filter', type: 'object', - description: g.f('Filter defining fields and include') }, + description: g.s('Filter defining fields and include') }, ], returns: {arg: 'data', type: typeName, root: true}, http: {verb: 'get', path: '/:id'}, @@ -623,7 +635,7 @@ module.exports = function(registry) { }); setRemoting(PersistedModel, 'find', { - description: g.f('Find all instances of the model matched by filter from the data source.'), + description: g.s('Find all instances of the model matched by filter from the data source.'), accessType: 'READ', accepts: {arg: 'filter', type: 'object', description: 'Filter defining fields, where, include, order, offset, and limit'}, returns: {arg: 'data', type: [typeName], root: true}, @@ -631,7 +643,7 @@ module.exports = function(registry) { }); setRemoting(PersistedModel, 'findOne', { - description: g.f('Find first instance of the model matched by filter from the data source.'), + description: g.s('Find first instance of the model matched by filter from the data source.'), accessType: 'READ', accepts: {arg: 'filter', type: 'object', description: 'Filter defining fields, where, include, order, offset, and limit'}, returns: {arg: 'data', type: typeName, root: true}, @@ -640,7 +652,7 @@ module.exports = function(registry) { }); setRemoting(PersistedModel, 'destroyAll', { - description: g.f('Delete all matching records.'), + description: g.s('Delete all matching records.'), accessType: 'WRITE', accepts: {arg: 'where', type: 'object', description: 'filter.where object'}, returns: { @@ -655,17 +667,17 @@ module.exports = function(registry) { setRemoting(PersistedModel, 'updateAll', { aliases: ['update'], - description: g.f('Update instances of the model matched by {{where}} from the data source.'), + description: g.s('Update instances of the model matched by {{where}} from the data source.'), accessType: 'WRITE', accepts: [ {arg: 'where', type: 'object', http: { source: 'query'}, - description: g.f('Criteria to match model instances')}, + description: g.s('Criteria to match model instances')}, {arg: 'data', type: 'object', http: {source: 'body'}, - description: g.f('An object of model property name/value pairs')}, + description: g.s('An object of model property name/value pairs')}, ], returns: { arg: 'count', - description: g.f('The number of instances updated'), + description: g.s('The number of instances updated'), type: 'object', root: true }, @@ -674,7 +686,7 @@ module.exports = function(registry) { setRemoting(PersistedModel, 'deleteById', { aliases: ['destroyById', 'removeById'], - description: g.f('Delete a model instance by {{id}} from the data source.'), + description: g.s('Delete a model instance by {{id}} from the data source.'), accessType: 'WRITE', accepts: {arg: 'id', type: 'any', description: 'Model id', required: true, http: {source: 'path'}}, @@ -683,7 +695,7 @@ module.exports = function(registry) { }); setRemoting(PersistedModel, 'count', { - description: g.f('Count instances of the model matched by where from the data source.'), + description: g.s('Count instances of the model matched by where from the data source.'), accessType: 'READ', accepts: {arg: 'where', type: 'object', description: 'Criteria to match model instances'}, returns: {arg: 'count', type: 'number'}, @@ -691,7 +703,7 @@ module.exports = function(registry) { }); setRemoting(PersistedModel.prototype, 'updateAttributes', { - description: g.f('Update attributes for a model instance and persist it into ' + + description: g.s('Update attributes for a model instance and persist it into ' + 'the data source.'), accessType: 'WRITE', accepts: {arg: 'data', type: 'object', http: {source: 'body'}, description: 'An object of model property name/value pairs'}, @@ -701,7 +713,7 @@ module.exports = function(registry) { if (options.trackChanges || options.enableRemoteReplication) { setRemoting(PersistedModel, 'diff', { - description: g.f('Get a set of deltas and conflicts since the given checkpoint.'), + description: g.s('Get a set of deltas and conflicts since the given checkpoint.'), accessType: 'READ', accepts: [ {arg: 'since', type: 'number', description: 'Find deltas since this checkpoint'}, @@ -713,7 +725,7 @@ module.exports = function(registry) { }); setRemoting(PersistedModel, 'changes', { - description: g.f('Get the changes to a model since a given checkpoint.' + + description: g.s('Get the changes to a model since a given checkpoint.' + 'Provide a filter object to reduce the number of results returned.'), accessType: 'READ', accepts: [ @@ -725,7 +737,7 @@ module.exports = function(registry) { }); setRemoting(PersistedModel, 'checkpoint', { - description: g.f('Create a checkpoint.'), + description: g.s('Create a checkpoint.'), // The replication algorithm needs to create a source checkpoint, // even though it is otherwise not making any source changes. // We need to allow this method for users that don't have full @@ -736,14 +748,14 @@ module.exports = function(registry) { }); setRemoting(PersistedModel, 'currentCheckpoint', { - description: g.f('Get the current checkpoint.'), + description: g.s('Get the current checkpoint.'), accessType: 'READ', returns: {arg: 'checkpoint', type: 'object', root: true}, http: {verb: 'get', path: '/checkpoint'} }); setRemoting(PersistedModel, 'createUpdates', { - description: g.f('Create an update list from a delta list.'), + description: g.s('Create an update list from a delta list.'), // This operation is read-only, it does not change any local data. // It is called by the replication algorithm to compile a list // of changes to apply on the target. @@ -754,14 +766,14 @@ module.exports = function(registry) { }); setRemoting(PersistedModel, 'bulkUpdate', { - description: g.f('Run multiple updates at once. Note: this is not atomic.'), + description: g.s('Run multiple updates at once. Note: this is not atomic.'), accessType: 'WRITE', accepts: {arg: 'updates', type: 'array'}, http: {verb: 'post', path: '/bulk-update'} }); setRemoting(PersistedModel, 'findLastChange', { - description: g.f('Get the most recent change record for this instance.'), + description: g.s('Get the most recent change record for this instance.'), accessType: 'READ', accepts: { arg: 'id', type: 'any', required: true, http: { source: 'path' }, @@ -773,7 +785,7 @@ module.exports = function(registry) { setRemoting(PersistedModel, 'updateLastChange', { description: [ - g.f('Update the properties of the most recent change record ' + + g.s('Update the properties of the most recent change record ' + 'kept for this instance.'), ], accessType: 'WRITE', @@ -784,7 +796,7 @@ module.exports = function(registry) { }, { arg: 'data', type: 'object', http: {source: 'body'}, - description: g.f('An object of Change property name/value pairs'), + description: g.s('An object of Change property name/value pairs'), }, ], returns: { arg: 'result', type: this.Change.modelName, root: true }, @@ -810,7 +822,7 @@ module.exports = function(registry) { } setRemoting(PersistedModel, 'createChangeStream', { - description: g.f('Create a change stream.'), + description: g.s('Create a change stream.'), accessType: 'READ', http: [ {verb: 'post', path: '/change-stream'},