Skip to content

Conversation

@DZakh
Copy link
Member

@DZakh DZakh commented Aug 19, 2025

Summary by CodeRabbit

  • Bug Fixes

    • Ensures new events are consistently sorted by block number and log index, even if received out of order, improving stability and predictability.
    • Processes contract registration events in forward (chronological) order for more intuitive behavior.
  • Refactor

    • Streamlined internal naming to reflect forward-ordered data flow.
  • Tests

    • Added tests confirming correct sorting of newly fetched events when the source returns them unsorted.
    • Updated existing tests to align with the new forward-order processing.

@DZakh DZakh requested a review from JonoPrest August 19, 2025 10:18
@coderabbitai
Copy link
Contributor

coderabbitai bot commented Aug 19, 2025

Walkthrough

Renames reversed* arrays to forward-ordered names, switches contract-register and event processing loops to forward iteration, and changes queue maintenance from merge to concatenate-plus-sort using a new comparator. Public APIs updated to use newItems labels. Tests adjusted and a new sorting test added.

Changes

Cohort / File(s) Summary
Event queue maintenance (envio FetchState)
codegenerator/cli/npm/envio/src/FetchState.res
Replace merge-based queue update with concat + in-place sort using new compareBufferItem (order by blockNumber desc, logIndex desc). Rename parameter reversedNewItems→newItems. Remove eventItemGt and mergeSortedEventList.
Contract register iteration (static templates)
codegenerator/cli/templates/static/codegen/src/eventFetching/ChainFetcher.res
runContractRegistersOrThrow now accepts ~itemsWithContractRegister and iterates forward (0..n-1). handleQueryResult param renamed to ~newItems and forwarded accordingly.
Contract register collection (dynamic template mock DB)
codegenerator/cli/templates/dynamic/codegen/src/TestHelpers_MockDb.res.hbs
Rename reversedWithContractRegister→itemsWithContractRegister; populate via push (append) instead of unshift (prepend); pass ~itemsWithContractRegister to ChainFetcher.
Global action and flow alignment
codegenerator/cli/templates/static/codegen/src/globalState/GlobalState.res
Action SubmitPartitionQueryResponse field reversedNewItems→newItems; processing arrays renamed to itemsWithContractRegister/newItems; loops switch to forward iteration; dispatch updated.
Tests: call sites
scenarios/test_codegen/test/ChainManager_test.res
Update FetchState.handleQueryResult labeled arg from ~reversedNewItems to ~newItems.
Tests: FetchState behavior
scenarios/test_codegen/test/lib_tests/FetchState_test.res
Rename labeled args to ~newItems across tests; add test ensuring queue sorts by (blockNumber desc, logIndex desc) when source returns unsorted events.

Sequence Diagram(s)

sequenceDiagram
  autonumber
  participant DS as Data Source
  participant GS as GlobalState
  participant CF as ChainFetcher
  participant FS as FetchState

  DS->>GS: PartitionQueryResponse (raw events)
  GS->>GS: Build itemsWithContractRegister, newItems (forward order)
  GS->>CF: runContractRegistersOrThrow(~itemsWithContractRegister)
  CF->>CF: Iterate forward and process registrations
  GS->>CF: handleQueryResult(~newItems, ...)
  CF->>FS: FetchState.handleQueryResult(~newItems, ...)
  FS->>FS: queue := queue ++ newItems
  FS->>FS: sort(queue) by (blockNumber desc, logIndex desc)
  FS-->>CF: updated state
  CF-->>GS: updated state
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Possibly related PRs

Suggested reviewers

  • JonoPrest
  • JasoonS

Poem

I found new carrots in tidy rows,
From tail to nose the order flows.
No backward hops, just forward cheer,
Sort, then snack—events made clear.
Thump-thump! The queue stands tall—
Descending blocks, I munch them all. 🥕🐇

Tip

🔌 Remote MCP (Model Context Protocol) integration is now available!

Pro plan users can now connect to remote MCP servers from the Integrations page. Connect with popular remote MCPs such as Notion and Linear to add more context to your reviews and chats.

✨ Finishing Touches
🧪 Generate unit tests
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch dz/sorce-items-received-from-source

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ 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>, please review it.
    • 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.
  • 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 gather interesting stats about this repository and render them as a table. Additionally, render a pie chart showing the language distribution in the codebase.
    • @coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.

Support

Need help? Create a ticket on our support page for assistance with any issues or questions.

CodeRabbit Commands (Invoked using PR/Issue comments)

Type @coderabbitai help to get the list of available commands.

