fix(multip-app-manager): init multiple Application instances while starting up a sub app (#1986)
* fix: init multiple Application instances while starting up sub app by Jacob * fix: import error by Jacob * feat: use async-mutex for locking process by Jacob * revert: unused update by Jacob * test: should get same obj ref when asynchronously access with same sub app name by Jacob * feat: add async-mutex dependency to multi-app-manager package.json by Jacob
This commit is contained in:
parent
2c9ce09304
commit
bc00bd161a
@ -9,7 +9,8 @@
|
||||
"main": "./lib/index.js",
|
||||
"types": "./lib/index.d.ts",
|
||||
"dependencies": {
|
||||
"@nocobase/server": "0.9.4-alpha.2"
|
||||
"@nocobase/server": "0.9.4-alpha.2",
|
||||
"async-mutex": "^0.3.2"
|
||||
},
|
||||
"gitHead": "ce588eefb0bfc50f7d5bbee575e0b5e843bf6644"
|
||||
}
|
||||
|
@ -172,4 +172,38 @@ describe('multiple apps create', () => {
|
||||
await app.start();
|
||||
expect(app.appManager.applications.get(subAppName)).toBeDefined();
|
||||
});
|
||||
|
||||
it('should get same obj ref when asynchronously access with same sub app name', async () => {
|
||||
const subAppName = `t_${uid()}`;
|
||||
|
||||
const subApp = await app.db.getRepository('applications').create({
|
||||
values: {
|
||||
name: subAppName,
|
||||
options: {},
|
||||
},
|
||||
});
|
||||
|
||||
await app.appManager.removeApplication(subAppName);
|
||||
expect(app.appManager.applications.get(subAppName)).toBeUndefined();
|
||||
|
||||
const instances = [];
|
||||
|
||||
app.on('afterSubAppAdded', (subApp) => {
|
||||
instances.push(subApp);
|
||||
});
|
||||
|
||||
const promises = [];
|
||||
for (let i = 0; i < 3; i++) {
|
||||
promises.push(
|
||||
(async () => {
|
||||
await app.appManager.getApplication(subAppName);
|
||||
})(),
|
||||
);
|
||||
}
|
||||
await Promise.all(promises);
|
||||
|
||||
expect(instances.length).toBe(1);
|
||||
expect(instances[0]).toBeDefined();
|
||||
expect(instances[0].name == subAppName).toBeTruthy();
|
||||
});
|
||||
});
|
||||
|
@ -4,6 +4,7 @@ import lodash from 'lodash';
|
||||
import * as path from 'path';
|
||||
import { resolve } from 'path';
|
||||
import { ApplicationModel } from './models/application';
|
||||
import { Mutex } from 'async-mutex';
|
||||
|
||||
export type AppDbCreator = (app: Application, transaction?: Transactionable) => Promise<void>;
|
||||
export type AppOptionsFactory = (appName: string, mainApp: Application) => any;
|
||||
@ -69,6 +70,8 @@ export class PluginMultiAppManager extends Plugin {
|
||||
appDbCreator: AppDbCreator = defaultDbCreator;
|
||||
appOptionsFactory: AppOptionsFactory = defaultAppOptionsFactory;
|
||||
|
||||
private beforeGetApplicationMutex = new Mutex();
|
||||
|
||||
setAppOptionsFactory(factory: AppOptionsFactory) {
|
||||
this.appOptionsFactory = factory;
|
||||
}
|
||||
@ -150,35 +153,37 @@ export class PluginMultiAppManager extends Plugin {
|
||||
this.app.on(
|
||||
'beforeGetApplication',
|
||||
async ({ appManager, name, options }: { appManager: AppManager; name: string; options: any }) => {
|
||||
if (appManager.applications.has(name)) {
|
||||
return;
|
||||
}
|
||||
await this.beforeGetApplicationMutex.runExclusive(async () => {
|
||||
if (appManager.applications.has(name)) {
|
||||
return;
|
||||
}
|
||||
|
||||
const applicationRecord = (await this.app.db.getRepository('applications').findOne({
|
||||
filter: {
|
||||
name,
|
||||
},
|
||||
})) as ApplicationModel | null;
|
||||
const applicationRecord = (await this.app.db.getRepository('applications').findOne({
|
||||
filter: {
|
||||
name,
|
||||
},
|
||||
})) as ApplicationModel | null;
|
||||
|
||||
const instanceOptions = applicationRecord.get('options');
|
||||
const instanceOptions = applicationRecord.get('options');
|
||||
|
||||
// skip standalone deployment application
|
||||
if (instanceOptions?.standaloneDeployment && appManager.runningMode !== 'single') {
|
||||
return;
|
||||
}
|
||||
// skip standalone deployment application
|
||||
if (instanceOptions?.standaloneDeployment && appManager.runningMode !== 'single') {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!applicationRecord) {
|
||||
return;
|
||||
}
|
||||
if (!applicationRecord) {
|
||||
return;
|
||||
}
|
||||
|
||||
const subApp = await applicationRecord.registerToMainApp(this.app, {
|
||||
appOptionsFactory: this.appOptionsFactory,
|
||||
const subApp = await applicationRecord.registerToMainApp(this.app, {
|
||||
appOptionsFactory: this.appOptionsFactory,
|
||||
});
|
||||
|
||||
// must skip load on upgrade
|
||||
if (!options?.upgrading) {
|
||||
await subApp.load();
|
||||
}
|
||||
});
|
||||
|
||||
// must skip load on upgrade
|
||||
if (!options?.upgrading) {
|
||||
await subApp.load();
|
||||
}
|
||||
},
|
||||
);
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user