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

Error object v2 #1371

Merged
merged 21 commits into from
Aug 28, 2024
Merged

Error object v2 #1371

merged 21 commits into from
Aug 28, 2024

Conversation

amorton
Copy link
Contributor

@amorton amorton commented Aug 28, 2024

What this PR does:

Initial error object v2

This is still not in use, it is dead code. Once we get it in we will move from the "playing" package into parent "exceptions" package once we start using it.

This PR is to get it into the main, next setps are:

  • start using it
  • start making it return the errors on the API

Which issue(s) this PR fixes:
Fixes #

Checklist

  • [*] Changes manually tested
  • Automated Tests added/updated
  • [ * Documentation added/updated
  • CLA Signed: DataStax CLA

amorton and others added 8 commits July 16, 2024 12:33
…ption-playing

# Conflicts:
#	src/main/java/io/stargate/sgv2/jsonapi/service/embedding/operation/EmbeddingProvider.java
Co-authored-by: Aaron Morton <aaron.morton@datastax.com>
Co-authored-by: YuqiDu <istimdu@gmail.com>
Co-authored-by: Tatu Saloranta <tatu.saloranta@datastax.com>
Co-authored-by: Mahesh Rajamani <99678631+maheshrajamani@users.noreply.github.com>
Co-authored-by: Jeffrey Carpenter <jeffrey.carpenter@datastax.com>
Co-authored-by: Hazel <hazel.he@datastax.com>
Co-authored-by: Kathiresan Selvaraj <96088452+kathirsvn@users.noreply.github.com>
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
Co-authored-by: Yuqi-Du <138714577+Yuqi-Du@users.noreply.github.com>
@amorton amorton requested a review from a team as a code owner August 28, 2024 08:26
pom.xml Outdated Show resolved Hide resolved
* and this exception is raised if the template and the code do not match. Note that it is OK for
* the code to provide more variables than the template uses, but not the other way around.
*/
public class UnresolvedErrorTemplateVariable extends RuntimeException {
Copy link
Contributor

@tatu-at-datastax tatu-at-datastax Aug 28, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

thought: we should probably have a root-level ultimate base exception for validity problems -- something like DataApiValidityException (or whatever name is) extending RuntimeException, being extended by this and other types (but not APIException).

This is just a follow-up change idea, no need to change for this PR.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

discussed, maybe later we can look at more base exceptions

*/
public class ServerException extends APIException {

public static final ErrorFamily FAMILY = ErrorFamily.SERVER;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is problematic:

  1. Should not really be public -- why would it be needed from outside (probably protected for sub-classes)
  2. Name is very vague, esp. when referred to from sub-classes. Maybe SERVER_EXCEPTION_FAMILY
  3. ... or is this constant needed at all? Just use ErrorFamily.SERVER directly in sub-class.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

idea was for subclasses to all reference the same definition of the family during construction, e.g. in the ctor for the Code enum


public class DatabaseException extends ServerException {

public static final Scope SCOPE = Scope.DATABASE;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same as with other constants:

  1. Should not be public?
  2. Name is very generic/vague, probably DEFAULT_SCOPE.
  3. ... but is this constant actually needed at all? Seems like its only used from sub-class.

Not 100% sure what to do with it, but just seems wrong. (1 and 2 easy enough to change, but eliminating need would be best)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

idea was for the Code enum to be able to refence a central definition while it is constructing

private final ErrorTemplate<DatabaseException> template;

Code() {
template = ErrorTemplate.load(DatabaseException.class, FAMILY, SCOPE, name());
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Loading from Enum instance constructors is extremely fragile -- I think it happens at class loading time -- and could cause really gnarly failure modes.

But then again, as long as our test coverage is good, maybe that's not a big issue.

Copy link
Contributor

@tatu-at-datastax tatu-at-datastax Aug 28, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ohhhh. MAybe that is worse -- this is called once per each Enum value? Does it really try to load something, or is that misnamed method? (to me, loading means reading YAML config file).

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The ErrorTemplate will cause the ErrorConfig to load, which happens once and loads all of the YAML config at once and only once.

Then this call to load() will fail if there is an error code defined in code but not in the yaml, so we want that failure to happen.

* <p>See {@link APIException}
*/
@FunctionalInterface
public interface ErrorScope {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't understand this -- why not just a simple Enum? What is the point in this contraption?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

discussed - make it clear this is here because the scope is disjoint sets for the diff families

*
* <p>Never {@code null}, uses "" for no scope. See {@link ErrorScope}
*/
public final String scope;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Related to oddity of ErrorScope, I am not sure why we use untyped String, then functional interface for ErrorScope? Why not plain old Enum value?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

the scope list is different for each family so there is no single enum with them all. i.e. we do not use the FILTER scope with a SERVER family.

super(errorInstance);
}

public enum Code implements ErrorCode<EmbeddingProviderException> {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is the idea it's ok to have same "code" (like CLIENT_ERROR) for multiple scopes? I guess that is the case.

One concern is that to know which problem you have, it is no longer possible to group failures solely by Code, as that will conflate Codes with different Scopes.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

will add a fully qualified code to the API exception, we can then use it later for metrics etc

*
* @return Instance of {@link APIException} the error code represents.
*/
default T get() {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is not a good method name. Get what?

So suggesting we rename to something like "toApiException()" or "asApiException()"

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

will look for better name - discussed

* "key-2", "value-2"]
* @return Instance of {@link APIException} the error code represents.
*/
default T get(String... values) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do we need this method? We already have overload with Map, why/where do we need this?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

there to make it easy when there is only one or two variables to pass. The idea was to make it as easy as possible to instantiate exceptions

* Then use {@link #getErrorDetail(ErrorFamily, String, String)} if you want the template, or the
* use {@link #getSnippetVars()} when running templates.
*
* <p>If you need more control use the {@link #initializeFromYamlResource(String)} before any code
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

... not necessarily easy/possible to do, due to class loading/initialization constraints.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

note to be - revisit the comment with how this is used in testing

*
* @return
*/
public List<ErrorDetail> requestErrors() {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why do we have this?

*
* @return
*/
public List<ErrorDetail> serverErrors() {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why do we have this?

@tatu-at-datastax
Copy link
Contributor

tatu-at-datastax commented Aug 28, 2024

@amorton @Hazel-Datastax

Ok, so. I have a few concerns with the code included in here, at implementation details level. But at high level it makes sense; and so with the expectation of thorough testing (included in PR) I will approve this.
But I have specific questions/suggestions/concerns about following things which we should discuss and maybe address either as part of this PR, or follow up.
So:

  1. Why is ErrorScope not a simple Enum? Making it a functional interface to produce String makes no immediate sense to me at all; why complication, loss of type-safety?
  2. (smaller concern) Splitting of Code and Scope allows (I think) duplicates of Code for different error types; and so we no longer have single unique identifier -- formerly this was effectively by using "scope" equivalent as prefix ("SERVER_xxx", "EMBEDDING_xxx").
  3. (smallish renaming) ErrorCode.get() is not a good name: from what it does, should probably be .asApiException() or .toApiException().

(earlier I had this too:

  • (small visibility concern) Why is ErrorConfig.serverErrors() and .requestErrors() exposed as public accessors?

but I resolved it by removing/reducing access)

Copy link
Contributor

@tatu-at-datastax tatu-at-datastax left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

As per my comment above, conditionally approving, just need to address concerns before or after merging.

@amorton
Copy link
Contributor Author

amorton commented Aug 28, 2024

follow up tickets, and there will soon be a PR that makes this usable that will move it from the "playing" package

#1374
#1375
#1377

merging

@amorton amorton merged commit 71e3bb1 into main Aug 28, 2024
3 checks passed
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

Successfully merging this pull request may close these issues.

3 participants