Other keywords and placeholders

  • Add @coderabbitai ignore anywhere in the PR description to prevent this PR from being reviewed.
  • Add @coderabbitai summary to generate the high-level summary at a specific location in the PR description.
  • Add @coderabbitai anywhere in the PR title to generate the title automatically.

CodeRabbit Configuration 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

Status, Documentation and Community

  • Visit our Status Page to check the current availability of CodeRabbit.
  • 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: 0

🧹 Nitpick comments (7)
scenarios/test_codegen/test/lib_tests/FetchState_test.res (1)

1911-1949: Good addition: verifies sorting of unsorted inputs

The new test robustly asserts the queue is sorted DESC by (blockNumber, logIndex) regardless of input order. This covers the new concatenate+sort strategy. Consider adding one more case with equal (blockNumber, logIndex) duplicates to assert duplicates are preserved as-is (idempotency), but that’s optional.

codegenerator/cli/templates/dynamic/codegen/src/TestHelpers_MockDb.res.hbs (1)

321-321: Rename and forward iteration alignment: OK

  • Switched from reversedWithContractRegister+unshift to itemsWithContractRegister+push and forward iteration. This removes reversal semantics and matches the new forward-processing approach in ChainFetcher.
  • Passing ~itemsWithContractRegister to ChainFetcher.runContractRegistersOrThrow is consistent with the updated signature.

Optional: If test inputs can arrive in arbitrary order, you may sort itemsWithContractRegister by (blockNumber asc, logIndex asc) before invoking registers to guarantee early registrations are processed first, but current registerDynamicContracts already chooses earliest per address, so this is non-essential.

Also applies to: 358-359, 391-392

codegenerator/cli/npm/envio/src/FetchState.res (3)

549-560: Comparator correctness; minor nit in comment

The comparator orders by blockNumber desc, then logIndex desc — matches the “latest-to-earliest” invariant used throughout (earliest is queue last). Minor: “Comparitor” -> “Comparator” in the comment.

Apply this small fix:

-/*
-Comparitor for two events from the same chain. No need for chain id or timestamp
-*/
+/*
+Comparator for two events from the same chain. No need for chain id or timestamp
+*/

566-567: Outdated docstring vs new behavior

The comment says “newItems are ordered earliest to latest”, but the implementation now accepts unsorted input and sorts internally. Update the doc to avoid confusion.

Apply this clarification:

- newItems are ordered earliest to latest (as they are returned from the worker)
+ newItems may be unsorted (as returned from the worker). The queue is maintained
+ sorted DESC by (blockNumber, logIndex) after concatenation.

629-635: Avoid unnecessary sort when newItems is empty; potential perf win

When newItems is [], we still sort the existing queue every time. Skipping the sort in that case reduces work, especially in steady-state polling or merge-only responses.

Apply this change:

-      ~queue=fetchState.queue
-      ->Array.concat(newItems)
-      // Theoretically it could be faster to asume that
-      // the items are sorted, but there are cases
-      // when the data source returns them unsorted
-      ->Js.Array2.sortInPlaceWith(compareBufferItem),
+      ~queue=
+        if newItems->Utils.Array.isEmpty {
+          fetchState.queue
+        } else {
+          fetchState.queue
+          ->Array.concat(newItems)
+          // Theoretically it could be faster to assume that
+          // the items are sorted, but there are cases
+          // when the data source returns them unsorted
+          ->Js.Array2.sortInPlaceWith(compareBufferItem)
+        },

Optional follow-up: if newItems is typically small, consider inserting each into the already-sorted queue via binary search to avoid full re-sorts. Keep as a future micro-optimization.

codegenerator/cli/templates/static/codegen/src/globalState/GlobalState.res (2)

551-570: Forward iteration and accumulation LGTM; consider avoiding getUnsafe for readability

The change to forward iteration plus push is consistent with the new ordering model. You can simplify the loop and avoid Array.getUnsafe with Array.forEach without cost here.

Apply within this range:

