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

Improve relayer e2e test #1058

Merged
merged 15 commits into from
Jun 11, 2024
Merged

Improve relayer e2e test #1058

merged 15 commits into from
Jun 11, 2024

Conversation

gabriel-aranha-cw
Copy link
Contributor

@gabriel-aranha-cw gabriel-aranha-cw commented Jun 10, 2024

Previously in some cases there was not enough time between the Relayer sending the transaction to Hardhat, and the test sending a request to it in order to get the latest info.

This PR adds a method to send these transactions, and wait with retries until a valid response is received.

Copy link

github-actions bot commented Jun 10, 2024

PR Reviewer Guide 🔍

(Review updated until commit 22aa60f)

⏱️ Estimated effort to review [1-5]

3

🧪 Relevant tests

No

🔒 Security concerns

No

⚡ Key issues to review

Logging and Monitoring:
The newly added sendWithRetry function does not include any logging for the retries or the errors caught. It would be beneficial to add logging to monitor how often retries are needed and the types of errors encountered.

Use of Async Tasks:
The PR correctly uses asynchronous tasks within the tokio context for the retry mechanism in sendWithRetry. However, it's important to ensure that any potentially blocking operations within these tasks are handled appropriately to avoid blocking the tokio executor.

Copy link

PR Code Suggestions ✨

CategorySuggestion                                                                                                                                    Score
Best practice
Add input validation for maxAttempts and delay

To ensure that the sendWithRetry function is more predictable and testable, consider
adding input validation for maxAttempts and delay. Ensure these values are positive
integers to avoid runtime errors or unexpected behavior.

e2e-contracts/integration/test/helpers/rpc.ts [191-204]

 async function sendWithRetry(methodName: string, params: any[], maxAttempts = 3, delay = 1000) {
+    if (maxAttempts <= 0 || delay < 0) throw new Error('Invalid input for maxAttempts or delay');
     let lastError;
     for (let attempt = 1; attempt <= maxAttempts; attempt++) {
         try {
             const result = await send(methodName, params);
             return result;
         } catch (error) {
             lastError = error;
             if (attempt < maxAttempts) {
                 await new Promise(resolve => setTimeout(resolve, delay));
             }
         }
     }
     throw lastError;
 }
 
Suggestion importance[1-10]: 9

Why: Adding input validation for maxAttempts and delay ensures that the function behaves predictably and avoids potential runtime errors, significantly improving the reliability of the code.

9
Limit the scope of lastError to inside the loop

To avoid potential memory leaks or excessive memory usage, consider limiting the scope of
the lastError variable by declaring it inside the for loop. This ensures that the variable
is garbage collected after each iteration if not needed.

e2e-contracts/integration/test/helpers/rpc.ts [192-202]

-let lastError;
 for (let attempt = 1; attempt <= maxAttempts; attempt++) {
+    let lastError;
     try {
         const result = await send(methodName, params);
         return result;
     } catch (error) {
         lastError = error;
         if (attempt < maxAttempts) {
             await new Promise(resolve => setTimeout(resolve, delay));
         }
     }
 }
+throw lastError;
 
Suggestion importance[1-10]: 3

Why: While limiting the scope of lastError to inside the loop is a good practice for memory management, it is not crucial in this context as lastError is needed outside the loop for the final throw statement.

3
Possible bug
Add error handling for undefined lastError

Consider adding error handling for the scenario where lastError is undefined. This can
happen if the send method never throws an error but also does not return a valid result.
You can initialize lastError with a default error message or check if lastError is
undefined before throwing it.

e2e-contracts/integration/test/helpers/rpc.ts [204]

+if (!lastError) {
+  throw new Error('Maximum attempts reached without a successful response');
+}
 throw lastError;
 
Suggestion importance[1-10]: 8

Why: This suggestion addresses a potential bug where lastError could be undefined, improving the robustness of the error handling. It ensures that a meaningful error is thrown if no valid result is obtained after all attempts.

8
Enhancement
Implement an exponential backoff strategy for retries

To improve the robustness of the retry logic, consider implementing a backoff strategy for
the retry delay. This can help in scenarios where repeated requests might be throttled or
need more time to become stable.

e2e-contracts/integration/test/helpers/rpc.ts [200]

-await new Promise(resolve => setTimeout(resolve, delay));
+await new Promise(resolve => setTimeout(resolve, delay * attempt));
 
Suggestion importance[1-10]: 7

Why: Implementing an exponential backoff strategy can enhance the robustness of the retry logic, especially in scenarios where repeated requests might be throttled. This is a valuable enhancement but not critical.

7

@gabriel-aranha-cw gabriel-aranha-cw changed the title relayer Improve relayer e2e test Jun 10, 2024
@gabriel-aranha-cw gabriel-aranha-cw marked this pull request as ready for review June 11, 2024 12:09
@gabriel-aranha-cw gabriel-aranha-cw requested a review from a team as a code owner June 11, 2024 12:09
Copy link

Persistent review updated to latest commit 22aa60f

Copy link

PR Code Suggestions ✨

CategorySuggestion                                                                                                                                    Score
Enhancement
Add error logging in the catch block for better debugging

Consider adding error logging in the catch block of the sendWithRetry function. This will
help in debugging by providing insights into the nature of the errors when retries fail.

e2e-contracts/integration/test/helpers/rpc.ts [202-206]

 try {
     const result = await send(methodName, params);
     if (result !== null) {
         return result;
     }
     if (attempt < maxAttempts) {
         await new Promise(resolve => setTimeout(resolve, delay));
     }
 } catch (error) {
+    console.error(`Attempt ${attempt} for ${methodName} failed with error: ${error}`);
     if (attempt < maxAttempts) {
         await new Promise(resolve => setTimeout(resolve, delay));
     } else {
         throw error;
     }
 }
 
Suggestion importance[1-10]: 9

Why: Adding error logging in the catch block is a valuable enhancement for debugging and understanding the nature of errors during retries. This suggestion is contextually accurate and improves the maintainability of the code.

9
Performance
Use exponential backoff for retry delays to improve network resilience

Implement a backoff strategy for retry delays to handle API rate limits or network issues
more efficiently.

e2e-contracts/integration/test/helpers/rpc.ts [200]

 if (attempt < maxAttempts) {
-    await new Promise(resolve => setTimeout(resolve, delay));
+    const backoffDelay = delay * attempt; // Exponential backoff
+    await new Promise(resolve => setTimeout(resolve, backoffDelay));
 }
 
Suggestion importance[1-10]: 8

Why: Using an exponential backoff strategy for retry delays can significantly improve the resilience of the network calls, especially under high load or rate-limited conditions. This is a performance enhancement that is contextually appropriate.

8
Maintainability
Refactor repeated timeout promises into a separate function for better maintainability

Refactor the repeated await new Promise(resolve => setTimeout(resolve, delay)); into a
separate reusable function to improve code maintainability and reduce redundancy.

e2e-contracts/integration/test/helpers/rpc.ts [200]

-await new Promise(resolve => setTimeout(resolve, delay));
+await delayExecution(delay);
 
+// Elsewhere in the module
+async function delayExecution(ms: number) {
+    return new Promise(resolve => setTimeout(resolve, ms));
+}
+
Suggestion importance[1-10]: 7

Why: Refactoring repeated code into a separate function enhances maintainability and reduces redundancy. This suggestion is contextually accurate and improves the overall code quality.

7
Best practice
Add explicit type annotations to the sendWithRetry function parameters and return type

Add type annotations for the parameters and return type of the sendWithRetry function to
enhance code readability and maintainability.

e2e-contracts/integration/test/helpers/rpc.ts [191]

-export async function sendWithRetry(methodName: string, params: any[], maxAttempts = 5, delay = 2000) {
+export async function sendWithRetry(methodName: string, params: any[], maxAttempts: number = 5, delay: number = 2000): Promise<any> {
     for (let attempt = 1; attempt <= maxAttempts; attempt++) {
         ...
     }
 }
 
Suggestion importance[1-10]: 6

Why: Adding explicit type annotations is a best practice that enhances code readability and maintainability. While this is a minor improvement, it is still beneficial and contextually accurate.

6

@gabriel-aranha-cw gabriel-aranha-cw merged commit b38a9ff into main Jun 11, 2024
31 checks passed
@gabriel-aranha-cw gabriel-aranha-cw deleted the enha-relayer-e2e branch June 11, 2024 12:19
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.

1 participant