Skip to content
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

Support validation on didSave and/or add debounce for didChange #613

Closed
1 of 4 tasks
deiteris opened this issue Sep 11, 2021 · 17 comments
Closed
1 of 4 tasks

Support validation on didSave and/or add debounce for didChange #613

deiteris opened this issue Sep 11, 2021 · 17 comments
Assignees

Comments

@deiteris
Copy link
Contributor

deiteris commented Sep 11, 2021

Your issue may already be reported! Please search on Github issues before creating one.

  • I'm submitting a ...

    • bug report
    • feature request
    • question
    • other
  • What is the current behavior?

Note: This seems to be mostly related to JS version, since this issue is not so noticeable in JVM.

Currently, the language server doesn't seem to handle didSave event (https://github.com/aml-org/als/blob/develop/als-server/jvm/src/main/scala/org/mulesoft/als/server/lsp4j/TextDocumentServiceImpl.scala#L88), and didChange event works in real-time.

  • What is the motivation/use case for changing the behavior? (for feature requests)

Either of the solutions would probably significantly improve the handling of large API definitions (and when working on slower PCs), since their validation may take some time. Providing validation with just didChange event in real-time in this case is suboptimal since the requests aren't executed on time and being enqueued and executed one by one (again, maybe this is how it works in JS specifically, with JVM I don't notice requests stacking) without any possibility to cancel them.

  • Please tell us about your environment:

    • ALS Version: 4.1.0
    • ALS Distribution: JS
    • OS: Windows 10 21H1
  • Other information (e.g. detailed explanation, stack traces, related issues, workarounds, links for us to have context, eg. StackOverflow, Gitter, etc)

@llibarona
Copy link
Member

Hi @deiteris !
Unfortunately we have some limitations regarding the cancelation of requests, since the most heavy processes (parsing/validating) are not possible to cancel on AMF' side at the moment.

We do have some mechanisms to try and improve performance and usability:

  • When multiple changes on the same file (or project) are being made, we do stage them and only address the latest up to the time we begin this process, not parsing/validating for each one

  • Regarding diagnostics, we also have implemented the possibility to receive on-availability diagnostics. This means for example if there are some parsing errors (wrong syntax for example), you get notified as soon as they're available, even though the validation stage may still be processing. This is not available for configuration today (only used locally for tests and POC), but if it interests you, we can look to provide it.

The issue is probably the same in both JVM and JS, the main difference being that JVM manages to better use the PC resources (more specifically, it provides multi-threaded processing).

