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

feat(filter)!: return error codes instead of throwing errors #1971

Merged
merged 21 commits into from
May 9, 2024

Conversation

danisharora099
Copy link
Collaborator

@danisharora099 danisharora099 commented Apr 23, 2024

Problem

The Filter implementation currently throws Error to handle non-success scenarios.
This needs a change to using error codes instead: #1694

Solution

Move all throws to returning error codes

Notes

Copy link

github-actions bot commented Apr 23, 2024

size-limit report 📦

Path Size Loading time (3g) Running time (snapdragon) Total time
Waku node 181.24 KB (+0.02% 🔺) 3.7 s (+0.02% 🔺) 13.7 s (+10.02% 🔺) 17.3 s
Waku Simple Light Node 181.32 KB (+0.06% 🔺) 3.7 s (+0.06% 🔺) 10.9 s (-52.21% 🔽) 14.5 s
ECIES encryption 23.12 KB (+0.19% 🔺) 463 ms (+0.19% 🔺) 4.5 s (-9.39% 🔽) 4.9 s
Symmetric encryption 22.53 KB (-0.1% 🔽) 451 ms (-0.1% 🔽) 4 s (-6.95% 🔽) 4.4 s
DNS discovery 72.5 KB (+0.11% 🔺) 1.5 s (+0.11% 🔺) 7.4 s (-51.72% 🔽) 8.9 s
Peer Exchange discovery 74.09 KB (-0.02% 🔽) 1.5 s (-0.02% 🔽) 9.2 s (-3.59% 🔽) 10.7 s
Local Peer Cache Discovery 67.63 KB (-0.03% 🔽) 1.4 s (-0.03% 🔽) 9.1 s (-39.91% 🔽) 10.5 s
Privacy preserving protocols 38.88 KB (+0.04% 🔺) 778 ms (+0.04% 🔺) 8.3 s (-14.03% 🔽) 9.1 s
Waku Filter 111.72 KB (-0.07% 🔽) 2.3 s (-0.07% 🔽) 22.5 s (+72.79% 🔺) 24.8 s
Waku LightPush 110.26 KB (+0.1% 🔺) 2.3 s (+0.1% 🔺) 16.7 s (+6.03% 🔺) 18.9 s
History retrieval protocols 110.76 KB (+0.02% 🔺) 2.3 s (+0.02% 🔺) 22.3 s (+143.7% 🔺) 24.5 s
Deterministic Message Hashing 7.24 KB (-0.15% 🔽) 145 ms (-0.15% 🔽) 2.3 s (+129.65% 🔺) 2.4 s

