-
-
Notifications
You must be signed in to change notification settings - Fork 887
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Return also errors when calling Ajv.validate (for stateless usage) #256
Comments
Gigantic +1 for this, I don't understand the logic in attaching errors to an instance when the point of ajv is that it caches schemas, making it perfect for being used as a singleton. I have an application which may perform thousands of validations per second and it's unrealistic to expect to never hit a race condition in which |
Thanks for the issue. It's similar to #242. JavaScript is always single threaded so you won't have any race conditions in asynchronous environment if you use the errors in the same execution block - see the referenced issue. It's also better to use compiled validation function rather than accessing it via |
Thanks for your quick reply. You are of course right when speaking about JavaScript. Please correct me if I am wrong, but the compiled validation function has exactly the same behaviour in which the error result is written back to the compiled validation function instance. |
Interesting... If the same JavaScript function (the same instance of function) is indeed used from multiple threads (not sure if that is possible) then changing Ajv instance API may not be enough to solve the problem. I would suggest checking if the validation function you access is the same instance (by marking it with some unique property e.g.). Even if it's the same function, it's worth checking if they are called concurrently. I would still expect there should be some locking mechanism in this case, that would prevent the same instance of function to be concurrently executed from multiple threads. Could you please check it?
There are both historical reasons (other fast validators have the same api) and performance reasons not to return an object. Given that convenience is a subjective thing and also that the majority of users end up having their custom wrapper anyway (to retrieve schemas, to process validation errors, etc.) I am not convinced that convenience is strong enough reason to change or to extend the api. By the way, are you aware that ajv supports asynchronous validation where you can add your own custom keywords (or formats) having some async calls in them? Even without custom keywords, if you add
Correct, compiled function is cached using stable-stringified schema as a key. When you use ajv.validate you also use the same function under the hood. What I meant is that by calling a compiled function directly you avoid extra function call. |
Sorry for my very late reply now, I have been on vacation and somehow I also forgot this issue ;-)
We have also our own wrapper around AJV, so of course the convenience thing matters just inside the wrapper implementation ;-) Anyway it was more about being stateless vs having a state in general, independently of the single-thread model of javascript. To be honest, I never thought that returning a simple object vs returning a boolean would be a performance bottleneck (I thought this wouldn't even be measurable after all).
I know this is a little bit strange due to we have a very special scenario. But at the end you are right, this might be more a general problem due to as soon we use an object with a state we cannot hold one object between several threads. |
Thank you, I will read. |
I've read the post. Using JavaScript with JVM seems to be fun :) Using the pool of objects seems to be the best approach, it will work in general case. Given that JS is inherently single threaded any library, even with a stateless api, could have some state under the hood, it would be too difficult to find out whether it's the case. Using pool of single threaded resources solves this issue in any case. |
@epoberezkin from my point of view it's a bad design to have something global if there is no reason to have it global. Furthermore, to assume, that JavaScript is always single threaded, is just a speculation. You can see, that there are runtimes, which are not single threaded. I'm fine with argumentation, that you don't want to spend time to make your library resistent against race condition, but still, such design bothers me and makes the usage counterintuitive and error prone. |
This is not a speculation - this is a standard for JavaScript implementations: https://www.ecma-international.org/ecma-262/10.0/index.html#sec-agents An object can only be accessed by one thread; even though there may be multiple threads in the environments, but they cannot access the same objects. So no normal objects are shared between the treads.
I believe it is resilient to race conditions as is - it follows from JS spec. Happy to be proven otherwise by a specific code sample that demonstrates race condition you are talking about in at least one JavaScript environment that complies with JS spec.
It may be the case, and it can be addressed by a single wrapper around the library to minimise the chances of any errors. Maintaining backward compatibility and avoiding unnecessary API changes takes priority over individual preferences to the API design. |
@epoberezkin would you mind providing an example of such wrapper? import Ajv from 'ajv'
const ajv = new Ajv({ verbose: true })
ajv.addSchema({$id: 'test', type: 'object', properties: { number: {type: 'number', maximum: -1} }})
async function myWrapperValidate(data, sleepTime) {
//Get cached compiled validator
let compiledSchemaValidator = ajv.getSchema('test')
//validate data
compiledSchemaValidator(data)
//simulate some delay
await new Promise(r => setTimeout(r, sleepTime));
//get error data information
return compiledSchemaValidator.errors[0].data
}
let returned = await Promise.all([
myWrapperValidate({number: 1}, 300),
myWrapperValidate({number: 2}, 200),
myWrapperValidate({number: 3}, 100)
])
// returned => [3,3,3] |
In the current version all errors of a validation are not returned, but are saved in the ajv instance, thus if I want to access them I need something like that:
ajv.addSchema(schema, 'mySchema'); var valid = ajv.validate('mySchema', data); var errors = ajv.errors; // All validation errors are written into this property
This is of course no problem in a normal javascript environment. We have however a quite advanced scenario where we call AJV in our java backend and we have only one AJV instance (that we initialize on server startup) for all requests. As far as I can tell this works, except for retrieving the specific error properties which are always written to the used AJV instance and is hence not stateless.
Now my question: Is it possible to create e.g. a new method (also for not breaking the existing API) like Ajv.validateWithErrorResult(...) that return a result object like e.g. following:
{ valid: false, errors: { .... } }
Hopefully this may be only a change in API (?)
The text was updated successfully, but these errors were encountered: