Skip to content

Commit e6ed719

Browse files
authored
feat(compass-shell): Integrate worker thread runtime in compass shell COMPASS-4556 (#538)
* feat(node-runtime-worker-thread): Allow to configure spawn options of the child process created by worker runtime * chore(compass-shell): Update electron dev environment to match current compass better * feat(compass-shell): Integrate worker runtime with compass shell, hide it behind an env varibable * fix(node-runtime-worker-thread): Add missing type import
1 parent 264b190 commit e6ed719

File tree

7 files changed

+115
-68
lines changed

7 files changed

+115
-68
lines changed

packages/compass-shell/electron/index.js

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,12 @@ if ( process.defaultApp || /[\\/]electron-prebuilt[\\/]/.test(process.execPath)
1616
function createWindow() {
1717
// Create the browser window.
1818
mainWindow = new BrowserWindow({
19-
width: 1024, height: 768, show: false
19+
width: 1024,
20+
height: 768,
21+
show: false,
22+
webPreferences: {
23+
nodeIntegration: true,
24+
},
2025
});
2126

2227
// and load the index.html of the app.

packages/compass-shell/electron/renderer/index.js

Lines changed: 34 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -58,22 +58,41 @@ render(CompassShellPlugin);
5858
// mongoclient is. For that reason we can use a mocked one, this avoid
5959
// the dependency to keytar:
6060
//
61-
const localUri = 'mongodb://localhost:27017/test';
62-
MongoClient.connect(localUri, { useNewUrlParser: true, useUnifiedTopology: true }, (error, client) => {
63-
const dataService = { client: { client } };
64-
appRegistry.emit('data-service-initialized', dataService);
65-
appRegistry.emit('data-service-connected', error, dataService);
66-
67-
if (error) {
68-
console.error('Unable to connect to', localUri, error);
69-
return;
70-
}
71-
72-
console.info('Connected to', localUri);
61+
const connectionOptions = {
62+
url: 'mongodb://localhost:27020/test?readPreference=primary&ssl=false',
63+
options: {
64+
useNewUrlParser: true,
65+
useUnifiedTopology: true,
66+
connectWithNoPrimary: true,
67+
sslValidate: false,
68+
},
69+
};
7370

74-
appRegistry.emit('data-service-initialized', dataService);
75-
appRegistry.emit('data-service-connected', error, dataService);
76-
});
71+
MongoClient.connect(
72+
connectionOptions.url,
73+
connectionOptions.options,
74+
(error, client) => {
75+
const dataService = {
76+
client: { client },
77+
getConnectionOptions() {
78+
return connectionOptions;
79+
},
80+
};
81+
82+
appRegistry.emit('data-service-initialized', dataService);
83+
appRegistry.emit('data-service-connected', error, dataService);
84+
85+
if (error) {
86+
console.error('Unable to connect to', connectionOptions.url, error);
87+
return;
88+
}
89+
90+
console.info('Connected to', connectionOptions.url);
91+
92+
appRegistry.emit('data-service-initialized', dataService);
93+
appRegistry.emit('data-service-connected', error, dataService);
94+
}
95+
);
7796

7897
if (module.hot) {
7998
module.hot.accept('plugin', () => render(CompassShellPlugin));

packages/compass-shell/package-lock.json

Lines changed: 4 additions & 4 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

packages/compass-shell/package.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,7 @@
5656
"@leafygreen-ui/icon-button": "^5.0.2",
5757
"@mongosh/browser-repl": "0.0.0-dev.0",
5858
"@mongosh/browser-runtime-electron": "0.0.0-dev.0",
59+
"@mongosh/node-runtime-worker-thread": "0.0.0-dev.0",
5960
"@mongosh/service-provider-server": "0.0.0-dev.0",
6061
"hadron-react-buttons": "^4.0.4",
6162
"mongodb-redux-common": "0.0.2"
@@ -93,7 +94,7 @@
9394
"cross-env": "^5.0.1",
9495
"css-loader": "^0.28.1",
9596
"debug": "^3.0.1",
96-
"electron": "^3.1.13",
97+
"electron": "^6.1.7",
9798
"electron-packager": "^8.7.0",
9899
"electron-rebuild": "^1.10.0",
99100
"enzyme": "^3.11.0",

packages/compass-shell/src/modules/runtime.js

Lines changed: 18 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import { ElectronRuntime } from '@mongosh/browser-runtime-electron';
22
import { CompassServiceProvider } from '@mongosh/service-provider-server';
3+
import { WorkerRuntime } from '@mongosh/node-runtime-worker-thread';
34

45
/**
56
* The prefix.
@@ -38,17 +39,29 @@ export default function reducer(state = INITIAL_STATE, action) {
3839

3940
function reduceSetupRuntime(state, action) {
4041
if (action.error || !action.dataService) {
41-
return {error: action.error, dataService: null, runtime: null};
42+
return { error: action.error, dataService: null, runtime: null };
4243
}
4344

4445
if (state.dataService === action.dataService) {
4546
return state;
4647
}
4748

48-
const runtime = new ElectronRuntime(
49-
CompassServiceProvider.fromDataService(action.dataService),
50-
action.appRegistry
51-
);
49+
const { url, options } = action.dataService.getConnectionOptions();
50+
51+
const runtime = !!process.env.COMPASS_SHELL_EXPERIMENTAL_WORKER_RUNTIME
52+
? new WorkerRuntime(
53+
url,
54+
options,
55+
{},
56+
{
57+
env: { ...process.env, ELECTRON_RUN_AS_NODE: 1 },
58+
serialization: 'advanced',
59+
}
60+
)
61+
: new ElectronRuntime(
62+
CompassServiceProvider.fromDataService(action.dataService),
63+
action.appRegistry
64+
);
5265

5366
return {
5467
error: action.error,
Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
import { ChildProcess } from 'child_process';
2+
import { EvaluationListener } from '@mongosh/shell-evaluator';
3+
import { exposeAll, WithClose } from './rpc';
4+
import type { WorkerRuntime } from './index';
5+
6+
export class ChildProcessEvaluationListener {
7+
exposedListener: WithClose<EvaluationListener>;
8+
9+
constructor(workerRuntime: WorkerRuntime, childProcess: ChildProcess) {
10+
this.exposedListener = exposeAll<EvaluationListener>(
11+
{
12+
onPrompt(question, type) {
13+
return (
14+
workerRuntime.evaluationListener?.onPrompt?.(question, type) ?? ''
15+
);
16+
},
17+
onPrint(values) {
18+
return workerRuntime.evaluationListener?.onPrint?.(values);
19+
},
20+
toggleTelemetry(enabled) {
21+
return workerRuntime.evaluationListener?.toggleTelemetry?.(enabled);
22+
},
23+
onClearCommand() {
24+
return workerRuntime.evaluationListener?.onClearCommand?.();
25+
},
26+
onExit() {
27+
return (
28+
workerRuntime.evaluationListener?.onExit?.() ??
29+
(Promise.resolve() as Promise<never>)
30+
);
31+
}
32+
},
33+
childProcess
34+
);
35+
}
36+
}

packages/node-runtime-worker-thread/src/index.ts

Lines changed: 15 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -1,59 +1,28 @@
11
/* istanbul ignore file */
22
/* ^^^ we test the dist directly, so isntanbul can't calculate the coverage correctly */
33

4-
import { ChildProcess } from 'child_process';
4+
import { ChildProcess, SpawnOptionsWithoutStdio } from 'child_process';
55
import { MongoClientOptions } from '@mongosh/service-provider-core';
66
import { Runtime } from '@mongosh/browser-runtime-core';
77
import { EvaluationListener } from '@mongosh/shell-evaluator';
88
import spawnChildFromSource, { kill } from './spawn-child-from-source';
9-
import { Caller, createCaller, exposeAll, WithClose } from './rpc';
9+
import { Caller, createCaller } from './rpc';
10+
import { ChildProcessEvaluationListener } from './child-process-evaluation-listener';
1011
import type { WorkerRuntime as WorkerThreadWorkerRuntime } from './worker-runtime';
1112
import childProcessProxySrc from 'inline-entry-loader!./child-process-proxy';
1213

1314
type ChildProcessRuntime = Caller<WorkerThreadWorkerRuntime>;
14-
15-
class WorkerEvaluationListener {
16-
exposedListener: WithClose<EvaluationListener>;
17-
18-
constructor(workerRuntime: WorkerRuntime, childProcess: ChildProcess) {
19-
this.exposedListener = exposeAll<EvaluationListener>(
20-
{
21-
onPrompt(question, type) {
22-
return (
23-
workerRuntime.evaluationListener?.onPrompt?.(question, type) ?? ''
24-
);
25-
},
26-
onPrint(values) {
27-
return workerRuntime.evaluationListener?.onPrint?.(values);
28-
},
29-
toggleTelemetry(enabled) {
30-
return workerRuntime.evaluationListener?.toggleTelemetry?.(enabled);
31-
},
32-
onClearCommand() {
33-
return workerRuntime.evaluationListener?.onClearCommand?.();
34-
},
35-
onExit() {
36-
return (
37-
workerRuntime.evaluationListener?.onExit?.() ??
38-
(Promise.resolve() as Promise<never>)
39-
);
40-
}
41-
},
42-
childProcess
43-
);
44-
}
45-
}
46-
4715
class WorkerRuntime implements Runtime {
4816
private initOptions: {
4917
uri: string;
5018
driverOptions: MongoClientOptions;
5119
cliOptions: { nodb?: boolean };
20+
spawnOptions: SpawnOptionsWithoutStdio;
5221
};
5322

5423
evaluationListener: EvaluationListener | null = null;
5524

56-
private childProcessEvaluationListener!: WorkerEvaluationListener;
25+
private childProcessEvaluationListener!: ChildProcessEvaluationListener;
5726

5827
private childProcess!: ChildProcess;
5928

@@ -64,27 +33,31 @@ class WorkerRuntime implements Runtime {
6433
constructor(
6534
uri: string,
6635
driverOptions: MongoClientOptions = {},
67-
cliOptions: { nodb?: boolean } = {}
36+
cliOptions: { nodb?: boolean } = {},
37+
spawnOptions: SpawnOptionsWithoutStdio = {}
6838
) {
69-
this.initOptions = { uri, driverOptions, cliOptions };
39+
this.initOptions = { uri, driverOptions, cliOptions, spawnOptions };
7040
this.initWorkerPromise = this.initWorker();
7141
}
7242

7343
private async initWorker() {
74-
this.childProcess = await spawnChildFromSource(childProcessProxySrc);
44+
const { uri, driverOptions, cliOptions, spawnOptions } = this.initOptions;
45+
46+
this.childProcess = await spawnChildFromSource(
47+
childProcessProxySrc,
48+
spawnOptions
49+
);
7550

7651
this.childProcessRuntime = createCaller(
7752
['init', 'evaluate', 'getCompletions', 'setEvaluationListener'],
7853
this.childProcess
7954
);
8055

81-
this.childProcessEvaluationListener = new WorkerEvaluationListener(
56+
this.childProcessEvaluationListener = new ChildProcessEvaluationListener(
8257
this,
8358
this.childProcess
8459
);
8560

86-
const { uri, driverOptions, cliOptions } = this.initOptions;
87-
8861
await this.childProcessRuntime.init(uri, driverOptions, cliOptions);
8962
}
9063

0 commit comments

Comments
 (0)