Skip to content

Simplify InMemoryTable by removing generic wrapper - #1265

Merged
DZakh merged 4 commits into
mainfrom
claude/cool-darwin-NgY3I
Jun 1, 2026
Merged

Simplify InMemoryTable by removing generic wrapper#1265
DZakh merged 4 commits into
mainfrom
claude/cool-darwin-NgY3I

Conversation

@DZakh

@DZakh DZakh commented Jun 1, 2026

Copy link
Copy Markdown
Member

Summary

Removes the generic InMemoryTable.t wrapper type and replaces all usages with direct dict operations. This simplifies the codebase by eliminating an unnecessary abstraction layer that only wrapped dictionary operations with a hash function.

Key Changes

  • InMemoryTable.res: Deleted the generic t<'key, 'val> type and all associated wrapper functions (make, set, get, setByHash, hasByHash, getUnsafeByHash, values)
  • Entity module: Updated type definitions to use dict directly instead of t<...>:
    • indicesSerializedToValue: now dict<indexWithRelatedIds> (keyed by TableIndices.Index.toString)
    • indexFieldNameToIndices: now dict<indicesSerializedToValue> (keyed by TableIndices.Index.getFieldName)
  • Entity operations: Replaced wrapper function calls with direct Dict operations and explicit hash key generation
  • InMemoryStore.res: Simplified rawEvents storage from InMemoryTable.t<rawEventsKey, InternalTable.RawEvents.t> to array<InternalTable.RawEvents.t>, eliminating the need for the hashRawEventsKey function
  • EventUtils.res: Removed unused getEventIdKeyString function
  • EventProcessing.res: Updated to push raw events directly to array instead of using the wrapper's set method

Implementation Details

  • Hash key generation is now explicit at call sites (e.g., index->TableIndices.Index.toString for dictionary keys)
  • The rawEvents field now uses a simple array instead of a keyed dictionary, as events are processed sequentially and don't require lookup by key
  • All index lookups now use Utils.Dict.dangerouslyGetNonOption with explicit key generation

https://claude.ai/code/session_0189AsEpZmVSjhrox3K4PKEc

Summary by CodeRabbit

  • Refactor
    • Improved internal event accumulation and storage for faster, more reliable event processing.
    • Reduced memory overhead and streamlined batching/rollback behavior to improve stability and throughput.

claude added 2 commits June 1, 2026 13:07
…nd indices

