Skip to content
This repository has been archived by the owner on Mar 8, 2020. It is now read-only.

Commit

Permalink
Avoid recompiling the same scripts/queries/ACLs if already cached (#1675
Browse files Browse the repository at this point in the history
)
  • Loading branch information
Simon Stone authored Jul 26, 2017
1 parent 1855c58 commit cdc16f4
Show file tree
Hide file tree
Showing 3 changed files with 165 additions and 12 deletions.
48 changes: 45 additions & 3 deletions packages/composer-runtime/lib/context.js
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,20 @@ class Context {
LOG.exit(method);
}

/**
* Get a compiled script bundle from the cache.
* @param {string} businessNetworkHash The hash of the business network definition.
* @return {CompiledScriptBundle} The cached compiled script bundle, or null if
* there is no entry in the cache for the specified business network definition.
*/
static getCachedCompiledScriptBundle(businessNetworkHash) {
const method = 'getCachedCompiledScriptBundle';
LOG.entry(method, businessNetworkHash);
const result = compiledScriptBundleCache.get(businessNetworkHash);
LOG.exit(method, result);
return result;
}

/**
* Store a compiled script bundle in the cache.
* @param {string} businessNetworkHash The hash of the business network definition.
Expand All @@ -67,6 +81,20 @@ class Context {
LOG.exit(method);
}

/**
* Get a compiled query bundle from the cache.
* @param {string} businessNetworkHash The hash of the business network definition.
* @return {CompiledQueryBundle} The cached compiled query bundle, or null if
* there is no entry in the cache for the specified business network definition.
*/
static getCachedCompiledQueryBundle(businessNetworkHash) {
const method = 'getCachedCompiledQueryBundle';
LOG.entry(method, businessNetworkHash);
const result = compiledQueryBundleCache.get(businessNetworkHash);
LOG.exit(method, result);
return result;
}

/**
* Store a compiled query bundle in the cache.
* @param {string} businessNetworkHash The hash of the business network definition.
Expand All @@ -79,6 +107,20 @@ class Context {
LOG.exit(method);
}

/**
* Get a compiled ACL bundle from the cache.
* @param {string} businessNetworkHash The hash of the business network definition.
* @return {CompiledAclBundle} The cached compiled ACL bundle, or null if
* there is no entry in the cache for the specified business network definition.
*/
static getCachedCompiledAclBundle(businessNetworkHash) {
const method = 'getCachedCompiledAclBundle';
LOG.entry(method, businessNetworkHash);
const result = compiledAclBundleCache.get(businessNetworkHash);
LOG.exit(method, result);
return result;
}