@danisharora099 danisharora099 marked this pull request as ready for review April 23, 2024 12:45
@danisharora099 danisharora099 requested a review from a team as a code owner April 23, 2024 12:45
return {
failures: [
{
error: ProtocolError.TOPIC_DECODER_MISMATCH
Copy link
Member

Choose a reason for hiding this comment

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

When passing multiple decoders, this does not tell the user which decoder is mismatched.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

It's not about which decoder, but about a mismatch in general.

Example:
DecoderA: /abc/xyz
DecoderB: /qwe/rty

So here, it's not about which decoder doesn't match the other, but the binary result that either they all match, or they don't.

Wdyt? @adklempner

Copy link
Member

Choose a reason for hiding this comment

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

The comparison is made between the decoder's pubsub topic and the subscription manager's pubsub topic. So you can have a case where all the decoders match each other but do not match the pubsub topic set in the subscription manager. This can be quickly detected if we log both pubsub topics.

Copy link
Member

Choose a reason for hiding this comment

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

I think if we go with changes described in this issue then it won't be as much of a problem: #1981

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

i like this change

error: ProtocolError.TOPIC_DECODER_MISMATCH
}
],
successes: []
Copy link
Member

Choose a reason for hiding this comment

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

I'm concerned with how much implicit state to handle is being passed on to the library consumer. Right now the SDKProtocolResult can mean several different things:

  1. the state here, when there is a single error in failures and no item in successes, which is like a "total failure"
  2. some items in successes and some items in failures, which means some subscriptions were successful
  3. some items in successes and no items in failures, which means all subscriptions were successful
  4. no items in successes and no items in failures, which should not happen, but technically is possible according to the type

It would be better if this function could only ever return either a failure or a success. But that might mean limiting it to one decoder at a time.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

It would be better if this function could only ever return either a failure or a success. But that might mean limiting it to one decoder at a time.

Correct. nwaku/protobuf currently allows us to pass multiple content topics to the same request -- limiting to one decoder (->one content topic) on js-waku API side will be limiting of that.
This seems to be an API change that we will introduce.

I agree with your overall comment of the potential mindload on library consumer, as it could be confusing to have both failure and success haha.

Not sure what the right step is.
We can track this in an issue and discuss them in the next PM call @adklempner

Copy link
Member

Choose a reason for hiding this comment

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

I created an issue for it, let me know if I captured it correctly: #1981

packages/interfaces/src/misc.ts Show resolved Hide resolved
@danisharora099 danisharora099 requested review from a team and adklempner April 25, 2024 12:01
Copy link
Collaborator

@weboko weboko left a comment

Choose a reason for hiding this comment

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

We need to postpone this PR in favor of #1958

return {
success: null,
failure: {
error: ProtocolError.GENERIC_FAIL,
Copy link
Collaborator

Choose a reason for hiding this comment

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

shouldn't it be more concrete that generic?

Copy link
Collaborator

Choose a reason for hiding this comment

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

I think we should make errors as informative as possible (without creating a mess) as it is the only thing developer would get, so let's try not to use GENERIC_FAIL imho

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

shouldn't it be more concrete that generic?

no, this is also what we do during any lightpush request.

if pipe fails unexpectedly, we can't be sure of what went wrong.

Copy link
Collaborator

Choose a reason for hiding this comment

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

is there no response that we can read? I expect full node potentially send some message if it is on the side of it, but if failure is in pipe - then it can be generic (though in that case I still think there is a reason we can root cause)

can you repro unexpected fails of pipe?

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

can you repro unexpected fails of pipe?

no test vector as such for that

--

this try catch case actually handles error if error is thrown and there is no defined response from the pipe operation

if you follow the next few lines you will see we also handle errors based on bad values of res

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

eg:

    try {
      res = await pipe(
        [query.encode()],
        lp.encode,
        stream,
        lp.decode,
        async (source) => await all(source)
      );
    } catch (err) {
      log.error("Failed to send waku light push request", err);
      return {
        success: null,
        failure: {
          error: ProtocolError.GENERIC_FAIL,
          peerId: peer.id
        }
      };
    }

    const bytes = new Uint8ArrayList();
    res.forEach((chunk) => {
      bytes.append(chunk);
    });

    let response: PushResponse | undefined;
    try {
      response = PushRpc.decode(bytes).response;
    } catch (err) {
      log.error("Failed to decode push reply", err);
      return {
        success: null,
        failure: {
          error: ProtocolError.DECODE_FAILED,
          peerId: peer.id
        }
      };
    }

    if (!response) {
      log.error("Remote peer fault: No response in PushRPC");
      return {
        success: null,
        failure: {
          error: ProtocolError.REMOTE_PEER_FAULT,
          peerId: peer.id
        }
      };
    }

    if (!response.isSuccess) {
      log.error("Remote peer rejected the message: ", response.info);
      return {
        success: null,
        failure: {
          error: ProtocolError.REMOTE_PEER_REJECTED,
          peerId: peer.id
        }
      };
    }

Copy link
Collaborator

Choose a reason for hiding this comment

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

I believe in that case GENERIC_FAIL on pipe means - failed to send a request, as it fails locally.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

I believe in that case GENERIC_FAIL on pipe means - failed to send a request, as it fails locally.

hmm, could it fail while receiving a response for the remote peer?
are we sure about that?

Copy link
Collaborator

Choose a reason for hiding this comment

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

dunno, I think we need to investigate it - if it bothers us

@danisharora099 danisharora099 self-assigned this Apr 30, 2024
`Filter unsubscribe all request ${requestId} failed with status code ${statusCode}: ${statusDesc}`
);
return {
failure: {
error: ProtocolError.REMOTE_PEER_REJECTED,
Copy link
Collaborator

Choose a reason for hiding this comment

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

here and in similar places - can we map statusCode to some tangible error

like in case if full node has no capacity (ddos etc) - it might return some particular code

to cover this I think we should do follow up, and sync with @NagyZoltanPeter of what error codes are expected here and in similar places

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

agree - that would be useful.

we do follow generic error handling based on statusCode:

 const { statusCode, requestId, statusDesc } =
      FilterSubscribeResponse.decode(res[0].slice());

    if (statusCode < 200 || statusCode >= 300) {
      log.error(
        `Filter unsubscribe all request ${requestId} failed with status code ${statusCode}: ${statusDesc}`
      );
      return {
        failure: {
          error: ProtocolError.REMOTE_PEER_REJECTED,
          peerId: peer.id
        },
        success: null
      };
    }

creating an issue to track this: #1999

@@ -381,7 +418,7 @@ class FilterSDK extends BaseProtocolSDK implements IFilterSDK {
return toAsyncIterator(this, decoders);
}

private getPubsubTopics<T extends IDecodedMessage>(
private getUniquePubsubTopics<T extends IDecodedMessage>(
Copy link
Collaborator

Choose a reason for hiding this comment

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

why unique? it just returns pubsubTopics without duplicates

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

yes, so it only returns unique pubsub topics, right? ^.^

);
return {
failure: {
error: ProtocolError.REMOTE_PEER_FAULT,
Copy link
Collaborator

Choose a reason for hiding this comment

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

it's more of no response error than remote peer fault

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

changed that to be a GENERIC_FAIL as done for other pipe failures. we can discuss the right error code here if required

subscription = await waku.filter.createSubscription(TestShardInfo);
const { error, subscription: _subscription } =
await waku.filter.createSubscription(TestShardInfo);
if (!error) subscription = _subscription;
Copy link
Collaborator

Choose a reason for hiding this comment

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

if error - let's throw from beforeEachCustom so that we see it failed to get subscription

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

done, thanks! also updated in other tests

@weboko weboko self-requested a review May 7, 2024 09:41
@danisharora099 danisharora099 merged commit 4eb06c6 into master May 9, 2024
10 checks passed
@danisharora099 danisharora099 deleted the feat/filter-error-codes branch May 9, 2024 11:21
@weboko weboko mentioned this pull request May 9, 2024
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