The generic InMemoryTable.t<'key, 'val> (dict + hash function) was overkill
for raw events and entity indices. Raw events now use a plain dict keyed by
the event id string, and the index maps use plain dicts keyed by the index's
serialized string. Removes the unused addIdToIndex helper.
Raw events have no in-memory dedup requirement: each event is processed once
per batch and the raw_events table uses a serial primary key, so a plain array
replaces the dict (the key was only ever values()'d at write time). Drops the
now-unused getEventIdKeyString helper. Also iterate index values via
Utils.Dict.forEach instead of materializing an intermediate array.
@coderabbitai

coderabbitai Bot commented Jun 1, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

Warning

Review limit reached

@DZakh, we couldn't start this review because you've reached your PR review rate limit.

More reviews will be available in 16 minutes and 39 seconds. Learn how PR review limits work.

Your organization has run out of usage credits. Purchase more in the billing tab.

⌛ How to resolve this issue?

After more reviews become available, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

We recommend that you space out your commits to avoid hitting the rate limit.

🚦 How do rate limits work?

CodeRabbit enforces hourly rate limits for each developer per organization.

Our paid plans include higher PR review limits than trial, open-source, and free plans. In all cases, reviews become available again over time. During sustained high-volume PR review activity, CodeRabbit may temporarily slow when the next review becomes available.

Please see our Fair Usage Limits Policy for further information.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 2250490d-dac5-490f-83b7-f2b03fd502f5

📥 Commits

Reviewing files that changed from the base of the PR and between b32b5b5 and 8362612.

📒 Files selected for processing (2)
  • packages/envio/src/InMemoryTable.res
  • packages/envio/src/TableIndices.res
📝 Walkthrough

Walkthrough

This PR refactors internal storage mechanisms throughout the event processing pipeline, replacing a generic keyed-table wrapper with direct data structures: raw events migrate from a keyed table to arrays, and entity index metadata migrate from a keyed-table wrapper to nested dicts.

Changes

Storage Data Structure Refactoring

Layer / File(s) Summary
Raw events: keyed table to array
packages/envio/src/InMemoryStore.res, packages/envio/src/EventProcessing.res, packages/envio/src/EventUtils.res
InMemoryStore.rawEvents changes from InMemoryTable.t<rawEventsKey, InternalTable.RawEvents.t> to array<InternalTable.RawEvents.t>. The rawEventsKey type and hashRawEventsKey function are removed. Raw event accumulation switches from keyed set to array push. Persistence receives the array directly, and reset logic uses [] assignment instead of table reinitialization. The getEventIdKeyString helper is removed.
Entity indices: keyed-table wrapper to nested dicts
packages/envio/src/InMemoryTable.res
Entity.indicesSerializedToValue and Entity.indexFieldNameToIndices types change from keyed-table wrapper to nested dict mappings. Index constructor, updateIndices, deleteEntityFromIndices, hasIndex, getUnsafeOnIndex, and addEmptyIndex functions are updated to operate directly on nested dicts via Dict.keysToArray, Dict.getUnsafe, Dict.dangerouslyGetNonOption, and explicit Dict.set calls instead of wrapper accessors. The getRow alias is removed.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Possibly related PRs

  • enviodev/hyperindex#1247: Overlaps on InMemoryStore.rawEvents write/clear pathway used for persistence and rollback diff logic.
  • enviodev/hyperindex#1258: Modifies overlapping index-management functions in InMemoryTable.res (updateIndices, deleteEntityFromIndices, addEmptyIndex, getUnsafeOnIndex).
  • enviodev/hyperindex#1250: Performs similar dict-based refactor of addEmptyIndex and index update paths in InMemoryTable.res.

Suggested reviewers

  • JonoPrest
🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title 'Simplify InMemoryTable by removing generic wrapper' accurately captures the main change: removing the generic InMemoryTable.t wrapper and replacing it with direct dict and array operations across multiple files.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.


Comment @coderabbitai help to get the list of available commands and usage tips.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🧹 Nitpick comments (1)
packages/envio/src/InMemoryTable.res (1)

187-195: ⚡ Quick win

Centralize index key serialization.

Both lookup paths rebuild the storage key by hand even though this table is keyed by TableIndices.Index.toString. That duplication will silently break hasIndex/getUnsafeOnIndex if the serializer ever changes. Please funnel these lookups through one shared helper, ideally the same serializer used on insert.

♻️ Suggested cleanup
+  let serializeIndexLookupKey = (~fieldName, ~operator, ~fieldValueHash) =>
+    `${fieldName}:${(operator :> string)}:${fieldValueHash}`
+
   let hasIndex = (inMemTable: t, ~fieldName, ~operator: TableIndices.Operator.t) =>
     fieldValueHash => {
       switch inMemTable.fieldNameIndices->Utils.Dict.dangerouslyGetNonOption(fieldName) {
       | None => false
       | Some(indicesSerializedToValue) => {
-          let key = `${fieldName}:${(operator :> string)}:${fieldValueHash}`
+          let key = serializeIndexLookupKey(~fieldName, ~operator, ~fieldValueHash)
           indicesSerializedToValue->Utils.Dict.dangerouslyGetNonOption(key) !== None
         }
       }
     }

   let getUnsafeOnIndex = (inMemTable: t, ~fieldName, ~operator: TableIndices.Operator.t) => {
     let getEntity = inMemTable->getUnsafe
     fieldValueHash => {
       switch inMemTable.fieldNameIndices->Utils.Dict.dangerouslyGetNonOption(fieldName) {
       | None =>
         JsError.throwWithMessage(`Unexpected error. Must have an index on field ${fieldName}`)
       | Some(indicesSerializedToValue) => {
-          let key = `${fieldName}:${(operator :> string)}:${fieldValueHash}`
+          let key = serializeIndexLookupKey(~fieldName, ~operator, ~fieldValueHash)
           switch indicesSerializedToValue->Utils.Dict.dangerouslyGetNonOption(key) {

Also applies to: 199-208

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@packages/envio/src/InMemoryTable.res` around lines 187 - 195, The index
lookup builds the storage key inline in hasIndex (and similarly in
getUnsafeOnIndex), duplicating TableIndices.Index.toString logic; extract a
single helper (e.g., serializeIndexKey or TableIndices.Index.toKey) and replace
the inline template `${fieldName}:${operator}:${fieldValueHash}` with a call to
that helper everywhere keys are created/used (including insert code that
currently uses TableIndices.Index.toString), so all lookups/inserts use the same
serializer and avoid drift if serialization changes.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@packages/envio/src/EventProcessing.res`:
- Line 96: The current append-only write
(inMemoryStore.rawEvents->Array.push(rawEvent)) can enqueue duplicates because
rawEvents is only cleared after InMemoryStore.writeBatch; add idempotent
buffering by tracking a companion key set (e.g., rawEventsKeys or rawEventIndex)
keyed by the per-(chainId,eventId) dedupe key and check it before pushing:
compute the unique key from rawEvent, skip push if key exists, otherwise add key
to the set and push the rawEvent; ensure the set is cleared alongside rawEvents
in the same place where InMemoryStore.writeBatch currently clears rawEvents so
replayed batches won't reintroduce duplicates.

---

Nitpick comments:
In `@packages/envio/src/InMemoryTable.res`:
- Around line 187-195: The index lookup builds the storage key inline in
hasIndex (and similarly in getUnsafeOnIndex), duplicating
TableIndices.Index.toString logic; extract a single helper (e.g.,
serializeIndexKey or TableIndices.Index.toKey) and replace the inline template
`${fieldName}:${operator}:${fieldValueHash}` with a call to that helper
everywhere keys are created/used (including insert code that currently uses
TableIndices.Index.toString), so all lookups/inserts use the same serializer and
avoid drift if serialization changes.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 3a8300e0-7b44-4a63-8645-b047fe6d5c09

📥 Commits

Reviewing files that changed from the base of the PR and between 4073059 and d91b40c.

📒 Files selected for processing (4)
  • packages/envio/src/EventProcessing.res
  • packages/envio/src/EventUtils.res
  • packages/envio/src/InMemoryStore.res
  • packages/envio/src/InMemoryTable.res
💤 Files with no reviewable changes (1)
  • packages/envio/src/EventUtils.res

Comment thread packages/envio/src/EventProcessing.res
hasIndex/getUnsafeOnIndex rebuilt the lookup key inline, duplicating
SingleIndex.toString's format. Funnel both insert and lookup through
Index.toStringByParts so the format has a single source of truth.
@DZakh
DZakh enabled auto-merge (squash) June 1, 2026 13:54
@DZakh
DZakh merged commit a64e468 into main Jun 1, 2026
8 checks passed
@DZakh
DZakh deleted the claude/cool-darwin-NgY3I branch June 1, 2026 13:56
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.

2 participants