From ae542e9501fa965c3a9c5c5358d35cc1334b8c32 Mon Sep 17 00:00:00 2001 From: Hornwitser Date: Fri, 28 Jul 2023 21:36:43 +0200 Subject: [PATCH 1/2] Lazy compile schema validators for ctl Speed up the startup time for ctl by delaying schemas compilation until the validator is used. Saves approximately 120ms on startup time. --- packages/ctl/ctl.js | 2 ++ packages/lib/src/schema.js | 32 +++++++++++++++++++++++++------- 2 files changed, 27 insertions(+), 7 deletions(-) diff --git a/packages/ctl/ctl.js b/packages/ctl/ctl.js index a699e22f..07c16fbc 100755 --- a/packages/ctl/ctl.js +++ b/packages/ctl/ctl.js @@ -10,6 +10,8 @@ const yargs = require("yargs"); const version = require("./package").version; const setBlocking = require("set-blocking"); +// Reduce startup time by lazy compiling schemas. +global.lazySchemaCompilation = true; const lib = require("@clusterio/lib"); const { ConsoleTransport, levels, logger } = lib; diff --git a/packages/lib/src/schema.js b/packages/lib/src/schema.js index 65f90937..63c2f5c9 100644 --- a/packages/lib/src/schema.js +++ b/packages/lib/src/schema.js @@ -5,18 +5,36 @@ "use strict"; const Ajv = require("ajv"); const ajv = new Ajv({ + // These are duplicated in scripts/compile_validator allowUnionTypes: true, strict: "log", strictTuples: false, verbose: true, }); +/** + * Compile JSON schema into validator + * @function + * @param {Object} schema - JSON schema to create validator for. + * @returns {Function} Validator for the schema. + */ +function compile(schema) { + if (typeof global !== "object" || !global.lazySchemaCompilation) { + return ajv.compile(schema); + } + + let doValidate; + function validate(data) { + if (!doValidate) { + doValidate = ajv.compile(schema); + } + let result = doValidate(data); + validate.errors = doValidate.errors; + return result; + } + return validate; +} + module.exports = { - /** - * Compile JSON schema into validator - * @function - * @param {Object} schema - JSON schema to create validator for. - * @returns {Function} Validator for the schema. - */ - compile: ajv.compile.bind(ajv), + compile, }; From 5eb91be6fba8303553549d8acc9a32df7cec5eda Mon Sep 17 00:00:00 2001 From: Hornwitser Date: Fri, 28 Jul 2023 21:43:20 +0200 Subject: [PATCH 2/2] Pre-compile Message validator Use a script to pre-compile the validator for the Message class to reduce the loading time for ctl by about 40ms. --- packages/lib/.gitignore | 1 + packages/lib/.npmignore | 0 packages/lib/package.json | 4 ++++ packages/lib/scripts/compile_validator.js | 28 +++++++++++++++++++++++ packages/lib/src/data/messages_core.js | 5 ++-- 5 files changed, 36 insertions(+), 2 deletions(-) create mode 100644 packages/lib/.gitignore create mode 100644 packages/lib/.npmignore create mode 100644 packages/lib/scripts/compile_validator.js diff --git a/packages/lib/.gitignore b/packages/lib/.gitignore new file mode 100644 index 00000000..922aef1f --- /dev/null +++ b/packages/lib/.gitignore @@ -0,0 +1 @@ +src/data/message_validate.js diff --git a/packages/lib/.npmignore b/packages/lib/.npmignore new file mode 100644 index 00000000..e69de29b diff --git a/packages/lib/package.json b/packages/lib/package.json index 635d1599..ce052530 100644 --- a/packages/lib/package.json +++ b/packages/lib/package.json @@ -4,6 +4,10 @@ "version": "2.0.0-alpha.13", "repository": "https://github.com/clusterio/clusterio", "license": "MIT", + "scripts": { + "prepare": "pnpm run compile_validator", + "compile_validator": "node scripts/compile_validator.js" + }, "browser": "browser.js", "keywords": [ "factorio" diff --git a/packages/lib/scripts/compile_validator.js b/packages/lib/scripts/compile_validator.js new file mode 100644 index 00000000..bc90dcf4 --- /dev/null +++ b/packages/lib/scripts/compile_validator.js @@ -0,0 +1,28 @@ +"use strict"; +const fs = require("fs-extra"); +const path = require("path"); +const Ajv = require("ajv"); +const standaloneCode = require("ajv/dist/standalone").default; + +/* eslint-disable node/no-sync */ + +// Provide an empty file for message_core to load +fs.outputFileSync(path.join(__dirname, "../src/data/message_validate.js"), ""); + +const { Message } = require("../src/data/messages_core"); + +const ajv = new Ajv({ + allowUnionTypes: true, + strict: "log", + strictTuples: false, + verbose: true, + code: { + source: true, + }, +}); +const validate = ajv.compile(Message.jsonSchema); +let moduleCode = standaloneCode(ajv, validate); +fs.outputFileSync( + path.join(__dirname, "../src/data/message_validate.js"), + `// generated by scripts/compile_validator.js\n/* eslint-disable */\n${moduleCode}` +); diff --git a/packages/lib/src/data/messages_core.js b/packages/lib/src/data/messages_core.js index 14e090f9..beda4502 100644 --- a/packages/lib/src/data/messages_core.js +++ b/packages/lib/src/data/messages_core.js @@ -1,5 +1,6 @@ +// Note: Run compile_validator script after making changes to this file. "use strict"; -const libSchema = require("../schema"); +const message_validate = require("./message_validate.js"); /** @memberof module:lib */ @@ -851,7 +852,7 @@ Message.jsonSchema = { MessageDisconnect.jsonSchema, ], }; -Message.validate = libSchema.compile(Message.jsonSchema); +Message.validate = message_validate; /** @memberof module:lib */ class PingRequest {