ajv-validator / ajv

The fastest JSON schema Validator. Supports JSON Schema draft-04/06/07/2019-09/2020-12 and JSON Type Definition (RFC8927)
https://ajv.js.org
MIT License
13.69k stars 872 forks source link

Optimization: Add .clone() method to Ajv instance #1903

Open sinclairzx81 opened 2 years ago

sinclairzx81 commented 2 years ago

What version of Ajv you are you using?

8.9.0

What problem do you want to solve?

Optimize Schema Compilation

What do you think is the correct solution to problem?

Allow Ajv instance to be cloned

Will you be able to implement it?

Unsure

Just wondering about a possible ajv.clone() method to help optimize schema compilation times. This would allow users to .clone() a partially configured Ajv instance, allowing additional configurations to be applied to the clone without impacting the initial instance. This with the hope of avoiding reinitialization of the whole instance in some scenarios.

Example

// -------------------------------------------------------------------------
// Partially Instance
// -------------------------------------------------------------------------
const ajv = addFormats(new Ajv({}), [ 
    'date-time', 'time', 'date', 'email', 'hostname', 'ipv4', 
    'ipv6', 'uri', 'uri-reference', 'uuid', 'uri-template', 
    'json-pointer', 'relative-json-pointer', 'regex'
])
.addKeyword({ keyword: 'specialized', type: 'object', validate: validateSpecialized })
.addKeyword('maxByteLength')
.addKeyword('minByteLength')
.addKeyword('modifier')
.addKeyword('kind')

// ------------------------------------------------------------
// Full Instance
//
// The following clones the partial instance for the purpose
// of creating a validation context. The additional schemas
// A, B and C are added to the clone, leaving the original
// instance unchanged. This should help avoid any costly
// setup required configuring the partial instance (measured
// upwards of 100ms)
// ------------------------------------------------------------
const context = ajv.clone().addSchema([A, B, C])
const validate = context.compile(C)
epoberezkin commented 2 years ago

What’s the use case for that?

sinclairzx81 commented 2 years ago

@epoberezkin Hi

I'm currently working on an application that manages sets of schemas grouped into namespaces defined by the application. Each schema within a namespace has an associated $id; with some $id's colliding with $id's in other namespaces. To avoid $id collision, I am currently creating a fresh Ajv instance for each namespace. For example

export function createNamespace(schemas: AnySchema[]): Ajv {
  return addFormats(new Ajv({}), [ ... ]).addSchema(schemas) // slow
}

However I've noticed that initializing fresh instances of Ajv tends to be quite expensive when using addFormats(...). As a possible suggestion avoid complete reinitialization; a .clone() function would allow one to partially configure an Ajv instance with common configurations (slow), with the .clone() function returning a new but preconfigured instance (fast). This would enable users to gradually apply additional configurations (such as addSchema()) to the clone without impacting the common instance, as well as avoiding re-initialization. So for example.


const commonAjv = addFormats(new Ajv({}), [ ... ]) // slow

export function createNamespace(schemas: AnySchema[]): Ajv {
  return commonAjv.clone().addSchema(schemas) // fast
}

So this would solve my particular use case (which is mostly dealing with potential ID collisions) while potentially speeding app start up by an order of magnitude. I imagine such a feature could be useful to users in general by allowing gradual configuration of Ajv contexts from a singular instance.

Would something like this be relatively straightforward to implement?