mysql-memory-server 1.2.0 → 1.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +40 -9
- package/dist/src/constants.d.ts +4 -0
- package/dist/src/constants.js +6 -0
- package/dist/src/index.js +17 -3
- package/dist/src/libraries/Downloader.js +22 -24
- package/dist/src/libraries/Executor.js +114 -14
- package/dist/src/libraries/FileLock.d.ts +2 -0
- package/dist/src/libraries/FileLock.js +25 -0
- package/dist/types/index.d.ts +6 -0
- package/package.json +5 -5
package/README.md
CHANGED
|
@@ -10,15 +10,20 @@ Download with your package manager of choice. The package name is `mysql-memory-
|
|
|
10
10
|
|
|
11
11
|
#### Requirements
|
|
12
12
|
|
|
13
|
-
- Node.js 16
|
|
14
|
-
- macOS 13+, Windows, or Linux (
|
|
13
|
+
- Node.js 16.6.0 and newer
|
|
14
|
+
- macOS 13+, Windows, or Linux (This package is only tested on Ubuntu 20.04, 22.04, 24.04, and Fedora 40. Other Linux distributions may or may not work at this time.)
|
|
15
15
|
|
|
16
16
|
Windows only requirements:
|
|
17
17
|
- `Microsoft Visual C++ 2019 Redistributable Package` needs to be installed
|
|
18
18
|
|
|
19
19
|
Linux only requirements:
|
|
20
|
-
- The `libaio1` package needs to be installed
|
|
21
|
-
-
|
|
20
|
+
- The `libaio1` or `libaio1t64` package needs to be installed
|
|
21
|
+
- If `libaio1` is not available but `libaio1t64` is, the `ldconfig` command needs to be available to run
|
|
22
|
+
- The `tar` package needs to be installed if you want to use MySQL versions that aren't system installed
|
|
23
|
+
|
|
24
|
+
Currently supported MySQL versions:
|
|
25
|
+
- If using the system installed MySQL server: 8.0.20 and newer
|
|
26
|
+
- If not using the system installed MySQL server: 8.0.39, 8.1.0, 8.2.0, 8.3.0, 8.4.2, 9.0.1
|
|
22
27
|
|
|
23
28
|
## Usage
|
|
24
29
|
|
|
@@ -72,7 +77,13 @@ Default: undefined
|
|
|
72
77
|
|
|
73
78
|
Description: Version of MySQL to use for the database. Uses semver for getting the version, so valid semver versions are allowed. For example, `8.x` is a valid version and will use the latest 8.x MySQL version.
|
|
74
79
|
|
|
75
|
-
If left undefined
|
|
80
|
+
If left undefined:
|
|
81
|
+
- If the system has MySQL installed, the system-installed version will be used. If the installed version is not supported by this package (currently <8.0.20), an error will be thrown unless `ignoreUnsupportedSystemVersion` is set to `true`.
|
|
82
|
+
- If the system does not have MySQL installed, the latest version of MySQL in the `versions.json` file in this package will be downloaded.
|
|
83
|
+
|
|
84
|
+
If defined:
|
|
85
|
+
- If the version is 8.0.19 or older, an error will be thrown as this package does not currently support those versions of MySQL.
|
|
86
|
+
- If MySQL is installed on the system, the installed version will be used. Otherwise the selected version will be downloaded from the MySQL CDN as long as it can be found in the `versions.json` file. If it cannot be found in that file, an error will be thrown.
|
|
76
87
|
|
|
77
88
|
- `dbName: string`
|
|
78
89
|
|
|
@@ -132,6 +143,30 @@ Default: root
|
|
|
132
143
|
|
|
133
144
|
Description: The username of the user that is used to login to the database.
|
|
134
145
|
|
|
146
|
+
- `port: number`
|
|
147
|
+
|
|
148
|
+
Required: No
|
|
149
|
+
|
|
150
|
+
Default: 0
|
|
151
|
+
|
|
152
|
+
Description: The port that the database will listen on. If set to 0, a randomly generated port is used.
|
|
153
|
+
|
|
154
|
+
- `xPort: number`
|
|
155
|
+
|
|
156
|
+
Required: No
|
|
157
|
+
|
|
158
|
+
Default: 0
|
|
159
|
+
|
|
160
|
+
Description: The port that the MySQL X Plugin will listen on. If set to 0, a randomly generated port is used.
|
|
161
|
+
|
|
162
|
+
- `ignoreUnsupportedSystemVersion: boolean`
|
|
163
|
+
|
|
164
|
+
Required: No
|
|
165
|
+
|
|
166
|
+
Default: false
|
|
167
|
+
|
|
168
|
+
Description: This option only applies if the system-installed MySQL version is lower than the oldest supported MySQL version for this package (8.0.20) and the `version` option is not defined. If set to `true`, this package will use the latest version of MySQL instead of the system-installed version. If `false`, the package will throw an error.
|
|
169
|
+
|
|
135
170
|
- `deleteDBAfterStopped: boolean`
|
|
136
171
|
|
|
137
172
|
Required: No
|
|
@@ -151,7 +186,3 @@ Default: `TMPDIR/mysqlmsn/dbs/UUID` (replacing TMPDIR with the OS temp directory
|
|
|
151
186
|
Gotchas: This option is intended to be for internal debugging purposes only and not meant for people to use. As such, this option will not follow Semantic Versioning.
|
|
152
187
|
|
|
153
188
|
Description: The folder to store database-related data in
|
|
154
|
-
|
|
155
|
-
## If using Ubuntu 24.04 and newer
|
|
156
|
-
|
|
157
|
-
Selecting what MySQL version to use is not currently supported on Ubuntu 24.04 and newer. To use this package on Ubuntu 24.04 and newer you must have the `mysql-server` package installed on your system and `ServerOptions.version` must either be the version that is installed on the system or undefined.
|
package/dist/src/index.js
CHANGED
|
@@ -37,6 +37,7 @@ const versions_json_1 = __importDefault(require("./versions.json"));
|
|
|
37
37
|
const Downloader_1 = require("./libraries/Downloader");
|
|
38
38
|
const crypto_1 = require("crypto");
|
|
39
39
|
const path_1 = require("path");
|
|
40
|
+
const constants_1 = __importDefault(require("./constants"));
|
|
40
41
|
process.on('exit', () => {
|
|
41
42
|
AbortSignal_1.default.abort('Process is exiting');
|
|
42
43
|
});
|
|
@@ -51,14 +52,27 @@ async function createDB(opts) {
|
|
|
51
52
|
username: 'root',
|
|
52
53
|
deleteDBAfterStopped: true,
|
|
53
54
|
//mysqlmsn = MySQL Memory Server Node.js
|
|
54
|
-
dbPath: (0, path_1.normalize)(`${os.tmpdir()}/mysqlmsn/dbs/${(0, crypto_1.randomUUID)().replace(/-/g, '')}`)
|
|
55
|
+
dbPath: (0, path_1.normalize)(`${os.tmpdir()}/mysqlmsn/dbs/${(0, crypto_1.randomUUID)().replace(/-/g, '')}`),
|
|
56
|
+
ignoreUnsupportedSystemVersion: false,
|
|
57
|
+
port: 0,
|
|
58
|
+
xPort: 0
|
|
55
59
|
};
|
|
56
60
|
const options = { ...defaultOptions, ...opts };
|
|
57
61
|
const logger = new Logger_1.default(options.logLevel);
|
|
58
62
|
const executor = new Executor_1.default(logger);
|
|
59
63
|
const version = await executor.getMySQLVersion(options.version);
|
|
64
|
+
const unsupportedMySQLIsInstalled = version && (0, semver_1.lt)(version.version, constants_1.default.MIN_SUPPORTED_MYSQL);
|
|
65
|
+
const throwUnsupportedError = unsupportedMySQLIsInstalled && !options.ignoreUnsupportedSystemVersion && !options.version;
|
|
66
|
+
if (throwUnsupportedError) {
|
|
67
|
+
throw `A version of MySQL is installed on your system that is not supported by this package. If you want to download a MySQL binary instead of getting this error, please set the option "ignoreUnsupportedSystemVersion" to true.`;
|
|
68
|
+
}
|
|
69
|
+
if (options.version && (0, semver_1.lt)(options.version, constants_1.default.MIN_SUPPORTED_MYSQL)) {
|
|
70
|
+
//The difference between the throw here and the throw above is this throw is because the selected "version" is not supported.
|
|
71
|
+
//The throw above is because the system-installed MySQL is out of date and "ignoreUnsupportedSystemVersion" is not set to true.
|
|
72
|
+
throw `The selected version of MySQL (${options.version}) is not currently supported by this package. Please choose a different version to use.`;
|
|
73
|
+
}
|
|
60
74
|
logger.log('Version currently installed:', version);
|
|
61
|
-
if (version === null || (options.version && !(0, semver_1.satisfies)(version.version, options.version))) {
|
|
75
|
+
if (version === null || (options.version && !(0, semver_1.satisfies)(version.version, options.version)) || unsupportedMySQLIsInstalled) {
|
|
62
76
|
let binaryInfo;
|
|
63
77
|
let binaryFilepath;
|
|
64
78
|
try {
|
|
@@ -76,7 +90,7 @@ async function createDB(opts) {
|
|
|
76
90
|
binaryFilepath = await (0, Downloader_1.downloadBinary)(binaryInfo, options, logger);
|
|
77
91
|
}
|
|
78
92
|
catch (error) {
|
|
79
|
-
logger.error('Failed to download binary');
|
|
93
|
+
logger.error('Failed to download binary:', error);
|
|
80
94
|
throw `Failed to download binary. The error was: "${error}"`;
|
|
81
95
|
}
|
|
82
96
|
logger.log('Running downloaded binary');
|
|
@@ -37,6 +37,7 @@ const path_1 = require("path");
|
|
|
37
37
|
const crypto_1 = require("crypto");
|
|
38
38
|
const child_process_1 = require("child_process");
|
|
39
39
|
const proper_lockfile_1 = require("proper-lockfile");
|
|
40
|
+
const FileLock_1 = require("./FileLock");
|
|
40
41
|
function getZipData(entry) {
|
|
41
42
|
return new Promise((resolve, reject) => {
|
|
42
43
|
entry.getDataAsync((data, err) => {
|
|
@@ -157,27 +158,6 @@ function extractBinary(url, archiveLocation, extractedLocation) {
|
|
|
157
158
|
});
|
|
158
159
|
});
|
|
159
160
|
}
|
|
160
|
-
function waitForLock(path, options) {
|
|
161
|
-
return new Promise(async (resolve, reject) => {
|
|
162
|
-
let retries = 0;
|
|
163
|
-
while (retries <= options.lockRetries) {
|
|
164
|
-
retries++;
|
|
165
|
-
try {
|
|
166
|
-
const locked = (0, proper_lockfile_1.checkSync)(path);
|
|
167
|
-
if (!locked) {
|
|
168
|
-
return resolve();
|
|
169
|
-
}
|
|
170
|
-
else {
|
|
171
|
-
await new Promise(resolve => setTimeout(resolve, options.lockRetryWait));
|
|
172
|
-
}
|
|
173
|
-
}
|
|
174
|
-
catch (e) {
|
|
175
|
-
return reject(e);
|
|
176
|
-
}
|
|
177
|
-
}
|
|
178
|
-
reject(`lockRetries has been exceeded. Lock had not been released after ${options.lockRetryWait} * ${options.lockRetries} milliseconds.`);
|
|
179
|
-
});
|
|
180
|
-
}
|
|
181
161
|
function downloadBinary(binaryInfo, options, logger) {
|
|
182
162
|
return new Promise(async (resolve, reject) => {
|
|
183
163
|
const { url, version } = binaryInfo;
|
|
@@ -190,13 +170,15 @@ function downloadBinary(binaryInfo, options, logger) {
|
|
|
190
170
|
const extractedPath = `${dirpath}/${version}`;
|
|
191
171
|
await fsPromises.mkdir(extractedPath, { recursive: true });
|
|
192
172
|
const binaryPath = (0, path_1.normalize)(`${extractedPath}/mysql/bin/mysqld${process.platform === 'win32' ? '.exe' : ''}`);
|
|
173
|
+
const archivePath = `${dirpath}/${version}.${fileExtension}`;
|
|
193
174
|
const binaryExists = fs.existsSync(binaryPath);
|
|
194
175
|
if (binaryExists) {
|
|
195
176
|
return resolve(binaryPath);
|
|
196
177
|
}
|
|
178
|
+
let lockedByUs = false;
|
|
197
179
|
try {
|
|
198
180
|
(0, proper_lockfile_1.lockSync)(extractedPath);
|
|
199
|
-
|
|
181
|
+
lockedByUs = true;
|
|
200
182
|
await downloadFromCDN(url, archivePath, logger);
|
|
201
183
|
await extractBinary(url, archivePath, extractedPath);
|
|
202
184
|
try {
|
|
@@ -208,12 +190,28 @@ function downloadBinary(binaryInfo, options, logger) {
|
|
|
208
190
|
return resolve(binaryPath);
|
|
209
191
|
}
|
|
210
192
|
catch (e) {
|
|
211
|
-
if (String(e)
|
|
193
|
+
if (String(e).includes('Lock file is already being held')) {
|
|
212
194
|
logger.log('Waiting for lock for MySQL version', version);
|
|
213
|
-
await waitForLock(extractedPath, options);
|
|
195
|
+
await (0, FileLock_1.waitForLock)(extractedPath, options);
|
|
214
196
|
logger.log('Lock is gone for version', version);
|
|
215
197
|
return resolve(binaryPath);
|
|
216
198
|
}
|
|
199
|
+
if (lockedByUs) {
|
|
200
|
+
try {
|
|
201
|
+
await Promise.all([
|
|
202
|
+
fsPromises.rm(extractedPath, { force: true, recursive: true }),
|
|
203
|
+
fsPromises.rm(archivePath, { force: true, recursive: true })
|
|
204
|
+
]);
|
|
205
|
+
}
|
|
206
|
+
finally {
|
|
207
|
+
try {
|
|
208
|
+
(0, proper_lockfile_1.unlockSync)(extractedPath, { realpath: false });
|
|
209
|
+
}
|
|
210
|
+
catch (e) {
|
|
211
|
+
logger.error('An error occurred while unlocking path:', e);
|
|
212
|
+
}
|
|
213
|
+
}
|
|
214
|
+
}
|
|
217
215
|
return reject(e);
|
|
218
216
|
}
|
|
219
217
|
}
|
|
@@ -30,7 +30,7 @@ var __classPrivateFieldGet = (this && this.__classPrivateFieldGet) || function (
|
|
|
30
30
|
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
31
31
|
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
32
32
|
};
|
|
33
|
-
var _Executor_instances, _Executor_execute, _Executor_killProcess, _Executor_startMySQLProcess, _Executor_setupDataDirectories;
|
|
33
|
+
var _Executor_instances, _Executor_execute, _Executor_executeFile, _Executor_killProcess, _Executor_startMySQLProcess, _Executor_setupDataDirectories;
|
|
34
34
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
35
35
|
const child_process_1 = require("child_process");
|
|
36
36
|
const semver_1 = require("semver");
|
|
@@ -40,6 +40,8 @@ const fs = __importStar(require("fs"));
|
|
|
40
40
|
const Port_1 = require("./Port");
|
|
41
41
|
const AbortSignal_1 = __importDefault(require("./AbortSignal"));
|
|
42
42
|
const path_1 = require("path");
|
|
43
|
+
const proper_lockfile_1 = require("proper-lockfile");
|
|
44
|
+
const FileLock_1 = require("./FileLock");
|
|
43
45
|
class Executor {
|
|
44
46
|
constructor(logger) {
|
|
45
47
|
_Executor_instances.add(this);
|
|
@@ -131,9 +133,10 @@ class Executor {
|
|
|
131
133
|
let retries = 0;
|
|
132
134
|
const datadir = (0, path_1.normalize)(`${options.dbPath}/data`);
|
|
133
135
|
do {
|
|
134
|
-
await __classPrivateFieldGet(this, _Executor_instances, "m", _Executor_setupDataDirectories).call(this, options, binaryFilepath, datadir);
|
|
135
|
-
|
|
136
|
-
const
|
|
136
|
+
await __classPrivateFieldGet(this, _Executor_instances, "m", _Executor_setupDataDirectories).call(this, options, binaryFilepath, datadir, true);
|
|
137
|
+
this.logger.log('Setting up directories was successful');
|
|
138
|
+
const port = options.port || (0, Port_1.GenerateRandomPort)();
|
|
139
|
+
const mySQLXPort = options.xPort || (0, Port_1.GenerateRandomPort)();
|
|
137
140
|
this.logger.log('Using port:', port, 'and MySQLX port:', mySQLXPort, 'on retry:', retries);
|
|
138
141
|
try {
|
|
139
142
|
this.logger.log('Starting MySQL process');
|
|
@@ -164,6 +167,12 @@ _Executor_instances = new WeakSet(), _Executor_execute = function _Executor_exec
|
|
|
164
167
|
resolve({ error, stdout, stderr });
|
|
165
168
|
});
|
|
166
169
|
});
|
|
170
|
+
}, _Executor_executeFile = function _Executor_executeFile(command, args, cwd) {
|
|
171
|
+
return new Promise(resolve => {
|
|
172
|
+
(0, child_process_1.execFile)(command, args, { signal: AbortSignal_1.default.signal, cwd }, (error, stdout, stderr) => {
|
|
173
|
+
resolve({ stdout, stderr: (error === null || error === void 0 ? void 0 : error.message) || stderr });
|
|
174
|
+
});
|
|
175
|
+
});
|
|
167
176
|
}, _Executor_killProcess = async function _Executor_killProcess(process) {
|
|
168
177
|
let killed = false;
|
|
169
178
|
if (os.platform() === 'win32') {
|
|
@@ -185,7 +194,7 @@ _Executor_instances = new WeakSet(), _Executor_execute = function _Executor_exec
|
|
|
185
194
|
const errorLogFile = `${datadir}/errorlog.err`;
|
|
186
195
|
return new Promise(async (resolve, reject) => {
|
|
187
196
|
await fsPromises.rm(logFile, { force: true });
|
|
188
|
-
const process = (0, child_process_1.spawn)(binaryFilepath, ['--no-defaults', `--port=${port}`, `--datadir=${datadir}`, `--mysqlx-port=${mySQLXPort}`, `--mysqlx-socket=${dbPath}/x.sock`, `--socket=${dbPath}/m.sock`, `--general-log-file=${logFile}`, '--general-log=1', `--init-file=${dbPath}/init.sql`, '--bind-address=127.0.0.1', '--innodb-doublewrite=OFF', '--mysqlx=FORCE', `--log-error=${errorLogFile}`], { signal: AbortSignal_1.default.signal, killSignal: 'SIGKILL' });
|
|
197
|
+
const process = (0, child_process_1.spawn)(binaryFilepath, ['--no-defaults', `--port=${port}`, `--datadir=${datadir}`, `--mysqlx-port=${mySQLXPort}`, `--mysqlx-socket=${dbPath}/x.sock`, `--socket=${dbPath}/m.sock`, `--general-log-file=${logFile}`, '--general-log=1', `--init-file=${dbPath}/init.sql`, '--bind-address=127.0.0.1', '--innodb-doublewrite=OFF', '--mysqlx=FORCE', `--log-error=${errorLogFile}`, `--user=${os.userInfo().username}`], { signal: AbortSignal_1.default.signal, killSignal: 'SIGKILL' });
|
|
189
198
|
//resolveFunction is the function that will be called to resolve the promise that stops the database.
|
|
190
199
|
//If resolveFunction is not undefined, the database has received a kill signal and data cleanup procedures should run.
|
|
191
200
|
//Once ran, resolveFunction will be called.
|
|
@@ -290,25 +299,116 @@ _Executor_instances = new WeakSet(), _Executor_execute = function _Executor_exec
|
|
|
290
299
|
}
|
|
291
300
|
});
|
|
292
301
|
});
|
|
293
|
-
}, _Executor_setupDataDirectories = async function _Executor_setupDataDirectories(options, binaryFilepath, datadir) {
|
|
302
|
+
}, _Executor_setupDataDirectories = async function _Executor_setupDataDirectories(options, binaryFilepath, datadir, retry) {
|
|
294
303
|
this.logger.log('Created data directory for database at:', datadir);
|
|
295
304
|
await fsPromises.mkdir(datadir, { recursive: true });
|
|
296
|
-
|
|
297
|
-
if (
|
|
298
|
-
|
|
299
|
-
|
|
305
|
+
let stderr;
|
|
306
|
+
if (binaryFilepath === 'mysqld') {
|
|
307
|
+
const { error, stderr: output } = await __classPrivateFieldGet(this, _Executor_instances, "m", _Executor_execute).call(this, `mysqld --no-defaults --datadir=${datadir} --initialize-insecure`);
|
|
308
|
+
stderr = output;
|
|
309
|
+
if (error) {
|
|
310
|
+
this.logger.error('An error occurred while initializing database with system-installed MySQL:', error);
|
|
311
|
+
throw 'An error occurred while initializing database with system-installed MySQL. Please check the console for more information.';
|
|
312
|
+
}
|
|
313
|
+
}
|
|
314
|
+
else {
|
|
315
|
+
let result;
|
|
316
|
+
try {
|
|
317
|
+
result = await __classPrivateFieldGet(this, _Executor_instances, "m", _Executor_executeFile).call(this, `${binaryFilepath}`, [`--no-defaults`, `--datadir=${datadir}`, `--initialize-insecure`], (0, path_1.resolve)(`${binaryFilepath}/..`));
|
|
318
|
+
}
|
|
319
|
+
catch (e) {
|
|
320
|
+
this.logger.error('Error occurred from executeFile:', e);
|
|
321
|
+
throw e;
|
|
322
|
+
}
|
|
323
|
+
stderr = result === null || result === void 0 ? void 0 : result.stderr;
|
|
324
|
+
}
|
|
325
|
+
if (stderr && !stderr.includes('InnoDB initialization has ended')) {
|
|
326
|
+
if (process.platform === 'win32' && stderr.includes('Command failed')) {
|
|
327
|
+
this.logger.error(stderr);
|
|
300
328
|
throw 'The mysqld command failed to run. A possible cause is that the Microsoft Visual C++ Redistributable Package is not installed. MySQL 5.7.40 and newer requires Microsoft Visual C++ Redistributable Package 2019 to be installed. Check the MySQL docs for Microsoft Visual C++ requirements for other MySQL versions. If you are sure you have this installed, check the error message in the console for more details.';
|
|
301
329
|
}
|
|
302
|
-
if (process.platform === 'linux' &&
|
|
303
|
-
|
|
304
|
-
|
|
330
|
+
if (process.platform === 'linux' && stderr.includes('libaio.so')) {
|
|
331
|
+
if (binaryFilepath === 'mysqld') {
|
|
332
|
+
throw 'libaio could not be found while running system-installed MySQL. libaio must be installed on this system for MySQL to run. To learn more, please check out https://dev.mysql.com/doc/refman/en/binary-installation.html';
|
|
333
|
+
}
|
|
334
|
+
if (retry === false) {
|
|
335
|
+
this.logger.error('An error occurred while initializing database:', stderr);
|
|
336
|
+
throw 'Tried to copy libaio into lib folder and MySQL is still failing to initialize. Please check the console for more information.';
|
|
337
|
+
}
|
|
338
|
+
if (binaryFilepath.slice(-16) === 'mysql/bin/mysqld') {
|
|
339
|
+
const { error: lderror, stdout, stderr: ldstderr } = await __classPrivateFieldGet(this, _Executor_instances, "m", _Executor_execute).call(this, 'ldconfig -p');
|
|
340
|
+
if (lderror || ldstderr) {
|
|
341
|
+
this.logger.error('The following libaio error occurred:', stderr);
|
|
342
|
+
this.logger.error('After the libaio error, an ldconfig error occurred:', lderror || ldstderr);
|
|
343
|
+
throw 'The ldconfig command failed to run. This command was ran to find libaio because libaio could not be found on the system. libaio is needed for MySQL to run. Do you have ldconfig and libaio installed? Learn more about libaio at Learn more at https://dev.mysql.com/doc/refman/en/binary-installation.html';
|
|
344
|
+
}
|
|
345
|
+
const libaioFound = stdout.split('\n').filter(lib => lib.includes('libaio.so.1t64'));
|
|
346
|
+
if (!libaioFound.length) {
|
|
347
|
+
this.logger.error('Error from launching MySQL:', stderr);
|
|
348
|
+
throw 'An error occurred while launching MySQL. The most likely cause is that libaio1 and libaio1t64 could not be found. Either libaio1 or libaio1t64 must be installed on this system for MySQL to run. To learn more, please check out https://dev.mysql.com/doc/refman/en/binary-installation.html. Check error in console for more information.';
|
|
349
|
+
}
|
|
350
|
+
const libaioEntry = libaioFound[0];
|
|
351
|
+
const libaioPathIndex = libaioEntry.indexOf('=>');
|
|
352
|
+
const libaioSymlinkPath = libaioEntry.slice(libaioPathIndex + 3);
|
|
353
|
+
const libaioPath = await fsPromises.realpath(libaioSymlinkPath);
|
|
354
|
+
const copyPath = (0, path_1.resolve)(`${binaryFilepath}/../../lib/private/libaio.so.1`);
|
|
355
|
+
try {
|
|
356
|
+
(0, proper_lockfile_1.lockSync)(copyPath, { realpath: false });
|
|
357
|
+
this.logger.log('libaio copy path:', copyPath, '| libaio symlink path:', libaioSymlinkPath, '| libaio actual path:', libaioPath);
|
|
358
|
+
let copyError;
|
|
359
|
+
try {
|
|
360
|
+
await fsPromises.copyFile(libaioPath, copyPath);
|
|
361
|
+
}
|
|
362
|
+
catch (e) {
|
|
363
|
+
copyError = e;
|
|
364
|
+
this.logger.error('An error occurred while copying libaio1t64 to lib folder:', e);
|
|
365
|
+
try {
|
|
366
|
+
await fsPromises.rm(copyPath, { force: true });
|
|
367
|
+
}
|
|
368
|
+
catch (e) {
|
|
369
|
+
this.logger.error('An error occurred while deleting libaio file:', e);
|
|
370
|
+
}
|
|
371
|
+
}
|
|
372
|
+
finally {
|
|
373
|
+
try {
|
|
374
|
+
(0, proper_lockfile_1.unlockSync)(copyPath, { realpath: false });
|
|
375
|
+
}
|
|
376
|
+
catch (e) {
|
|
377
|
+
this.logger.error('Error unlocking libaio file:', e);
|
|
378
|
+
}
|
|
379
|
+
if (copyError) {
|
|
380
|
+
throw 'An error occurred while copying libaio1t64 to the MySQL lib folder. Please check the console for more details.';
|
|
381
|
+
}
|
|
382
|
+
//Retry setting up directory now that libaio has been copied
|
|
383
|
+
this.logger.log('Retrying directory setup');
|
|
384
|
+
await this.deleteDatabaseDirectory(datadir);
|
|
385
|
+
await __classPrivateFieldGet(this, _Executor_instances, "m", _Executor_setupDataDirectories).call(this, options, binaryFilepath, datadir, false);
|
|
386
|
+
return;
|
|
387
|
+
}
|
|
388
|
+
}
|
|
389
|
+
catch (error) {
|
|
390
|
+
if (String(error) === 'Error: Lock file is already being held') {
|
|
391
|
+
this.logger.log('Waiting for lock for libaio copy');
|
|
392
|
+
await (0, FileLock_1.waitForLock)(copyPath, options);
|
|
393
|
+
this.logger.log('Lock is gone for libaio copy');
|
|
394
|
+
}
|
|
395
|
+
this.logger.error('An error occurred from locking libaio section:', error);
|
|
396
|
+
throw error;
|
|
397
|
+
}
|
|
398
|
+
}
|
|
399
|
+
else {
|
|
400
|
+
throw 'Cannot recognize file structure for the MySQL binary folder. This was caused by not being able to find libaio. Try installing libaio. Learn more at https://dev.mysql.com/doc/refman/en/binary-installation.html';
|
|
401
|
+
}
|
|
305
402
|
}
|
|
306
|
-
throw
|
|
403
|
+
throw stderr;
|
|
307
404
|
}
|
|
405
|
+
this.logger.log('Creating init text');
|
|
308
406
|
let initText = `CREATE DATABASE ${options.dbName};`;
|
|
309
407
|
if (options.username !== 'root') {
|
|
310
408
|
initText += `\nRENAME USER 'root'@'localhost' TO '${options.username}'@'localhost';`;
|
|
311
409
|
}
|
|
410
|
+
this.logger.log('Writing init file');
|
|
312
411
|
await fsPromises.writeFile(`${options.dbPath}/init.sql`, initText, { encoding: 'utf8' });
|
|
412
|
+
this.logger.log('Finished writing init file');
|
|
313
413
|
};
|
|
314
414
|
exports.default = Executor;
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.waitForLock = waitForLock;
|
|
4
|
+
const proper_lockfile_1 = require("proper-lockfile");
|
|
5
|
+
function waitForLock(path, options) {
|
|
6
|
+
return new Promise(async (resolve, reject) => {
|
|
7
|
+
let retries = 0;
|
|
8
|
+
while (retries <= options.lockRetries) {
|
|
9
|
+
retries++;
|
|
10
|
+
try {
|
|
11
|
+
const locked = (0, proper_lockfile_1.checkSync)(path);
|
|
12
|
+
if (!locked) {
|
|
13
|
+
return resolve();
|
|
14
|
+
}
|
|
15
|
+
else {
|
|
16
|
+
await new Promise(resolve => setTimeout(resolve, options.lockRetryWait));
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
catch (e) {
|
|
20
|
+
return reject(e);
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
reject(`lockRetries has been exceeded. Lock had not been released after ${options.lockRetryWait} * ${options.lockRetries} milliseconds.`);
|
|
24
|
+
});
|
|
25
|
+
}
|
package/dist/types/index.d.ts
CHANGED
|
@@ -11,6 +11,9 @@ export type ServerOptions = {
|
|
|
11
11
|
username?: string;
|
|
12
12
|
deleteDBAfterStopped?: boolean;
|
|
13
13
|
dbPath?: string;
|
|
14
|
+
ignoreUnsupportedSystemVersion?: boolean;
|
|
15
|
+
port?: number;
|
|
16
|
+
xPort?: number;
|
|
14
17
|
};
|
|
15
18
|
export type InternalServerOptions = {
|
|
16
19
|
version?: string;
|
|
@@ -23,6 +26,9 @@ export type InternalServerOptions = {
|
|
|
23
26
|
username: string;
|
|
24
27
|
deleteDBAfterStopped: boolean;
|
|
25
28
|
dbPath: string;
|
|
29
|
+
ignoreUnsupportedSystemVersion: boolean;
|
|
30
|
+
port: number;
|
|
31
|
+
xPort: number;
|
|
26
32
|
};
|
|
27
33
|
export type ExecutorOptions = {
|
|
28
34
|
logLevel: LOG_LEVEL;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "mysql-memory-server",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.4.0",
|
|
4
4
|
"description": "Spin up an ephemeral MySQL database from your JavaScript code",
|
|
5
5
|
"main": "dist/src/index.js",
|
|
6
6
|
"types": "dist/src/index.d.ts",
|
|
@@ -16,9 +16,9 @@
|
|
|
16
16
|
"memory database"
|
|
17
17
|
],
|
|
18
18
|
"scripts": {
|
|
19
|
-
"test": "jest --testPathIgnorePatterns=/stress-tests/",
|
|
20
|
-
"test:ci": "jest --testPathIgnorePatterns=/stress-tests/ --setupFilesAfterEnv ./ciSetup.js",
|
|
21
|
-
"stress": "jest --runTestsByPath stress-tests/stress.test.ts --setupFilesAfterEnv ./ciSetup.js"
|
|
19
|
+
"test": "jest --testPathIgnorePatterns=/stress-tests/ --verbose",
|
|
20
|
+
"test:ci": "jest --testPathIgnorePatterns=/stress-tests/ --setupFilesAfterEnv ./ciSetup.js --verbose",
|
|
21
|
+
"stress": "jest --runTestsByPath stress-tests/stress.test.ts --setupFilesAfterEnv ./ciSetup.js --verbose"
|
|
22
22
|
},
|
|
23
23
|
"author": "Sebastian-Webster",
|
|
24
24
|
"license": "MIT",
|
|
@@ -27,7 +27,7 @@
|
|
|
27
27
|
"@babel/preset-env": "^7.24.8",
|
|
28
28
|
"@babel/preset-typescript": "^7.24.7",
|
|
29
29
|
"@types/adm-zip": "^0.5.5",
|
|
30
|
-
"@types/node": "^22.
|
|
30
|
+
"@types/node": "^22.2.0",
|
|
31
31
|
"@types/proper-lockfile": "^4.1.4",
|
|
32
32
|
"@types/semver": "^7.5.8",
|
|
33
33
|
"babel-jest": "^29.7.0",
|