If what you want is to turn off automatic validations and just validate on demand, I suggest you look at Clean Diagnostics (https://github.com/aml-org/als/blob/develop/documentation/features/custom-messages.md#cleandiagnostictree)
This may help specifically on very large/complex APIs.

If there is a specific optimization you have in mind (please be aware that cancellation is not an option at this time), please let us know. Performance/consistency is always a priority for us and any idea to improve this is always welcomed with open arms.

@llibarona llibarona self-assigned this Sep 11, 2021
@deiteris
Copy link
Contributor Author

deiteris commented Sep 11, 2021

Hi @llibarona!

If what you want is to turn off automatic validations and just validate on demand, I suggest you look at Clean Diagnostics (https://github.com/aml-org/als/blob/develop/documentation/features/custom-messages.md#cleandiagnostictree)
This may help specifically on very large/complex APIs.

I am aware of this custom request, but if I would like to use this approach, how can I disable automatic validation?

When multiple changes on the same file (or project) are being made, we do stage them and only address the latest up to the time we begin this process, not parsing/validating for each one

Hmm, from my observations, I see that actually each change done (probably there's some time window since it's not character by character) to the file is being parsed. While with JVM I see previous entries being processed fast in parallel (and this actually may put a lot of strain even on powerful PC too), in JS I see requests stacking and being processed one by one with single thread (worker threads could probably put performance to the same level as in JVM).

If there is a specific optimization you have in mind (please be aware that cancellation is not an option at this time), please let us know. Performance/consistency is always a priority for us and any idea to improve this is always welcomed with open arms.

As I mentioned in my initial post, debouncing didChange requests could probably help to mitigate the issue (if it doesn't affect any abovementioned techniques). I made a quick PoC and it works (improves long request times, but with short request times should be removed), but I'm not familiar with Scala and can't figure out how can I use timers (some construction equivalent to setTimeout() and clearTimeout() in JS) to ensure the last request will be processed. Here's what I did just to see whether it will work:

class TextDocumentManager(...) extends AlsTextDocumentSyncConsumer {
  ...
  private var prevRequestTime: Long = System.currentTimeMillis

  override def didChange(params: DidChangeTextDocumentParams): Unit = {
    ...
    val curRequestTime = System.currentTimeMillis
    // Threshold in millis, could be configurable.
    val requestThreshold = 1000
    if ((curRequestTime - this.prevRequestTime) > requestThreshold) {
      documentWasChanged(ChangedDocument(uri.toAmfUri, version, text, None))
    }
    this.prevRequestTime = curRequestTime
  }
  ...
}

The other way around would probably be running an AMF instance for each file on a separate thread (using Java thread and JS worker thread), let some number of threads run and finish execution so we have some parsed document data available ASAP (like it seems to be done right now), and stopping thread execution when cancellation request was received (so we don't rely on AMF implementation), but this looks like more work and investigation to do.

Sorry if it doesn't really make sense or was already researched, since I have little idea how the server is currently designed, what possible limitations can be when maintaining both versions and can mostly judge from the way I see it works :)

@llibarona
Copy link
Member

llibarona commented Sep 13, 2021

Hi @deiteris , great research
Let me tell you a bit about our architecture so it may be easier for you to debug if needed and to look for changes

As of 4.1.0 (latest release), AMF (our parser/validator, which does most of the heavy work for us) is a static object (we use it through this AMF Instance)

Now in develop, we are adopting the beta of AMF 5 which allows for multiple instances (just for context)

In ALS we have multiple managers for specific tasks (requests/notifications)
Every change/open/close notification we receive, will go through the aforementioned TextDocumentManager and update our in memory filesystem.
Not only does this updates our memory file system, this also notifies our WorkspaceManager that a change has been made (could be open, close, change, focus...).
The WorkspaceManager is tasked to look for the correct WorkspaceContentManager, which has the responsibility to work on a specific workspace folder (with up to one project each), if there is no specific one it may be created or go to the default one (depending on wether there is a project or just isolated files). This WorkspaceContentManager has a StagingArea which is where the notification will be queued.

Inside the WorkspaceContentManager is where the juicier part occurs, here we have 1 process thread dedicated to the run itself (current), as long as there are notifications queued, we take one with the following priorities:

  1. Project Configuration changes (i.e. changes root file)
  2. Files in Project (main file or tree)
  3. Isolated files

1 and 2 will trigger the project parse, which means it will remove from the queue any notification referencing the main file or tree (as it is already processed)
Isolated will only remove from the queue the notifications from this same file (as the context here is relevant and different files will be processed as it own root in each case).
So this is where the "filtering" of duplicated notifications should happen.

This process (which should only be occurring one at a time per WorkspaceContentManager) is where the AMF Parse happens, and that part is not possible to cancel.

When the process is done, the new result is kept in the repository and other managers which depend on the result are notified (for example the Diagnostic/Resolution Managers), where a similar process occurs.

Any other request should not trigger directly a parse/resolution/validation, instead it just consumes the last one from the WorkspaceContentManager.

(sorry for the long expo)

Regarding multi threaded performance in JS, unfortunately ScalaJS does not support this as far as I'm aware of. There is always the possibility to implement this low-level obviously, but it has it's risks and maintainability issues.

Regarding the multithreaded performance in JVM, are you able to confirm if there are in fact multiple process calls at the same time in WorkspaceContentManager? (or ResolutionTaskManager, which is similar but with the resolution stage instead of parsing stage). This should only be possible if there are multiple workspace folders, so if it's happening we may have to look into it. (It is expected to have multiple threads, but not multiple runs at that step simultaneously parsing different files)

Through the telemetry notifications this should be quite visible, as each parse/resolution/diagnostic should send a telemetry notification on start and finish, with a UUID (so each start should match a finish with no other start in between)

As I mentioned in my initial post, debouncing didChange requests could probably help to mitigate the issue (if it doesn't affect any abovementioned techniques). I made a quick PoC and it works (improves long request times, but with short request times should be removed), but I'm not familiar with Scala and can't figure out how can I use timers (some construction equivalent to setTimeout() and clearTimeout() in JS) to ensure the last request will be processed. Here's what I did just to see whether it will work:

Regarding the debouncing mechanisms, it is a strong candidate for improvement, we talked about it in the past and never came up with a strategy we felt confortable with, and also never encountered that many strong cases in favor of doing so, but I will refloat the talks about it
Regarding the timeouts, it is tricky in ScalaJS, but you can look at this as an example:
als-server/shared/src/test/scala/org/mulesoft/als/server/TimeoutFuture.scala
or a bit more complex:
als-server/shared/src/main/scala/org/mulesoft/als/server/modules/common/reconciler/Reconciler.scala
Unfortunately this won't stop running threads, just let's you handle them a bit better (schedule them)

I'm sorry for the verbose explanation, and hope it was somewhat clear.
Please feel free to ask about any doubt or confusing part.
We are aware we have some technical debt regarding performance (specially on JS), and are always eager to hear suggestions and specific cases to look upon, so if you want to upload a specific API (assuming it is not sensitive information) for us to analyze you are also welcome.
If you are going to look at code, I would strongly recommend at this moment in time to look in the develop branch, as there were plenty of internal changes regarding the last stable release.

@deiteris
Copy link
Contributor Author

deiteris commented Sep 13, 2021

Great explanation :) It's a bit hard to wrap my head around since it's a really complex subject while I am also getting to understand Scala a bit. But now I see the limitations, and things aren't as bright as I was imagining in the beginning.

Regarding the multithreaded performance in JVM, are you able to confirm if there are in fact multiple process calls at the same time in WorkspaceContentManager? (or ResolutionTaskManager, which is similar but with the resolution stage instead of parsing stage). This should only be possible if there are multiple workspace folders, so if it's happening we may have to look into it. (It is expected to have multiple threads, but not multiple runs at that step simultaneously parsing different files)

Through the telemetry notifications this should be quite visible, as each parse/resolution/diagnostic should send a telemetry notification on start and finish, with a UUID (so each start should match a finish with no other start in between)

I've just re-checked, and no, there are no other BEGIN_PARSE in-between these sequences, so it's ok. What I was observing is growing CPU utilization and memory consumption when I was sending more requests by changing the same file (I guess there are just some tasks done in parallel), and that JVM version was gradually catching up with parse/validation as I was typing.

But now I did direct comparison (with version 4.1.0) and figured, that JS variant is almost 10x times slower than JVM when it comes to parsing alone, and 6-7x times slower in other scenarios like document linking, folding in the same RAML API definition. Unfortunately, I cannot share the API definition without a permission, but I can say that it has around 130 endpoints and each one has a JSON schema (there are also some common schemas linked as type definitions) + examples.

Here're few telemetry entries:

JVM

[Trace - 20:14:24] Received notification 'telemetry/event'.
Params: {
    "event": "AMF Parse",
    "messageType": "END_PARSE",
    "message": "WorkspaceContentManager : parse \n\ttook 713 millis",
    "uri": "file:///e:/Sources/v2/api.raml",
    "time": 1631553264625,
    "uuid": "c4d2e307-a449-45e5-9748-79b761540b2e"
}
...
[Trace - 20:14:30] Received notification 'telemetry/event'.
Params: {
    "event": "FoldingRange",
    "messageType": "END_FOLDING",
    "message": "request for document highlights on file:///e:/Sources/v2/api.raml \n\ttook 6344 millis",
    "uri": "file:///e:/Sources/v2/api.raml",
    "time": 1631553270018,
    "uuid": "80dafd91-c573-4253-a0e7-c25e85731398"
}
...
[Trace - 20:14:31] Received notification 'telemetry/event'.
Params: {
    "event": "DocumentLink",
    "messageType": "END_DOCUMENT_LINK",
    "message": "request for document links on file:///e:/Sources/v2/api.raml \n\ttook 6140 millis",
    "uri": "file:///e:/Sources/v2/api.raml",
    "time": 1631553271420,
    "uuid": "2e9b06c7-0c15-4b06-af7f-bd68076307c5"
}

JS

[Trace - 20:34:34] Received notification 'telemetry/event'.
Params: {
    "event": "AMF Parse",
    "messageType": "END_PARSE",
    "message": "WorkspaceContentManager : parse \n\ttook 6128 millis",
    "uri": "file:///e:/Sources/v2/api.raml",
    "time": "1631554450632",
    "uuid": "32729b7c-cabb-4aa9-a47c-e0591cce95df"
}
...
[Trace - 20:34:34] Received notification 'telemetry/event'.
Params: {
    "event": "FoldingRange",
    "messageType": "END_FOLDING",
    "message": "request for document highlights on file:///e:/Sources/v2/api.raml \n\ttook 34442 millis",
    "uri": "file:///e:/Sources/v2/api.raml",
    "time": "1631554463665",
    "uuid": "a9d8d155-f5c9-4f10-b663-50f769800168"
}
...
[Trace - 20:34:34] Received notification 'telemetry/event'.
Params: {
    "event": "DocumentLink",
    "messageType": "END_DOCUMENT_LINK",
    "message": "request for document links on file:///e:/Sources/v2/api.raml \n\ttook 70029 millis",
    "uri": "file:///e:/Sources/v2/api.raml",
    "time": "1631554473271",
    "uuid": "af545394-b7ae-4472-9f2b-b76b69e1ffe6"
}

So, this actually requires more thorough investigation whether things can be improved for JS variant specifically. I see good amount of work done on optimization, and not sure I can actually give a better idea now. The best idea I currently have, is adding an option to do all processing on save (or on demand). Yes, we'll loose some fancy functionality if we choose to enable it, but this will help to avoid stacking lots of requests when the API is too large or we don't have fast enough PC to do the processing in real-time, that might take an eternity in our case :)

Debouncing now looks not as good idea, as I was thinking, since your current code already implements better handling. Making an option with didSave looks better because:

  1. My initial thought was that the processing was done character-by-character, but the algorithm that prevents such behavior is already implemented.
  2. If one has long processing times due to the reasons mentioned above, providing processing with didChange seems to be redundant since event can't be cancelled, so one will be stuck for some time without any meaningful information, completion and etc. until the parsing of previous few strings is done (and if we take my worst case, it might take up to few minutes until I get any meaningful output with JS variant).

But nevertheless, even with relatively worse performance, JS variant is a great alternative for smaller API definitions since it doesn't require any external dependencies to run the extension, performance penalty is not that critical for them and final size is much smaller (55mb for fat jar and 7.5mb for bundled js).

@llibarona
Copy link
Member

llibarona commented Sep 13, 2021

That data you provide seems a bit concerning for us.
We were mostly working under circumstances that AMF_ tasks where the most time consuming (PARSE, RESOLUTION, VALIDATION)
Based on the telemetry you share, I assume the Parse starts along with the other requests around the same time (it triggers with the first request, or in case of a project when the folder is open, but both other requests trigger the moment the file is opened, so it should be mostly simultaneous)
With this in mind, it is as you say and the AMF_PARSE is just 1/8 of the total time per request (approximately based on this two requests)
This is alarming, as the requests processing should be insignificant in relationship to the total processing of the document.
Could you tell me if this is a Project, and if so, if this has Dependencies? (which should explain optimization on AMF part)
Also, would you be able to tell me if there are plenty of recursive definitions in the spec?
Thanks very much!

edit: in case this was taken while editing the API, it could be explained by us waiting until the last change is parsed in order to respond (in that case, to the total time of the request we would need to subtract the sum of all parsing times in between)

@deiteris
Copy link
Contributor Author

deiteris commented Sep 13, 2021

I noticed that I specified wrong main API file. Here's a bit more telemetry with JS variant starting from one of BEGIN_PARSE tasks and ending with several END_FOLDING and END_DOCUMENT_LINK: https://pastebin.com/wg6re7Kd.

edit: in case this was taken while editing the API, it could be explained by us waiting until the last change is parsed in order to respond (in that case, to the total time of the request we would need to subtract the sum of all parsing times in between)

Yes, the part I've shown in my previous post was taken while I was editing. In that case only small part was cut since it's hard to navigate in telemetry log, as it becomes huge even after few edits, and filter out the data I'm probably not allowed to show. I provided a bit more telemetry above that may give you a better insight.

Could you tell me if this is a Project, and if so, if this has Dependencies? (which should explain optimization on AMF part)

Not sure what you mean. If by Project you mean whether I have main API file, then yes. But what do you mean by Dependencies?

Also, would you be able to tell me if there are plenty of recursive definitions in the spec?

According to AMF Graph that I generated with ALS, search by word recursive outputs 282 results. This is mostly caused by the following constructs in JSON schemas:

Type definition (f.e., common_type.json):

{
  "type": "string",
  // Some type constraints here
}

Usage:

{
  "$schema": "http://json-schema.org/draft-04/schema#",
  "definitions": {
    "common_type": {"$ref": "common_type.json"},
  },
  "type": "object",
  "properties": {
    "prop": {
      "$ref": "#/definitions/common_type"
    },
  },
  ...
}

@llibarona
Copy link
Member

Not sure what you mean. If by Project you mean whether I have main API file, then yes. But what do you mean by Dependencies?
Yes, basically if you have a main file, then you have a project
This can be set by 2 means, an exchange.json file or using the project config request. Either way, one optimization that can be done in a project is setting dependencies. This are immutable assets that we will store in caché in order to avoid reprocessing.
If you are using exchange.json files, this assets will be found by searching recursively every other exchange.json inside the subfolders and marking this assets as immutable, and this can actually shave lot of time in AMF processing.
Unfortunately this caché only works with assets (API files, not any json schema)

According to AMF Graph that I generated with ALS, search by word recursive outputs 282 results. This is mostly caused by the following constructs in JSON schemas:

This should not affect our times that much.

Looking at the telemetry logs, I see this:


[Trace - 23:22:51] Received notification 'telemetry/event'.
Params: {
    "event": "DocumentLink",
    "messageType": "END_DOCUMENT_LINK",
    "message": "request for document links on file:///e:/Sources/v2/api.raml \n\ttook 3442 millis",
    "uri": "file:///e:/Sources/v2/api.raml",
    "time": "1631564569382",
    "uuid": "57940346-e881-4c7d-b1bd-fc505a525cfd"
}
 
 
[Trace - 23:22:51] Received notification 'telemetry/event'.
Params: {
    "event": "DocumentLink",
    "messageType": "END_DOCUMENT_LINK",
    "message": "request for document links on file:///e:/Sources/v2/api.raml \n\ttook 12118 millis",
    "uri": "file:///e:/Sources/v2/api.raml",
    "time": "1631564569733",
    "uuid": "2f48d411-50fe-44e2-b873-d8948803e7c6"
}
 
 
[Trace - 23:22:51] Received notification 'telemetry/event'.
Params: {
    "event": "DocumentLink",
    "messageType": "END_DOCUMENT_LINK",
    "message": "request for document links on file:///e:/Sources/v2/api.raml \n\ttook 11155 millis",
    "uri": "file:///e:/Sources/v2/api.raml",
    "time": "1631564569781",
    "uuid": "a4f3ce68-a5a5-4897-bbad-87baaaf647c9"
}
 
 
[Trace - 23:22:51] Received notification 'telemetry/event'.
Params: {
    "event": "DocumentLink",
    "messageType": "END_DOCUMENT_LINK",
    "message": "request for document links on file:///e:/Sources/v2/api.raml \n\ttook 21404 millis",
    "uri": "file:///e:/Sources/v2/api.raml",
    "time": "1631564570327",
    "uuid": "701de698-749d-4e0e-a13a-e05e17b682d9"
}
 
 
[Trace - 23:22:51] Received notification 'telemetry/event'.
Params: {
    "event": "DocumentLink",
    "messageType": "END_DOCUMENT_LINK",
    "message": "request for document links on file:///e:/Sources/v2/api.raml \n\ttook 21432 millis",
    "uri": "file:///e:/Sources/v2/api.raml",
    "time": "1631564570327",
    "uuid": "a9dd4379-569a-4c5c-8bb9-c289e4918626"
}

It would look like the requests are stacking really bad, I created an internal ticket (https://www.mulesoft.org/jira/browse/ALS-1702) to look to improve this (ideally implementing a cancellation handler)
The bulk of the processing should not be badly affected by this multiple requests (because most of the heavy work is centralized in the optimized processing flow from the API), but this is for sure increasing our memory consumption and generating multiple locked threads which are not necessary.
The real response time is probably the 3442 millis from the first response, which probably only includes 1 parsing time (the last one), and the other response times are probably mostly idle times waiting for the parse thread to unlock. But seeing as so many threads are waiting idle I can tell this is not good performance-wise.

Thanks for the provided data!

@deiteris
Copy link
Contributor Author

deiteris commented Sep 18, 2021

One thing I've noticed, and probably why this issue is especially noticeable in the JS variant, is that generated client never utilizes Promise.all(iterable) that would enable parallel processing in Node.js (but just concurrent processing in browser) the same way you have it in Scala. This would be equivalent to commonly used Future.sequence(iterable) in your Scala code, and eliminate the issue I've shown if fixed.
https://www.scala-js.org/doc/sjs-for-js/es6-to-scala-part3.html#futures

To confirm this, the search for Promise.all() gives no results in the generated client.
image

@llibarona
Copy link
Member

Yes, we dabbled with this issue a few times, and found no out of the box way to enable true multithreading in the js client of scalaJS
I created an internal task to analyze it once again, but it's hard to draw the line between performance and maintainability/scalability
if we do custom fixes on the js side it will grow really hard to maintain and it won't solve our dependencies issues (AMF)

@deiteris
Copy link
Contributor Author

deiteris commented Sep 20, 2021

That's not exactly what I meant. Just to make sure we're on the same page, I'm saying that that node.js environment allows multithreaded processing for Promise.all(iterable), which makes it a full equivalent of Future.sequence(iterable) that you use for multithreaded processing in Scala. If it's somehow possible to convert scala future sequences to the equivalent iterable of JS promises (like with .toJSPromise), that would significantly improve the situation in JS variant, so you don't have to tackle true multithreading issue.

@llibarona
Copy link
Member

I am not sure I understand
It is totally possible to switch pair specific code (for instance, override some methods in JS), and implement this change
the problem is that this is per-class, so it will be difficult to implement (specially on our dependencies which would be the ones that benefit the most)

I will try and make a small POC to check if we are in the same page and show you so you can tell me if it is what you mean

@llibarona
Copy link
Member

llibarona commented Sep 21, 2021

I gave it a shot, but cannot make the JS version to run in parallel
GH-613/poc-js-threads (524674d)
here you can see what I tried, this should use the native Promise.all, but I think the issue is that this will only run parallel in case the Iterable is async, which I have not found a way to indicate from ScalaJS

For what I read (correct me if I'm wrong), Promise.all(p) is similar to p[0].then(p[1]).then....(p[n]), and the main advantage is that in case p is iterable[async] then the Promise.all will await all

(based on https://stackoverflow.com/a/46086037)

@deiteris
Copy link
Contributor Author

deiteris commented Sep 22, 2021

You're on the right track but something with Scala.js code might be not right. Here are few notes:

  1. In JS, async/await is just a syntactic sugar for function myFunc() { return new Promise((reject, resolve) => {/* resolve(res) or reject(err) */}) } and .then(callback). So it's no different from supplying an array of functions that return promises as function execution result (assuming that .toJSPromise will create a function equivalent to syntax I've shown previously). You can see a demo here: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise/all
  2. Since you've mentioned that .toJSPromise doesn't convert function to async, I've searched the pre-built JS variant code for async/await keywords, and noticed that it doesn't have them either (all async words are related to scala imports, and no await words at all). So it doesn't even leverage the advantages of concurrent execution, it's all synchronized.
    image
  3. If it is as you say, and .toJSPromise doesn't convert function to js.native Promise function, Promise.all() will just return the function references immediately and timers will keep running in background synchronously, and, of course, the test will fail as this is not what you'd expect. So either this conversion mechanism is misused or there's an issue with it (as per my limited understanding of Scala and how Scala.js works).

Unfortunately, I'm unable to build from this branch to analyze transpiled code or see the test, so my current observations are based on the Scala source. Here're the errors I have:

[error] /home/snek/als-git/als-actions/shared/src/main/scala/org/mulesoft/als/actions/codeactions/plugins/conversions/AmfObjectResolver.scala:17:80: value resolve is not a member of amf.shapes.internal.domain.resolution.elements.CompleteShapeTransformationPipeline
[error]     new CompleteShapeTransformationPipeline(anyShape, eh, ProfileNames.RAML10).resolve() match {
[error]                                                                                ^
[error] one error found
[error] /home/snek/als-git/als-suggestions/shared/src/main/scala/org/mulesoft/als/suggestions/plugins/aml/AMLStructureCompletionPlugin.scala:51:33: wrong number of arguments for pattern amf.core.internal.metamodel.Field(type: amf.core.internal.metamodel.Type,value: amf.core.client.scala.vocabulary.ValueType,doc: amf.core.internal.metamodel.domain.ModelDoc,jsonldField: Boolean,deprecated: Boolean)
[error]       case Some(FieldEntry(Field(t: ArrayLike, _, _, _), _))
[error]                                 ^
[error] /home/snek/als-git/als-suggestions/shared/src/main/scala/org/mulesoft/als/suggestions/plugins/aml/webapi/ObjectExamplePropertiesCompletionPlugin.scala:66:89: value resolve is not a member of amf.shapes.internal.domain.resolution.elements.CompleteShapeTransformationPipeline
[error]     new CompleteShapeTransformationPipeline(anyShape, LocalIgnoreErrorHandler, profile).resolve() match {
[error]                                                                                         ^
[error] /home/snek/als-git/als-suggestions/shared/src/main/scala/org/mulesoft/als/suggestions/plugins/aml/webapi/raml/NodeShapeDiscriminatorProperty.scala:36:94: value resolve is not a member of amf.shapes.internal.domain.resolution.elements.CompleteShapeTransformationPipeline
[error]     new CompleteShapeTransformationPipeline(s, LocalIgnoreErrorHandler, ProfileNames.RAML10).resolve()
[error]                                                                                              ^
[error] three errors found
[error] (actionsJS / Compile / compileIncremental) Compilation failed
[error] (suggestionsJS / Compile / compileIncremental) Compilation failed
[error] Total time: 54 s, completed Sep 22, 2021, 4:09:58 AM

@llibarona
Copy link
Member

Unfortunately, I'm unable to build from this branch to analyze transpiled code or see the test, so my current observations are based on the Scala source. Here're the errors I have:

sorry about that!
we are having some issues with our dependency (AMF is close to releasing a major and we are adopting the beta/snapshot which is still changing frequently)
I'm working on fixing this and then I will rebase the branch and let you know!

@llibarona
Copy link
Member

sorry for the long wait! I rebased the branch and it should now be compiling (although it is only illustrative as the POC did not work as intended)

@deiteris
Copy link
Contributor Author

Sorry for long response. I've looked into compiled code, and found that it doesn't use promises either.

I was looking into Scala.js and the only thing I was able to figure out is that tasks could be enqueued using QueueExecutionContext to leverage concurrent processing, but I see there's a problem with promisified version of such queue (scala-js/scala-js#4129).

Due to my limited knowledge I'm out of suggestions for now and don't want to bother with speculations (and we diverged from the original issue anyway). Hope at least something of this was useful and may help in future!

@llibarona
Copy link
Member

No problem!
I think there are some possible optimization in more recent versions of Scala JS, we will definitely keep this in mind when we are able to update versions.
In the mean time we will try to implement cancellation and other suggestions!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants