Skip to content

Conversation

@Rcc999
Copy link
Contributor

@Rcc999 Rcc999 commented Dec 22, 2025

Description

Closes: #214

Summary by Sourcery

Return structured insert status from block storage writes and propagate it through the indexer.

New Features:

  • Expose a typed InsertBlockResult union to indicate whether a block was inserted, updated, or skipped.

Enhancements:

  • Change CFStorage.insertBlockInfo to return InsertBlockResult instead of boolean, including batching a pre-check and upsert query.
  • Update indexer logging to use the new InsertBlockResult and clarify skip reasons.

Tests:

  • Adjust existing storage and stale block protection tests to assert on InsertBlockResult outcomes instead of boolean flags.

Signed-off-by: Rayane Charif <rayane.charif@gonative.cc>
@Rcc999 Rcc999 requested a review from a team as a code owner December 22, 2025 16:33
@sourcery-ai
Copy link
Contributor

sourcery-ai bot commented Dec 22, 2025

Reviewer's Guide

Refactors CFStorage.insertBlockInfo to return a typed InsertBlockResult describing whether a block write was inserted, updated, or skipped, wires this result through the Indexer, optimizes the underlying D1 operations with a batched existence check + upsert, and updates tests and types accordingly.

Sequence diagram for Indexer.processBlock using InsertBlockResult

sequenceDiagram
    actor Node
    participant Indexer
    participant Storage
    participant CFStorage
    participant D1
    participant Logger

    Node->>Indexer: processBlock(blockInfo, rawBlockBuffer)
    Indexer->>Storage: insertBlockInfo(blockInfo)
    activate Storage
    Storage->>CFStorage: insertBlockInfo(blockInfo)
    activate CFStorage
    CFStorage->>D1: batch([checkRowStmt, insertStmt])
    D1-->>CFStorage: [checkResult, upsertResult]
    CFStorage-->>Storage: InsertBlockResult(status, changed)
    deactivate CFStorage
    Storage-->>Indexer: InsertBlockResult(status, changed)
    deactivate Storage

    alt result.changed is false
        Indexer->>Logger: debug(msg=Skipping block, status=result.status)
        Indexer-->>Node: return
    else result.changed is true
        Indexer->>Logger: debug(msg=Processing block, status=result.status)
        Indexer-->>Node: continue processing
    end
Loading

Class diagram for updated Storage, CFStorage, Indexer, and InsertBlockResult

classDiagram
    class BlockQueueRecord
    class BtcNet

    class InsertBlockResult {
      <<type_alias>>
      +status
      +changed
    }

    class Storage {
      <<interface>>
      +markBlockAsProcessed(hash, network BtcNet) Promise~void~
      +insertBlockInfo(blockMessage BlockQueueRecord) Promise~InsertBlockResult~
      +getLatestBlockHeight(network BtcNet) Promise~number_or_null~
      +getChainTip(network BtcNet) Promise~number_or_null~
      +setChainTip(height, network BtcNet) Promise~void~
    }

    class CFStorage {
      +d1
      +insertBlockInfo(b BlockQueueRecord) Promise~InsertBlockResult~
    }

    class Indexer {
      +storage Storage
      +processBlock(blockInfo BlockQueueRecord, rawBlockBuffer) Promise~void~
    }

    Storage <|.. CFStorage
    Indexer --> Storage : uses
    Indexer --> InsertBlockResult : consumes
    CFStorage --> InsertBlockResult : produces
Loading

File-Level Changes

Change Details Files
Change insertBlockInfo to return a structured InsertBlockResult instead of a boolean and batch the pre-insert check with the upsert.
  • Replace the insertBlockInfo return type from Promise to Promise.
  • Add a SELECT existence check query and run it together with the INSERT ... ON CONFLICT upsert in a single d1.batch call.
  • Derive inserted/updated/skipped status based on whether a row previously existed and whether the upsert changed any rows, and throw if the batch responses are missing.
  • Map the derived status into a discriminated union with changed: true/false.
packages/btcindexer/src/cf-storage.ts
Propagate InsertBlockResult through the Indexer and improve logging around skipped blocks.
  • Update Indexer.processBlock to consume InsertBlockResult from storage.insertBlockInfo instead of a boolean flag.
  • Use the changed property to decide whether to skip further processing when no changes occurred.
  • Enhance debug logging message and include the InsertBlockResult.status when skipping a block.
packages/btcindexer/src/btcindexer.ts
Introduce the InsertBlockResult type and update the Storage interface to use it.
  • Define InsertBlockResult as a discriminated union of inserted, updated, and skipped outcomes with an associated changed flag.
  • Change the Storage interface insertBlockInfo signature to return InsertBlockResult.
packages/btcindexer/src/models.ts
packages/btcindexer/src/storage.ts
Align existing tests with the new InsertBlockResult return type and semantics.
  • Update CFStorage.insertBlockInfo stale-block protection tests to assert the full InsertBlockResult object for inserted, updated, and skipped cases.
  • Adjust general storage tests to compare against InsertBlockResult instead of simple booleans for the same scenarios.
packages/btcindexer/src/btcindexer.test.ts
packages/btcindexer/src/storage.test.ts

Assessment against linked issues

Issue Objective Addressed Explanation
#214 Change CFStorage.insertBlockInfo to return a structured status type (proper status) instead of a boolean, indicating whether a block was inserted, updated, or skipped.
#214 Propagate the new structured status return type of insertBlockInfo through the btcindexer codebase and tests so callers use the status instead of a boolean.

Possibly linked issues


Tips and commands

Interacting with Sourcery

  • Trigger a new review: Comment @sourcery-ai review on the pull request.
  • Continue discussions: Reply directly to Sourcery's review comments.
  • Generate a GitHub issue from a review comment: Ask Sourcery to create an
    issue from a review comment by replying to it. You can also reply to a
    review comment with @sourcery-ai issue to create an issue from it.
  • Generate a pull request title: Write @sourcery-ai anywhere in the pull
    request title to generate a title at any time. You can also comment
    @sourcery-ai title on the pull request to (re-)generate the title at any time.
  • Generate a pull request summary: Write @sourcery-ai summary anywhere in
    the pull request body to generate a PR summary at any time exactly where you
    want it. You can also comment @sourcery-ai summary on the pull request to
    (re-)generate the summary at any time.
  • Generate reviewer's guide: Comment @sourcery-ai guide on the pull
    request to (re-)generate the reviewer's guide at any time.
  • Resolve all Sourcery comments: Comment @sourcery-ai resolve on the
    pull request to resolve all Sourcery comments. Useful if you've already
    addressed all the comments and don't want to see them anymore.
  • Dismiss all Sourcery reviews: Comment @sourcery-ai dismiss on the pull
    request to dismiss all existing Sourcery reviews. Especially useful if you
    want to start fresh with a new review - don't forget to comment
    @sourcery-ai review to trigger a new review!

Customizing Your Experience

Access your dashboard to:

  • Enable or disable review features such as the Sourcery-generated pull request
    summary, the reviewer's guide, and others.
  • Change the review language.
  • Add, remove or edit custom review instructions.
  • Adjust other review settings.

Getting Help

Copy link
Contributor

@sourcery-ai sourcery-ai bot left a comment

Choose a reason for hiding this comment

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

Hey - I've found 1 issue, and left some high level feedback:

  • Since you already rely on the upsert’s meta.changes, you can likely avoid the separate SELECT + race-prone existence check and instead derive inserted/updated/skipped from a single INSERT ... ON CONFLICT ... RETURNING (if supported) or from the upsert result alone, which would simplify the logic and make it more robust under concurrency.
  • When throwing new Error("Batch operation failed"), consider including which part of the batch failed (e.g., missing check vs upsert result and relevant block identifiers) to make operational debugging of storage issues easier.
Prompt for AI Agents
Please address the comments from this code review:

## Overall Comments
- Since you already rely on the upsert’s `meta.changes`, you can likely avoid the separate `SELECT` + race-prone existence check and instead derive `inserted/updated/skipped` from a single `INSERT ... ON CONFLICT ... RETURNING` (if supported) or from the upsert result alone, which would simplify the logic and make it more robust under concurrency.
- When throwing `new Error("Batch operation failed")`, consider including which part of the batch failed (e.g., missing check vs upsert result and relevant block identifiers) to make operational debugging of storage issues easier.

## Individual Comments

### Comment 1
<location> `packages/btcindexer/src/cf-storage.ts:94-97` </location>
<code_context>
+				throw new Error("Batch operation failed");
+			}
+
+			const wasFound = checkResult.results.length > 0;
+			const rowsChanged = upsertResult.meta.changes > 0;
+
+			if (!wasFound) {
+				return { status: "inserted", changed: true };
+			} else if (rowsChanged) {
</code_context>

<issue_to_address>
**issue (bug_risk):** The status inference can be incorrect under races where another writer inserts between the check and upsert.

This assumes `wasFound === false` means we inserted a new row. With concurrent writers, a race can occur: we run `SELECT 1` and see no row, another writer inserts the same `(height, network)`, and then our `INSERT ... ON CONFLICT DO UPDATE` actually executes an `UPDATE`. In that case `wasFound` is `false` but we updated, not inserted. Consider deriving the status from something that reflects the actual DML path (e.g., additional `meta` info or a single consolidated statement), or at least treating the `!wasFound && rowsChanged` case specially.
</issue_to_address>

Sourcery is free for open source - if you like our reviews please consider sharing them ✨
Help me be more useful! Please click 👍 or 👎 on each comment and I'll use the feedback to improve your reviews.

Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

This PR enhances the block storage API by replacing boolean return values with a structured InsertBlockResult type that clearly indicates whether a block was inserted, updated, or skipped. This improvement provides better observability and more informative logging throughout the indexer.

Key Changes:

  • Introduced InsertBlockResult discriminated union type with three states: "inserted", "updated", and "skipped"
  • Modified CFStorage.insertBlockInfo to return InsertBlockResult and use batched queries to determine the operation outcome
  • Updated indexer logging to leverage the detailed status information

Reviewed changes

Copilot reviewed 6 out of 6 changed files in this pull request and generated no comments.

Show a summary per file
File Description
packages/btcindexer/src/models.ts Added InsertBlockResult type definition as a discriminated union
packages/btcindexer/src/storage.ts Updated Storage interface to return InsertBlockResult instead of boolean
packages/btcindexer/src/cf-storage.ts Implemented batched query approach with pre-check SELECT to determine insert vs. update status
packages/btcindexer/src/btcindexer.ts Updated block processing to use InsertBlockResult and improved skip logging
packages/btcindexer/src/storage.test.ts Updated test assertions to validate structured result objects
packages/btcindexer/src/btcindexer.test.ts Updated stale block protection tests to assert on structured results

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

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.

btcindexer: update insertBlockInfo to return proper status

3 participants