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

Resilience of the buildTarget/* requests #204

Open
adpi2 opened this issue Jul 29, 2021 · 8 comments
Open

Resilience of the buildTarget/* requests #204

adpi2 opened this issue Jul 29, 2021 · 8 comments

Comments

@adpi2
Copy link
Member

adpi2 commented Jul 29, 2021

The requests of the form buildTarget/* take a sequence of build target identifiers as paramater and expect a sequence of result items as response, each item corresponding to one of the build target identifier.

For example, the buildTarget/source params are

export interface SourcesParams {
  targets: BuildTargetIdentifier[];
}

and result is:

export interface SourcesResult {
  items: SourcesItem[];
}

But what if the request fails on a single build target:

  1. should the server fails the entire request and returns an error response?
  2. should it returns an incomplete result and ignore the failed build target?

sbt does 1 which is probably not a good idea because we don't want the entire build import to fail if one build target fails.
Bloop does 2 but that's not fully satisfying either because the build client doesn't really know why some build targets are missing.

So I propose to augment every buildTarget/* response with a new failedTargets field which must contain the build target failures.

export interface FailedBuildTarget {
  target: BuildTargetIdentifier;
  code: integer;
  message: string;
  data?: string | number | boolean | array | object | null;
}
export interface SourcesResult {
  items: SourcesItem[];
  failedTargets: FailedBuildTarget[];
}

Do you think this is the correct approach for this problem?

@jastice
Copy link
Member

jastice commented Jul 29, 2021

Your suggestion sounds reasonable. What should code be in FailedBuildTarget?

@adpi2
Copy link
Member Author

adpi2 commented Jul 29, 2021

What should code be in FailedBuildTarget?

Probably we don't need it.

adpi2 added a commit to adpi2/sbt that referenced this issue Jul 29, 2021
The request of the form buildTarget/* often take a sequence of build
targets as parameter. So far if there is an error on a single build
target, the entire request fails.
This is not the best because the client wants the result of the other
build targets anyway:
For example:
- workspace/buildTargets: if one build target has an invalid Scala
version we still want to import the other ones
- buildTarget/scalacOptions: if a dependency cannot be resolved we still
want to import the build targets that do not depend on it
- buildTarget/scalaMainClasses: if buildTarget does not compile we still
want the main classes of the other targets
...

The change is to respond to BSP requests with the successful
build targets and  to ignore the failed ones.
This is implemented the same in Bloop since before BSP in sbt.

In  build-server-protocol/build-server-protocol#204,
I made a proposal to also add the failed build targets in the response.
@ckipp01

This comment has been minimized.

@julienrf
Copy link
Collaborator

What would IDEs do with the failures? Would they show a message saying that some build targets could not be resolved?

@ckipp01
Copy link
Member

ckipp01 commented Jul 30, 2021

What would IDEs do with the failures? Would they show a message saying that some build targets could not be resolved?

I believe on the Metals side we more or less swallow it. In the buildTarget/sources example it seems that in this scenario with Bloop if we successfully import the build but not all build targets were able to retrieve their their sources for example, then we'd still have a successful import, but one build target wouldn't have any recognized sources. As you can imagine that can cause confusion. If I'm not mistaken we actually had an issue with this before where the build target was recognized, but nothing would work with it, and it was hard to track down the why behind it. I'll have to dig a bit deeper though to see in these situations exactly what is happening. My thought behind this was that on the build server side if they know more information behind why something is failing, it may be helpful to forward that information back to the client in order for the client to at least log it, which may greatly help with debugging the issue. Again, just thinking out loud about this.

@adpi2
Copy link
Member Author

adpi2 commented Jul 30, 2021

Either that or do you think it'd be useful to also have space for an error or message?

Yes of course. I am just saying that code is probably useless but message should be mandatory and we can also have an optional data.

export interface FailedBuildTarget {
  target: BuildTargetIdentifier;
  message: string;
  data?: string | number | boolean | array | object | null;
}

What would IDEs do with the failures? Would they show a message saying that some build targets could not be resolved?

It's up to them.

Also I agree with @ckipp01 that it's not a good idea to swallow a failure. In general I think if one of the buildTarget/scalacOptions, buildTarget/dependencySources, buildTarget/sources fails the client should clearly states that the import of some build targets failed.

In Metals, the build target error messages could be displayed in the doctor section.

Build target scalacOptions sources dependencySources message
foo
bar x Error downloading some-library_2.13:1.2.3
fizz x Failed source generation

@ckipp01
Copy link
Member

ckipp01 commented Jul 30, 2021

Yes of course. I am just saying that code is probably useless but message should be mandatory and we can also have an optional data.

Ach, I totally misread the first post and missed that you already had it all defined 😆. Sorry!

adpi2 added a commit to adpi2/sbt that referenced this issue Jul 30, 2021
The request of the form buildTarget/* often take a sequence of build
targets as parameter. So far if there is an error on a single build
target, the entire request fails.
This is not the best because the client wants the result of the other
build targets anyway:
For example:
- workspace/buildTargets: if one build target has an invalid Scala
version we still want to import the other ones
- buildTarget/scalacOptions: if a dependency cannot be resolved we still
want to import the build targets that do not depend on it
- buildTarget/scalaMainClasses: if buildTarget does not compile we still
want the main classes of the other targets
...

The change is to respond to BSP requests with the successful
build targets and  to ignore the failed ones.
This is implemented the same in Bloop since before BSP in sbt.

In  build-server-protocol/build-server-protocol#204,
I made a proposal to also add the failed build targets in the response.
Nirvikalpa108 pushed a commit to Nirvikalpa108/sbt that referenced this issue Oct 30, 2021
The request of the form buildTarget/* often take a sequence of build
targets as parameter. So far if there is an error on a single build
target, the entire request fails.
This is not the best because the client wants the result of the other
build targets anyway:
For example:
- workspace/buildTargets: if one build target has an invalid Scala
version we still want to import the other ones
- buildTarget/scalacOptions: if a dependency cannot be resolved we still
want to import the build targets that do not depend on it
- buildTarget/scalaMainClasses: if buildTarget does not compile we still
want the main classes of the other targets
...

The change is to respond to BSP requests with the successful
build targets and  to ignore the failed ones.
This is implemented the same in Bloop since before BSP in sbt.

In  build-server-protocol/build-server-protocol#204,
I made a proposal to also add the failed build targets in the response.
Nirvikalpa108 pushed a commit to Nirvikalpa108/sbt that referenced this issue Oct 30, 2021
The request of the form buildTarget/* often take a sequence of build
targets as parameter. So far if there is an error on a single build
target, the entire request fails.
This is not the best because the client wants the result of the other
build targets anyway:
For example:
- workspace/buildTargets: if one build target has an invalid Scala
version we still want to import the other ones
- buildTarget/scalacOptions: if a dependency cannot be resolved we still
want to import the build targets that do not depend on it
- buildTarget/scalaMainClasses: if buildTarget does not compile we still
want the main classes of the other targets
...

The change is to respond to BSP requests with the successful
build targets and  to ignore the failed ones.
This is implemented the same in Bloop since before BSP in sbt.

In  build-server-protocol/build-server-protocol#204,
I made a proposal to also add the failed build targets in the response.
@lefou
Copy link
Contributor

lefou commented Feb 19, 2024

This approach makes it necessary to extends each response type with failure information. Wouldn't it be easier to just wrap the specific result type in some generic Either-like result type? The only reason to not do this is, that your proposed solution could be delivered in a backward compatible ways. Whereas my suggestion to use a result wrapper isn't compatible at all.

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

5 participants