-  for idx in 0 to parsedQueueItems->Array.length - 1 {
-    let item = parsedQueueItems->Array.getUnsafe(idx)
-    if (
-      switch chainFetcher.processingFilters {
-      | None => true
-      | Some(processingFilters) => ChainFetcher.applyProcessingFilters(~item, ~processingFilters)
-      }
-    ) {
-      if item.eventConfig.contractRegister !== None {
-        itemsWithContractRegister->Array.push(item)
-      }
-
-      // TODO: Don't really need to keep it in the queue
-      // when there's no handler (besides raw_events, processed counter, and dcsToStore consuming)
-      newItems->Array.push(item)
-    }
-  }
+  parsedQueueItems->Array.forEach(item => {
+    if (
+      switch chainFetcher.processingFilters {
+      | None => true
+      | Some(processingFilters) => ChainFetcher.applyProcessingFilters(~item, ~processingFilters)
+      }
+    ) {
+      if item.eventConfig.contractRegister !== None {
+        itemsWithContractRegister->Array.push(item)
+      }
+      // TODO: Don't really need to keep it in the queue
+      // when there's no handler (besides raw_events, processed counter, and dcsToStore consuming)
+      newItems->Array.push(item)
+    }
+  })

Also, the TODO suggests we might skip enqueuing when no handler exists; if we decide to keep pushing for raw_events/metrics, consider clarifying that in the comment to avoid future churn. I can help wire a guard once the desired behavior is decided.


572-581: Avoid Utils.magic on empty array branch

The empty-branch cast is unnecessary and unsafe. Prefer a typed binding to keep this branch allocation-free and type-safe.

Replace with:

-let dynamicContracts = switch itemsWithContractRegister {
-| [] as empty =>
-  // A small optimisation to not recreate an empty array
-  empty->(Utils.magic: array<Internal.eventItem> => array<FetchState.indexingContract>)
-| _ =>
-  await ChainFetcher.runContractRegistersOrThrow(
-    ~itemsWithContractRegister,
-    ~config=state.config,
-  )
-}
+let dynamicContracts: array<FetchState.indexingContract> =
+  switch itemsWithContractRegister {
+  | [] => []
+  | _ =>
+    await ChainFetcher.runContractRegistersOrThrow(
+      ~itemsWithContractRegister,
+      ~config=state.config,
+    )
+  }
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

💡 Knowledge Base configuration:

  • MCP integration is disabled by default for public repositories
  • Jira integration is disabled by default for public repositories
  • Linear integration is disabled by default for public repositories

You can enable these sources in your CodeRabbit configuration.

📥 Commits

Reviewing files that changed from the base of the PR and between a021fd1 and 76d0744.

📒 Files selected for processing (6)
  • codegenerator/cli/npm/envio/src/FetchState.res (3 hunks)
  • codegenerator/cli/templates/dynamic/codegen/src/TestHelpers_MockDb.res.hbs (3 hunks)
  • codegenerator/cli/templates/static/codegen/src/eventFetching/ChainFetcher.res (4 hunks)
  • codegenerator/cli/templates/static/codegen/src/globalState/GlobalState.res (6 hunks)
  • scenarios/test_codegen/test/ChainManager_test.res (1 hunks)
  • scenarios/test_codegen/test/lib_tests/FetchState_test.res (24 hunks)
🧰 Additional context used
📓 Path-based instructions (4)
**/*.{res,resi}

📄 CodeRabbit Inference Engine (.cursor/rules/rescript.mdc)

**/*.{res,resi}: Never use [| item |] to create an array. Use [ item ] instead.
Must always use = for setting value to a field. Use := only for ref values created using ref function.
ReScript has record types which require a type definition before hand. You can access record fields by dot like foo.myField.
It's also possible to define an inline object, it'll have quoted fields in this case.
Use records when working with structured data, and objects to conveniently pass payload data between functions.
Never use %raw to access object fields if you know the type.

Files:

  • scenarios/test_codegen/test/ChainManager_test.res
  • codegenerator/cli/templates/static/codegen/src/eventFetching/ChainFetcher.res
  • codegenerator/cli/npm/envio/src/FetchState.res
  • codegenerator/cli/templates/static/codegen/src/globalState/GlobalState.res
  • scenarios/test_codegen/test/lib_tests/FetchState_test.res
codegenerator/cli/templates/{dynamic/**/*.hbs,static/**}

📄 CodeRabbit Inference Engine (.cursor/rules/navigation.mdc)

Templates live under codegenerator/cli/templates: dynamic/ for Handlebars (.hbs), static/ for raw Rescript files copied verbatim.

Files:

  • codegenerator/cli/templates/dynamic/codegen/src/TestHelpers_MockDb.res.hbs
  • codegenerator/cli/templates/static/codegen/src/eventFetching/ChainFetcher.res
  • codegenerator/cli/templates/static/codegen/src/globalState/GlobalState.res
{**/generated/src/**/*.res,codegenerator/cli/templates/static/codegen/src/**/*.res,codegenerator/cli/templates/dynamic/codegen/src/**/*.res}

📄 CodeRabbit Inference Engine (.cursor/rules/navigation.mdc)