/**
* Store a compiled ACL bundle in the cache.
* @param {string} businessNetworkHash The hash of the business network definition.
Expand Down Expand Up @@ -197,7 +239,7 @@ class Context {
const method = 'loadCompiledScriptBundle';
LOG.entry(method);
LOG.debug(method, 'Looking in cache for compiled script bundle', businessNetworkRecord.hash);
let compiledScriptBundle = compiledScriptBundleCache.get(businessNetworkRecord.hash);
let compiledScriptBundle = Context.getCachedCompiledScriptBundle(businessNetworkRecord.hash);
if (compiledScriptBundle) {
LOG.debug(method, 'Compiled script bundle is in cache');
return Promise.resolve(compiledScriptBundle);
Expand Down Expand Up @@ -227,7 +269,7 @@ class Context {
const method = 'loadCompiledQueryBundle';
LOG.entry(method);
LOG.debug(method, 'Looking in cache for compiled query bundle', businessNetworkRecord.hash);
let compiledQueryBundle = compiledQueryBundleCache.get(businessNetworkRecord.hash);
let compiledQueryBundle = Context.getCachedCompiledQueryBundle(businessNetworkRecord.hash);
if (compiledQueryBundle) {
LOG.debug(method, 'Compiled query bundle is in cache');
return Promise.resolve(compiledQueryBundle);
Expand Down Expand Up @@ -257,7 +299,7 @@ class Context {
const method = 'loadCompiledAclBundle';
LOG.entry(method);
LOG.debug(method, 'Looking in cache for compiled ACL bundle', businessNetworkRecord.hash);
let compiledAclBundle = compiledAclBundleCache.get(businessNetworkRecord.hash);
let compiledAclBundle = Context.getCachedCompiledAclBundle(businessNetworkRecord.hash);
if (compiledAclBundle) {
LOG.debug(method, 'Compiled ACL bundle is in cache');
return Promise.resolve(compiledAclBundle);
Expand Down
27 changes: 18 additions & 9 deletions packages/composer-runtime/lib/engine.js
Original file line number Diff line number Diff line change
Expand Up @@ -153,19 +153,28 @@ class Engine {
Context.cacheBusinessNetwork(businessNetworkHash, businessNetworkDefinition);

// Cache the compiled script bundle.
compiledScriptBundle = context.getScriptCompiler().compile(businessNetworkDefinition.getScriptManager());
LOG.debug(method, 'Loaded compiled script bundle, storing in cache');
Context.cacheCompiledScriptBundle(businessNetworkHash, compiledScriptBundle);
compiledScriptBundle = Context.getCachedCompiledScriptBundle(businessNetworkHash);
if (!compiledScriptBundle) {
compiledScriptBundle = context.getScriptCompiler().compile(businessNetworkDefinition.getScriptManager());
LOG.debug(method, 'Loaded compiled script bundle, storing in cache');
Context.cacheCompiledScriptBundle(businessNetworkHash, compiledScriptBundle);
}

// Cache the compiled query bundle.
compiledQueryBundle = context.getQueryCompiler().compile(businessNetworkDefinition.getQueryManager());
LOG.debug(method, 'Loaded compiled query bundle, storing in cache');
Context.cacheCompiledQueryBundle(businessNetworkHash, compiledQueryBundle);
compiledQueryBundle = Context.getCachedCompiledQueryBundle(businessNetworkHash);
if (!compiledQueryBundle) {
compiledQueryBundle = context.getQueryCompiler().compile(businessNetworkDefinition.getQueryManager());
LOG.debug(method, 'Loaded compiled query bundle, storing in cache');
Context.cacheCompiledQueryBundle(businessNetworkHash, compiledQueryBundle);
}

// Cache the compiled ACL bundle.
compiledAclBundle = context.getAclCompiler().compile(businessNetworkDefinition.getAclManager(), businessNetworkDefinition.getScriptManager());
LOG.debug(method, 'Loaded compiled ACL bundle, storing in cache');
Context.cacheCompiledAclBundle(businessNetworkHash, compiledAclBundle);
compiledAclBundle = Context.getCachedCompiledAclBundle(businessNetworkHash);
if (!compiledAclBundle) {
compiledAclBundle = context.getAclCompiler().compile(businessNetworkDefinition.getAclManager(), businessNetworkDefinition.getScriptManager());
LOG.debug(method, 'Loaded compiled ACL bundle, storing in cache');
Context.cacheCompiledAclBundle(businessNetworkHash, compiledAclBundle);
}

// Get the sysdata collection where the business network definition is stored.
LOG.debug(method, 'Loaded business network definition, storing in $sysdata collection');
Expand Down
102 changes: 102 additions & 0 deletions packages/composer-runtime/test/engine.js
Original file line number Diff line number Diff line change
Expand Up @@ -284,6 +284,108 @@ describe('Engine', () => {
});
});

it('should reuse the cached compiled script bundle', () => {
let sysdata = sinon.createStubInstance(DataCollection);
let sysregistries = sinon.createStubInstance(DataCollection);
mockDataService.ensureCollection.withArgs('$sysdata').resolves(sysdata);
let mockBusinessNetworkDefinition = sinon.createStubInstance(BusinessNetworkDefinition);
let mockScriptManager = sinon.createStubInstance(ScriptManager);
mockBusinessNetworkDefinition.getScriptManager.returns(mockScriptManager);
mockBusinessNetworkDefinition.getIdentifier.returns('test');
sandbox.stub(BusinessNetworkDefinition, 'fromArchive').resolves(mockBusinessNetworkDefinition);
let mockScriptCompiler = sinon.createStubInstance(ScriptCompiler);
let mockCompiledScriptBundle = sinon.createStubInstance(CompiledScriptBundle);
mockScriptCompiler.compile.throws(new Error('should not be called'));
mockContext.getScriptCompiler.returns(mockScriptCompiler);
Context.cacheCompiledScriptBundle('dc9c1c09907c36f5379d615ae61c02b46ba254d92edb77cb63bdcc5247ccd01c', mockCompiledScriptBundle);
let mockQueryCompiler = sinon.createStubInstance(QueryCompiler);
let mockCompiledQueryBundle = sinon.createStubInstance(CompiledQueryBundle);
mockQueryCompiler.compile.returns(mockCompiledQueryBundle);
mockContext.getQueryCompiler.returns(mockQueryCompiler);
let mockAclCompiler = sinon.createStubInstance(AclCompiler);
let mockCompiledAclBundle = sinon.createStubInstance(CompiledAclBundle);
mockAclCompiler.compile.returns(mockCompiledAclBundle);
mockContext.getAclCompiler.returns(mockAclCompiler);
sysdata.add.withArgs('businessnetwork', sinon.match.any).resolves();
mockDataService.ensureCollection.withArgs('$sysregistries').resolves(sysregistries);
mockRegistryManager.ensure.withArgs('Transaction', 'default', 'Default Transaction Registry').resolves();
sandbox.stub(Context, 'cacheBusinessNetwork');
sandbox.stub(Context, 'cacheCompiledScriptBundle');
mockRegistryManager.createDefaults.resolves();
return engine.init(mockContext, 'init', ['aGVsbG8gd29ybGQ=','{}'])
.then(() => {
sinon.assert.notCalled(Context.cacheCompiledScriptBundle);
});
});

it('should reuse the cached compiled query bundle', () => {
let sysdata = sinon.createStubInstance(DataCollection);
let sysregistries = sinon.createStubInstance(DataCollection);
mockDataService.ensureCollection.withArgs('$sysdata').resolves(sysdata);
let mockBusinessNetworkDefinition = sinon.createStubInstance(BusinessNetworkDefinition);
let mockScriptManager = sinon.createStubInstance(ScriptManager);
mockBusinessNetworkDefinition.getScriptManager.returns(mockScriptManager);
mockBusinessNetworkDefinition.getIdentifier.returns('test');
sandbox.stub(BusinessNetworkDefinition, 'fromArchive').resolves(mockBusinessNetworkDefinition);
let mockScriptCompiler = sinon.createStubInstance(ScriptCompiler);
let mockCompiledScriptBundle = sinon.createStubInstance(CompiledScriptBundle);
mockScriptCompiler.compile.returns(mockCompiledScriptBundle);
mockContext.getScriptCompiler.returns(mockScriptCompiler);
let mockQueryCompiler = sinon.createStubInstance(QueryCompiler);
let mockCompiledQueryBundle = sinon.createStubInstance(CompiledQueryBundle);
mockQueryCompiler.compile.throws(new Error('should not be called'));
mockContext.getQueryCompiler.returns(mockQueryCompiler);
Context.cacheCompiledQueryBundle('dc9c1c09907c36f5379d615ae61c02b46ba254d92edb77cb63bdcc5247ccd01c', mockCompiledQueryBundle);
let mockAclCompiler = sinon.createStubInstance(AclCompiler);
let mockCompiledAclBundle = sinon.createStubInstance(CompiledAclBundle);
mockAclCompiler.compile.returns(mockCompiledAclBundle);
mockContext.getAclCompiler.returns(mockAclCompiler);
sysdata.add.withArgs('businessnetwork', sinon.match.any).resolves();
mockDataService.ensureCollection.withArgs('$sysregistries').resolves(sysregistries);
mockRegistryManager.ensure.withArgs('Transaction', 'default', 'Default Transaction Registry').resolves();
sandbox.stub(Context, 'cacheBusinessNetwork');
sandbox.stub(Context, 'cacheCompiledQueryBundle');
mockRegistryManager.createDefaults.resolves();
return engine.init(mockContext, 'init', ['aGVsbG8gd29ybGQ=','{}'])
.then(() => {
sinon.assert.notCalled(Context.cacheCompiledQueryBundle);
});
});

it('should reuse the cached compiled ACL bundle', () => {
let sysdata = sinon.createStubInstance(DataCollection);
let sysregistries = sinon.createStubInstance(DataCollection);
mockDataService.ensureCollection.withArgs('$sysdata').resolves(sysdata);
let mockBusinessNetworkDefinition = sinon.createStubInstance(BusinessNetworkDefinition);
let mockScriptManager = sinon.createStubInstance(ScriptManager);
mockBusinessNetworkDefinition.getScriptManager.returns(mockScriptManager);
mockBusinessNetworkDefinition.getIdentifier.returns('test');
sandbox.stub(BusinessNetworkDefinition, 'fromArchive').resolves(mockBusinessNetworkDefinition);
let mockScriptCompiler = sinon.createStubInstance(ScriptCompiler);
let mockCompiledScriptBundle = sinon.createStubInstance(CompiledScriptBundle);
mockScriptCompiler.compile.returns(mockCompiledScriptBundle);
mockContext.getScriptCompiler.returns(mockScriptCompiler);
let mockQueryCompiler = sinon.createStubInstance(QueryCompiler);
let mockCompiledQueryBundle = sinon.createStubInstance(CompiledQueryBundle);
mockQueryCompiler.compile.returns(mockCompiledQueryBundle);
mockContext.getQueryCompiler.returns(mockQueryCompiler);
let mockAclCompiler = sinon.createStubInstance(AclCompiler);
let mockCompiledAclBundle = sinon.createStubInstance(CompiledAclBundle);
mockAclCompiler.compile.throws(new Error('should not be called'));
mockContext.getAclCompiler.returns(mockAclCompiler);
Context.cacheCompiledAclBundle('dc9c1c09907c36f5379d615ae61c02b46ba254d92edb77cb63bdcc5247ccd01c', mockCompiledAclBundle);
sysdata.add.withArgs('businessnetwork', sinon.match.any).resolves();
mockDataService.ensureCollection.withArgs('$sysregistries').resolves(sysregistries);
mockRegistryManager.ensure.withArgs('Transaction', 'default', 'Default Transaction Registry').resolves();
sandbox.stub(Context, 'cacheBusinessNetwork');
sandbox.stub(Context, 'cacheCompiledAclBundle');
mockRegistryManager.createDefaults.resolves();
return engine.init(mockContext, 'init', ['aGVsbG8gd29ybGQ=','{}'])
.then(() => {
sinon.assert.notCalled(Context.cacheCompiledAclBundle);
});
});

it('should throw if an error occurs', () => {
let mockDataCollection = sinon.createStubInstance(DataCollection);
mockDataService.getCollection.rejects();
Expand Down

0 comments on commit cdc16f4

Please sign in to comment.