Closed almostchristian closed 1 week ago
This is interesting, as we initially had an async interface, but due to the fact that many validations (cardinality, string length, patterns, etc) were merely very short computes, the overhead of Task/await on all those nested validates became noticable, so we removed it. I guess it depends on the ratio of I/O bound validations and simple, compute validations. And as we know, async tends to spread everywhere, so we cannot have non-async validates for simple computes, and async validates for i/o bound ones. At least I think so, I'd love to be proven wrong ;-) As it stands, I don't feel like re-introducing async anytime soon after our previous experiences.
In your previous testing, did you use ValueTask
or Task
for the validators? I agree that superfluous use of async can hurt performance and it may take testing many use cases to definitively say which one is better for performance (and in the end the answer may be 'it depends'). I think the validator should support both synchronous and asynchronous validation, and it will be up to the end users to validate which is the best one to use for their use case.
For now, the main advantage to using async is that it preserves the traces in flame graphs which makes it easier to diagnose performance issues. In the synchronous version, the occasional calls to TaskHeper.Await changes the stack traces
Flame graph (synchronous)
Flame graph (async)
My QR validator has a hybrid of both approaches.
https://github.com/brianpos/fhir-net-web-api/blob/66e90f72c6adcdf36f436a313e8e886fa61abf9e/src/Hl7.Fhir.StructuredDataCapture/QuestionnaireResponseValidator.cs#L742
It has an async top level, with a List
One benefit to an async validtion API is that potentially, validation could take a long time and it is possible that the client has cancelled the validation request. Respecting the cancellation token can help with scalability.
We think async will lead to too much overhead current, since most validation are so fast that starting up a task will take more time than actually validating the validation blocks.
The new validator should include an async API to avoid calls to
TaskHelper.Await
. During my performance profiling sessions, I found usingTaskHelper.Await
, or evenTask.GetAwater().GetResult()
makes the traces chaotic. Also, I feel it has a significant scalability impact when used with network bound resource resolvers.Proposed new API
The three interfaces use the default interface method implementation for the new API. If the validator does not need to be async (i.e., it doesn't call any async api such as other other validators), it doesn't need to implement the new async method.
Performance
The async validate API also appears to be faster according to benchmarks: