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

Fix flaky E2E eth_streaming_test JS test #289

Merged
merged 1 commit into from
Jun 10, 2024

Conversation

m-Peter
Copy link
Collaborator

@m-Peter m-Peter commented Jun 8, 2024

Closes: #283

Description


For contributor use:

  • Targeted PR against master branch
  • Linked to Github issue with discussion and accepted design OR link to spec that describes this work.
  • Code follows the standards mentioned here.
  • Updated relevant documentation
  • Re-reviewed Files changed in the Github PR explorer
  • Added appropriate labels

Summary by CodeRabbit

  • Tests
    • Enhanced test case to include streaming of blocks, transactions, and logs using filters.
    • Updated event subscriptions and data handling for new blocks, pending transactions, and logs.

@m-Peter m-Peter added this to the Flow-EVM-M2 milestone Jun 8, 2024
@m-Peter m-Peter self-assigned this Jun 8, 2024
Copy link
Contributor

coderabbitai bot commented Jun 8, 2024

Walkthrough

The changes in eth_streaming_test.js enhance the test case for streaming logs by including blocks and transactions. The modifications involve updating event subscriptions and handling for new blocks, pending transactions, and logs. Additionally, variable names and test case descriptions have been adjusted to reflect these updates.

Changes

Files Change Summary
tests/web3js/eth_streaming_test.js Updated test case description and logic for subscribing to new blocks, transactions, and logs. Renamed variables to improve clarity.

Assessment against linked issues

Objective Addressed Explanation
Fix frequent flaky failures for TestWeb3_E2E/streaming_of_entities_and_subscription_with_filters (#283)

Poem

In the world of Web3's streaming flow,
Blocks, transactions, and logs now glow.
With tests refined and bugs allayed,
Flaky failures have been swayed.
Code in harmony, tests in sync,
A smoother future is on the brink.


Thank you for using CodeRabbit. We offer it for free to the OSS community and would appreciate your support in helping us grow. If you find it useful, would you consider giving us a shout-out on your favorite social media?

Share
Tips

Chat

There are 3 ways to chat with CodeRabbit:

  • Review comments: Directly reply to a review comment made by CodeRabbit. Example:
    • I pushed a fix in commit <commit_id>.
    • Generate unit testing code for this file.
    • Open a follow-up GitHub issue for this discussion.
  • Files and specific lines of code (under the "Files changed" tab): Tag @coderabbitai in a new review comment at the desired location with your query. Examples:
    • @coderabbitai generate unit testing code for this file.
    • @coderabbitai modularize this function.
  • PR comments: Tag @coderabbitai in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:
    • @coderabbitai generate interesting stats about this repository and render them as a table.
    • @coderabbitai show all the console.log statements in this repository.
    • @coderabbitai read src/utils.ts and generate unit testing code.
    • @coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.
    • @coderabbitai help me debug CodeRabbit configuration file.

Note: Be mindful of the bot's finite context window. It's strongly recommended to break down tasks such as reading entire modules into smaller chunks. For a focused discussion, use review comments to chat about specific files and their changes, instead of using the PR comments.

CodeRabbit Commands (invoked as PR comments)

  • @coderabbitai pause to pause the reviews on a PR.
  • @coderabbitai resume to resume the paused reviews.
  • @coderabbitai review to trigger an incremental review. This is useful when automatic reviews are disabled for the repository.
  • @coderabbitai full review to do a full review from scratch and review all the files again.
  • @coderabbitai summary to regenerate the summary of the PR.
  • @coderabbitai resolve resolve all the CodeRabbit review comments.
  • @coderabbitai configuration to show the current CodeRabbit configuration for the repository.
  • @coderabbitai help to get help.

Additionally, you can add @coderabbitai ignore anywhere in the PR description to prevent this PR from being reviewed.

CodeRabbit Configration File (.coderabbit.yaml)

  • You can programmatically configure CodeRabbit by adding a .coderabbit.yaml file to the root of your repository.
  • Please see the configuration documentation for more information.
  • If your editor has YAML language server enabled, you can add the path at the top of this file to enable auto-completion and validation: # yaml-language-server: $schema=https://coderabbit.ai/integrations/schema.v2.json

Documentation and Community

  • Visit our Documentation for detailed information on how to use CodeRabbit.
  • Join our Discord Community to get help, request features, and share feedback.
  • Follow us on X/Twitter for updates and announcements.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

Review details

Configuration used: CodeRabbit UI
Review profile: CHILL

Commits

Files that changed from the base of the PR and between a61971c and 877d553.

Files selected for processing (1)
  • tests/web3js/eth_streaming_test.js (3 hunks)
Additional comments not posted (6)
tests/web3js/eth_streaming_test.js (6)

4-4: Minor formatting improvement in object destructuring.


106-108: Validate that the assertions correctly compare the expected and actual transaction hashes.


111-111: Setting a custom timeout for the test is good practice, especially for tests involving asynchronous operations.


48-58: Similar to the previous comment, verify that the subscription to pending transactions is robust against errors and cleans up correctly.

Verification successful

The search results indicate that the error handling and unsubscribe logic are consistently implemented across different subscription types in the test suite. Each subscription pattern includes error handling via the sub.on("error", ...) method and unsubscribes correctly when the required condition is met.

  • File: tests/web3js/eth_streaming_test.js

    • Lines:
      • subBlocks.on("error", err => { rej(err) })
      • subBlocks.unsubscribe()
      • subTx.on("error", err => { rej(err) })
      • subTx.unsubscribe()
      • subLog.on("error", err => { rej(err) })
      • subLog.unsubscribe()
  • File: tests/web3js/eth_streaming_filters_test.js

    • Lines:
      • subscription.unsubscribe()

The verification confirms that the subscription to pending transactions is robust against errors and cleans up correctly.

Scripts executed

The following scripts were executed for the analysis:


Script:

#!/bin/bash
# Description: Verify that error handling and unsubscribe logic are consistent across all subscription types in the test suite.

# Test: Search for subscription patterns and check for error handling and unsubscribe logic.
rg --type python $'subscribe' --context 10

Length of output: 72



Script:

#!/bin/bash
# Description: Verify that error handling and unsubscribe logic are consistent across all subscription types in the test suite.

# Test: Search for subscription patterns and check for error handling and unsubscribe logic.
rg --type js $'subscribe' --context 10

Length of output: 8292


30-40: Ensure that the subscription to new block headers handles errors appropriately and unsubscribes correctly to avoid memory leaks.

Verification successful

The search results indicate that the error handling and unsubscribe logic are present in the test suite for various subscription types. Specifically, the unsubscribe method is called after the required conditions are met, and errors are handled using the on("error", ...) event listener.

  • File: tests/web3js/eth_streaming_test.js

    • Lines:
      • subBlocks.unsubscribe()
      • subTx.unsubscribe()
      • subLog.unsubscribe()
    • Error Handling: Present in all cases with subBlocks.on("error", ...), subTx.on("error", ...), and subLog.on("error", ...).
  • File: tests/web3js/eth_streaming_filters_test.js

    • Lines:
      • subscription.unsubscribe()

The code appears to handle errors and unsubscribe correctly to avoid memory leaks.

Scripts executed

The following scripts were executed for the analysis:


Script:

#!/bin/bash
# Description: Verify that error handling and unsubscribe logic are consistent across all subscription types in the test suite.

# Test: Search for subscription patterns and check for error handling and unsubscribe logic.
rg --type python $'subscribe' --context 10

Length of output: 72



Script:

#!/bin/bash
# Description: Verify that error handling and unsubscribe logic are consistent across all subscription types in the test suite.

# Test: Search for subscription patterns and check for error handling and unsubscribe logic.
rg --type js $'subscribe' --context 10

Length of output: 8292


65-78: Check that the subscription to logs is correctly handling errors and unsubscribing, especially in the context of multiple asynchronous operations.

Verification successful

The search results indicate that the subscription patterns in the test suite consistently handle errors and include unsubscribe logic. Here are the relevant findings:

  • Error Handling: Each subscription (subBlocks, subTx, subLog) has an on("error", ...) handler that rejects the promise with the error.
  • Unsubscribe Logic: Each subscription (subBlocks, subTx, subLog) includes an unsubscribe() call when the expected condition is met.

Specific Locations:

  • tests/web3js/eth_streaming_test.js
    • Lines involving subBlocks:
      let subBlocks = await ws.eth.subscribe('newBlockHeaders')
      subBlocks.on("error", err => { rej(err) })
      subBlocks.on('data', async block => {
          if (blockTxHashes.length === testValues.length) {
              subBlocks.unsubscribe()
              res()
          }
      })
    • Lines involving subTx:
      let subTx = await ws.eth.subscribe('pendingTransactions')
      subTx.on("error", err => { rej(err) })
      subTx.on('data', async tx => {
          if (txHashes.length === testValues.length) {
              subTx.unsubscribe()
              res()
          }
      })
    • Lines involving subLog:
      let subLog = await ws.eth.subscribe('logs', { address: contractAddress })
      subLog.on("error", err => { rej(err) })
      subLog.on('data', async (data) => {
          if (logTxHashes.length === testValues.length) {
              subLog.unsubscribe()
              res()
          }
      })

Conclusion

The code correctly handles errors and unsubscribes from the subscriptions as expected.

Scripts executed

The following scripts were executed for the analysis:


Script:

#!/bin/bash
# Description: Verify that error handling and unsubscribe logic are consistent across all subscription types in the test suite.

# Test: Search for subscription patterns and check for error handling and unsubscribe logic.
rg --type python $'subscribe' --context 10

Length of output: 72



Script:

#!/bin/bash
# Description: Verify that error handling and unsubscribe logic are consistent across all subscription types in the test suite.

# Test: Search for subscription patterns and check for error handling and unsubscribe logic.
rg --type js $'subscribe' --context 10

Length of output: 8292


const timeout = 30 // test timeout seconds

it('streaming of logs using filters', async() => {
setTimeout(() => process.exit(1), (timeout-1)*1000) // hack if the ws connection is not closed
it('streaming of blocks, transactions, logs using filters', async () => {
Copy link
Contributor

Choose a reason for hiding this comment

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

Tip

Codebase Verification

Ensure proper cleanup of WebSocket connections to avoid potential memory leaks or unintended process exits. The current test files do not show evidence of WebSocket connections being closed.

  • tests/web3js/eth_streaming_test.js
  • tests/web3js/eth_rate_limit_test.js
  • tests/web3js/simple_node_test.js
  • tests/web3js/eth_streaming_filters_test.js
Analysis chain

Ensure proper cleanup of resources to avoid potential memory leaks or unintended process exits.

Scripts executed

The following scripts were executed for the analysis:


Script:

#!/bin/bash
# Description: Verify that all WebSocket connections are properly closed in all test cases.

# Test: Search for WebSocket usage and ensure proper cleanup.
rg --type python $'new Web3' --context 5

Length of output: 72



Script:

#!/bin/bash
# Description: Verify that all WebSocket connections are properly closed in all test cases.

# Test: Search for WebSocket usage and ensure proper cleanup.
rg --type js 'new Web3' --context 5

Length of output: 3444



Script:

#!/bin/bash
# Description: Verify that all WebSocket connections are properly closed in the identified test files.

# Test: Search for WebSocket connection closures.
rg --type js 'ws.close' --context 5

Length of output: 35

@sideninja
Copy link
Contributor

@m-Peter can you please explain the description what was the change you did? with all the lining changes is a bit hard to see.

res()
}
})
})

// wait for subscription for a bit
await new Promise((res, rej) => setTimeout(() => res(), 300))
await new Promise((res, rej) => setTimeout(() => res(), 1000))
Copy link
Collaborator Author

Choose a reason for hiding this comment

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

The 300 ms wait time seems to have been quite low.

let doneTxs = new Promise(async (res, rej) => {
let subTx = await ws.eth.subscribe('pendingTransactions')
Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Moved the

await ws.eth.subscribe

outside the Promise closure.

if (blockCount === testValues.length) {
await subBlocks.unsubscribe()
if (blockTxHashes.length === testValues.length) {
subBlocks.unsubscribe()
Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Removed the await from

await subBlocks.unsubscribe()

as this might be causing the blocking time to increase, hence reaching the timeout limit.

Copy link
Contributor

Choose a reason for hiding this comment

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

the problem with this I see, is that we now ignore any issues with unsubscribing, which would be better if test fails if there are issues. Unsubscribe shouldn't take long, so I rather increase the time a bit than not wait for result

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 took this pattern from eth_streaming_filters_test, which has never failed in CI. If we observe any problem, we can bring it back.

@m-Peter
Copy link
Collaborator Author

m-Peter commented Jun 10, 2024

@sideninja Take this with a grain of salt, as I am not a JS ninja 😇 . But these are the main kind of changes:

My main goal was to make the test file similar to eth_streaming_filters_test, as it does about the same kind of assertions, but in a rather different manner.

This is more or less a trial-and-error kind of change, as it is not possible to re-produce locally. I did run the job about 10 times, and did not get any failures. And this test was quite flaky, would fail about ~3 runs.

@m-Peter m-Peter force-pushed the fix-flaky-eth-streaming-test branch from 877d553 to 1470a1a Compare June 10, 2024 12:38
Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 3

Outside diff range and nitpick comments (2)
tests/web3js/eth_streaming_test.js (2)

Line range hint 8-111: Address the use of async within Promise executors to avoid potential issues with unhandled promise rejections.

The use of async functions as promise executors in lines 31-44, 49-62, and 69-82 is flagged by static analysis as potentially problematic. This can lead to unhandled promise rejections if an error occurs before the await expression. Consider refactoring these to handle errors more robustly or remove the async keyword if not necessary.

- let doneBlocks = new Promise(async (res, rej) => {
+ let doneBlocks = new Promise((res, rej) => {
- let doneTxs = new Promise(async (res, rej) => {
+ let doneTxs = new Promise((res, rej) => {
- let doneAddressLogs = new Promise(async (res, rej) => {
+ let doneAddressLogs = new Promise((res, rej) => {
Tools
Biome

[error] 31-44: Promise executor functions should not be async. (lint/suspicious/noAsyncPromiseExecutor)


[error] 49-62: Promise executor functions should not be async. (lint/suspicious/noAsyncPromiseExecutor)


[error] 69-82: Promise executor functions should not be async. (lint/suspicious/noAsyncPromiseExecutor)


111-111: Adjust the test timeout multiplier to reflect realistic expectations.

The test timeout is set to timeout * 1500, which might be excessively high. Consider adjusting this based on realistic network and processing delays.

Review details

Configuration used: CodeRabbit UI
Review profile: CHILL

Commits

Files that changed from the base of the PR and between 877d553 and 1470a1a.

Files selected for processing (1)
  • tests/web3js/eth_streaming_test.js (3 hunks)
Additional context used
Biome
tests/web3js/eth_streaming_test.js

[error] 31-44: Promise executor functions should not be async. (lint/suspicious/noAsyncPromiseExecutor)


[error] 49-62: Promise executor functions should not be async. (lint/suspicious/noAsyncPromiseExecutor)


[error] 69-82: Promise executor functions should not be async. (lint/suspicious/noAsyncPromiseExecutor)

Additional comments not posted (1)
tests/web3js/eth_streaming_test.js (1)

106-108: Validate the correctness of assertions comparing arrays of transaction hashes.

Ensure that the order of transaction hashes in sentHashes and those received from subscriptions (blockTxHashes, txHashes, logTxHashes) are intended to be in the same order, as assert.deepEqual checks for the same order of elements.

it('streaming of logs using filters', async() => {
setTimeout(() => process.exit(1), (timeout-1)*1000) // hack if the ws connection is not closed
it('streaming of blocks, transactions, logs using filters', async () => {
setTimeout(() => process.exit(1), (timeout - 1) * 1000) // hack if the ws connection is not closed
Copy link
Contributor

Choose a reason for hiding this comment

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

Consider handling the potential unclean exit due to process.exit(1).

Using process.exit(1) as a timeout mechanism can lead to unclean exits and unhandled state. It's better to use proper test timeouts or cleanup mechanisms.

if (blockCount === testValues.length) {
await subBlocks.unsubscribe()
if (blockTxHashes.length === testValues.length) {
subBlocks.unsubscribe()
Copy link
Contributor

Choose a reason for hiding this comment

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

Ensure proper cleanup of subscriptions to avoid potential memory leaks.

The unsubscribing from events is handled within the data event handlers. This might lead to cases where unsubscribing does not occur if no data events are received. Consider adding a timeout or a final block to ensure unsubscribing happens.

Also applies to: 58-58, 78-78

@@ -1,12 +1,12 @@
const conf = require('./config')
const helpers = require('./helpers')
const { assert } = require('chai')
const {Web3} = require("web3");
const { Web3 } = require("web3");
Copy link
Contributor

Choose a reason for hiding this comment

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

Ensure consistent spacing in object destructuring.

- const { Web3 } = require("web3");
+ const {Web3} = require("web3");
Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
const { Web3 } = require("web3");
const {Web3} = require("web3");

let blockCount = 0
let blockHashes = []
let blockTxHashes = []
let subBlocks = await ws.eth.subscribe('newBlockHeaders')
Copy link
Contributor

Choose a reason for hiding this comment

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

why you think this is better?

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 took this pattern from eth_streaming_filters_test, which has never failed in CI. If we observe any problem, we can change it back.

@sideninja
Copy link
Contributor

@m-Peter are you able to re-run the CI couple of time so you can see if things are better?

@m-Peter
Copy link
Collaborator Author

m-Peter commented Jun 10, 2024

@sideninja Yes, I did run the CI quite some times on Saturday. I wasn't able to produce a failure.

@m-Peter m-Peter requested a review from sideninja June 10, 2024 14:55
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
Status: ✅ Done
Development

Successfully merging this pull request may close these issues.

Frequent flaky failures for TestWeb3_E2E
2 participants