Runtime code lives in each project’s generated/src, but template versions (good for editing) are under codegenerator/cli/templates/static/codegen/src or codegenerator/cli/templates/dynamic/codegen/src.

Files:

  • codegenerator/cli/templates/static/codegen/src/eventFetching/ChainFetcher.res
  • codegenerator/cli/templates/static/codegen/src/globalState/GlobalState.res
codegenerator/cli/npm/envio/**

📄 CodeRabbit Inference Engine (.cursor/rules/navigation.mdc)

Library-fied runtime shared across indexers lives in codegenerator/cli/npm/envio.

Files:

  • codegenerator/cli/npm/envio/src/FetchState.res
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
  • GitHub Check: build_and_test
🔇 Additional comments (9)
scenarios/test_codegen/test/ChainManager_test.res (1)

83-85: Renamed label to ~newItems: OK

Callsite updated to ~newItems=[batchItem] aligns with the new FetchState.handleQueryResult signature. Since each iteration passes a single item, ordering semantics are unaffected and the downstream queue sort remains correct.

scenarios/test_codegen/test/lib_tests/FetchState_test.res (1)

1051-1053: Bulk migration to ~newItems: consistent and correct

All updated call sites now pass ~newItems (including empty arrays) to FetchState.handleQueryResult. This is consistent with the updated API and the sorting behavior introduced in FetchState. No logical issues spotted.

Also applies to: 1186-1188, 1302-1303, 1421-1423, 1500-1502, 1548-1550, 1986-1987, 2000-2001, 2067-2072, 2136-2137, 2246-2247, 2263-2264, 2279-2280, 2296-2297, 2320-2321, 2337-2338, 2410-2411, 2446-2450, 2518-2519, 2686-2690, 2731-2735, 2773-2777

codegenerator/cli/npm/envio/src/FetchState.res (1)

569-575: No lingering reversed identifiers found*

Verified via ripgrep that there are no remaining references to reversedNewItems or reversedWithContractRegister, and all handleQueryResult call sites correctly pass ~newItems.

codegenerator/cli/templates/static/codegen/src/eventFetching/ChainFetcher.res (2)

356-357: Forward iteration over itemsWithContractRegister: OK

Renamed arg and switched to forward iteration. Order shouldn’t matter for registerDynamicContracts (it dedups and picks earliest per address), so this is safe. The explicit range loop with getUnsafe is fine here.

Also applies to: 399-401


453-460: Propagating ~newItems to FetchState: OK

Signature and callsite changes are consistent with the new FetchState API; dynamic contract registration is applied prior to queue update as expected.

Also applies to: 469-476

codegenerator/cli/templates/static/codegen/src/globalState/GlobalState.res (4)

585-595: Dispatch payload: newItems and latestFetchedBlock shape look correct

Using newItems and packaging latestFetchedBlock as {blockNumber, blockTimestamp} aligns with FetchState.blockNumberAndTimestamp and PR intent.


654-669: Reducer updates for newItems are consistent

Destructuring newItems and passing it through submitPartitionQueryResponse matches the upstream changes. Looks good.


126-133: Verification complete: no residual reversed* identifiers found

  • Searched all .res and .resi files for reversed(NewItems), reversed(WithContractRegister), and any reversed.*Item(s)
  • No occurrences detected

Switching SubmitPartitionQueryResponse payload to newItems aligns with the PR’s intent and improves clarity. Code changes approved.


482-500: Verified: ChainFetcher.handleQueryResult Accepts ~newItems

  • Definition in codegenerator/cli/templates/static/codegen/src/eventFetching/ChainFetcher.res (line 452) uses ~newItems in its parameter list.
  • All downstream call-sites—including the call in GlobalState.res (line 493)—pass ~newItems.
    No ~reversedNewItems remains. No further action required.

Comment on lines +631 to +633
// Theoretically it could be faster to asume that
// the items are sorted, but there are cases
// when the data source returns them unsorted
Copy link
Collaborator

Choose a reason for hiding this comment

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

Wow interesting 😮

Copy link
Member Author

Choose a reason for hiding this comment

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

This is the issue I shared yesterday about Base internally.

@DZakh DZakh enabled auto-merge (squash) August 20, 2025 12:15
@DZakh DZakh merged commit 7362090 into main Aug 20, 2025
1 check passed
@DZakh DZakh deleted the dz/sorce-items-received-from-source branch August 20, 2025 12:23
@coderabbitai coderabbitai bot mentioned this pull request Sep 5, 2025
14 tasks
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