[ 
https://issues.apache.org/jira/browse/IGNITE-19535?page=com.atlassian.jira.plugin.system.issuetabpanels:all-tabpanel
 ]

Vyacheslav Koptilin updated IGNITE-19535:
-----------------------------------------
    Description: 
For now, it is required that all public exceptions have to have a constructor 
which accepts trace identifier, error code, error message, and cause:
{code:java}
    InternalException(UUID traceId, int code, String message, Throwable cause)
{code}
Initially, this requirement came from the following considerations:  if you 
trying to implement sync API over async:
{code:java}
/**
 * Returns a future that can be used to extract a result of async computation.
 * This future can be completed with a {@code CustomException} if computation 
failed.
 */
public CompletableFuture<Result> asyncApiCall() {...}

/**
 * Returns a result of sync computation.
 * @throws CustomException if computation failed.
 */
public Result syncApiCall() throws CustomException {
    try {
        return asyncApiCall().join();
    catch (CompletionException e) {
        throw e.getCause(); // simple solution that is not correct
    }
}{code}
Obviously, the user does not expect that `syncApiCall` can throw 
CompletionException instead of CustomException. The simplest option is throwing 
`e.getCause()`, but in that case, the end-user will lose a part of the stack 
trace representing CompletionException (path to the syncApiCall), and he/she 
will only stay with the `remote` stack trace. So, the implementation should 
create a new exception that has the same type as `e.getCause` and should throw 
it.

One of the possible solutions is to write a util method that iterates over all 
predefined constructor signatures, starting from the most specific signature, 
and tries to create the required type of the exception:
{noformat}
Exception(UUID traceId, int code, String message, Throwable cause)
Exception(UUID traceId, int code, String message)
Exception(UUID traceId, int code, Throwable cause)
Exception(int code, String message, Throwable cause)
Exception(int code, String message)
Exception(int code, Throwable cause)
Exception(UUID traceId, int code)
Exception(String msg, Throwable cause)
Exception(int code)
Exception(String msg)
Exception(Throwable cause)
Exception(){noformat}
In that case, our example can be modified as follows:
{code:java}
/**
 * Returns a result of sync computation.
 * @throws CustomException if computation failed.
 */
public Result syncApiCall() throws CustomException {
    try {
        return asyncApiCall().join();
    catch (CompletionException e) {
        throw sneakyThrow(createCopyExceptionWithCause(e)); // here we will 
throw a new exception with the same type 
    }
}  {code}
 

It seems to me, that this approach should resolve the vast majority of use 
cases. Unfortunately, both solutions fail when the CustomException does not 
have any of mentioned constructor signatures:
{noformat}
public class CustomException extends IgniteException {
    int[] partitionIds;

    public CustomException(int[] patrIds) {
        super(SPECIFICE_ERR, "Failed to map an operation for partitions 
[partIds=" + partIds + ']);
    }
}{noformat}
Such cases should be handled differently. This case will be addressed by a 
separate ticket.

  was:
For now, it is required that all public exceptions should have a constructor 
with the following parameters:
{code:java}
    InternalException(UUID traceId, int code, String message, Throwable cause)
{code}
This requirement looks a bit restrictive. Need to find a way to improve 
implementation in order to avoid this.


> Removing the constraint that all public exceptions should implement the 
> mandatory constructor Exception(UUID, int, String, Throwable)
> -------------------------------------------------------------------------------------------------------------------------------------
>
>                 Key: IGNITE-19535
>                 URL: https://issues.apache.org/jira/browse/IGNITE-19535
>             Project: Ignite
>          Issue Type: Improvement
>            Reporter: Vyacheslav Koptilin
>            Assignee: Vyacheslav Koptilin
>            Priority: Major
>              Labels: iep-84, ignite-3
>             Fix For: 3.0.0-beta2
>
>          Time Spent: 10m
>  Remaining Estimate: 0h
>
> For now, it is required that all public exceptions have to have a constructor 
> which accepts trace identifier, error code, error message, and cause:
> {code:java}
>     InternalException(UUID traceId, int code, String message, Throwable cause)
> {code}
> Initially, this requirement came from the following considerations:  if you 
> trying to implement sync API over async:
> {code:java}
> /**
>  * Returns a future that can be used to extract a result of async computation.
>  * This future can be completed with a {@code CustomException} if computation 
> failed.
>  */
> public CompletableFuture<Result> asyncApiCall() {...}
> /**
>  * Returns a result of sync computation.
>  * @throws CustomException if computation failed.
>  */
> public Result syncApiCall() throws CustomException {
>     try {
>         return asyncApiCall().join();
>     catch (CompletionException e) {
>         throw e.getCause(); // simple solution that is not correct
>     }
> }{code}
> Obviously, the user does not expect that `syncApiCall` can throw 
> CompletionException instead of CustomException. The simplest option is 
> throwing `e.getCause()`, but in that case, the end-user will lose a part of 
> the stack trace representing CompletionException (path to the syncApiCall), 
> and he/she will only stay with the `remote` stack trace. So, the 
> implementation should create a new exception that has the same type as 
> `e.getCause` and should throw it.
> One of the possible solutions is to write a util method that iterates over 
> all predefined constructor signatures, starting from the most specific 
> signature, and tries to create the required type of the exception:
> {noformat}
> Exception(UUID traceId, int code, String message, Throwable cause)
> Exception(UUID traceId, int code, String message)
> Exception(UUID traceId, int code, Throwable cause)
> Exception(int code, String message, Throwable cause)
> Exception(int code, String message)
> Exception(int code, Throwable cause)
> Exception(UUID traceId, int code)
> Exception(String msg, Throwable cause)
> Exception(int code)
> Exception(String msg)
> Exception(Throwable cause)
> Exception(){noformat}
> In that case, our example can be modified as follows:
> {code:java}
> /**
>  * Returns a result of sync computation.
>  * @throws CustomException if computation failed.
>  */
> public Result syncApiCall() throws CustomException {
>     try {
>         return asyncApiCall().join();
>     catch (CompletionException e) {
>         throw sneakyThrow(createCopyExceptionWithCause(e)); // here we will 
> throw a new exception with the same type 
>     }
> }  {code}
>  
> It seems to me, that this approach should resolve the vast majority of use 
> cases. Unfortunately, both solutions fail when the CustomException does not 
> have any of mentioned constructor signatures:
> {noformat}
> public class CustomException extends IgniteException {
>     int[] partitionIds;
>     public CustomException(int[] patrIds) {
>         super(SPECIFICE_ERR, "Failed to map an operation for partitions 
> [partIds=" + partIds + ']);
>     }
> }{noformat}
> Such cases should be handled differently. This case will be addressed by a 
> separate ticket.



--
This message was sent by Atlassian Jira
(v8.20.10#820010)

Reply via email to