Skip to content

[Access] Access ingestion error handle#8385

Merged
zhangchiqing merged 11 commits intomasterfrom
leo/access-ingestion-error-handle
Feb 4, 2026
Merged

[Access] Access ingestion error handle#8385
zhangchiqing merged 11 commits intomasterfrom
leo/access-ingestion-error-handle

Conversation

@zhangchiqing
Copy link
Member

@zhangchiqing zhangchiqing commented Feb 3, 2026

Backport #8383 and #8389 to master

Summary by CodeRabbit

  • New Features

    • Added an ingestion finalized block height metric to track ingestion progress.
  • Improvements

    • Engine now records and updates ingestion-finalized block height and propagates the metrics collector through ingestion paths.
    • Finalized block processing hardened: already-indexed blocks are handled gracefully and fatal indexing errors are surfaced.
  • Tests

    • Added tests verifying behavior when a finalized block is already indexed and metrics usage.

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Feb 3, 2026

📝 Walkthrough

Walkthrough

Adds Access metrics propagation to the ingestion engine, introduces a new ingestion-finalized-block-height metric and noop/mock implementations, and hardens finalized-block processing with context-aware error handling and updated metric reporting.

Changes

Cohort / File(s) Summary
Core Ingestion Engine
engine/access/ingestion/engine.go
Added accessMetrics module.AccessMetrics field and parameter to New(...); hardened finalized-block handling (treat non-ErrAlreadyExists errors as wrapped returns, log/continue on ErrAlreadyExists), and update ingestion-finalized-block-height metric after finalization.
Engine Tests
engine/access/ingestion/engine_test.go, engine/access/access_test.go
Updated constructor calls to pass a metrics collector (metrics.NewNoopCollector() or suite.metrics), added TestOnFinalizedBlockAlreadyIndexed to validate continuation when block already indexed.
Metrics API & Implementations
module/metrics.go, module/metrics/access.go, module/metrics/noop.go
Added UpdateIngestionFinalizedBlockHeight(height uint64) to AccessMetrics interface; implemented new Prometheus gauge ingestion_finalized_block_height in AccessCollector; added noop implementation in NoopCollector.
Mocks
module/mock/access_metrics.go
Generated mock support for UpdateIngestionFinalizedBlockHeight(height uint64) including call expecter and Run/Return helpers.
Node Builder Integration
cmd/access/node_builder/access_node_builder.go
Pass builder.AccessMetrics into ingestion.New(...) to propagate metrics into the ingestion engine.

Sequence Diagram(s)

sequenceDiagram
    participant FinBlock as Finalized Block Event
    participant Engine as Ingestion Engine
    participant Storage as Storage Layer
    participant Collections as Collection Lookup
    participant Metrics as AccessMetrics

    FinBlock->>Engine: OnFinalizedBlock(block)
    Engine->>Storage: Index block
    alt Indexing Success
        Storage-->>Engine: success
        Engine->>Collections: Request missing collections
        Collections-->>Engine: collection data
        Engine->>Metrics: UpdateIngestionFinalizedBlockHeight(height)
        Metrics-->>Engine: recorded
        Engine-->>FinBlock: processing complete
    else ErrAlreadyExists
        Storage-->>Engine: ErrAlreadyExists
        Engine->>Engine: Log warning (height, blockID)
        Engine->>Collections: Request missing collections
        Collections-->>Engine: collection data
        Engine->>Metrics: UpdateIngestionFinalizedBlockHeight(height)
        Metrics-->>Engine: recorded
        Engine-->>FinBlock: processing continues
    else Other Error
        Storage-->>Engine: error
        Engine->>Engine: Wrap error with context
        Engine-->>FinBlock: return error
    end
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Poem

🐰 I hopped through blocks and tallied the height,

gauges and counters glowing bright.
When "already there" whispers in the night,
I warn, request, and carry on the light—
ingestion hops forward, metrics all right.

🚥 Pre-merge checks | ✅ 1 | ❌ 2
❌ Failed checks (1 warning, 1 inconclusive)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 50.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
Title check ❓ Inconclusive The title '[Access] Access ingestion error handle' is vague and uses incomplete phrasing that doesn't clearly convey the main changes in the PR. Revise the title to be more specific and complete, such as '[Access] Add metrics tracking and error handling for finalized block ingestion' or '[Access] Improve ingestion error handling with metrics propagation'.
✅ Passed checks (1 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.

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

✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch leo/access-ingestion-error-handle

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

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

@github-actions
Copy link
Contributor

github-actions bot commented Feb 3, 2026

Dependency Review

✅ No vulnerabilities or license issues or OpenSSF Scorecard issues found.

Scanned Files

None

@zhangchiqing zhangchiqing marked this pull request as ready for review February 4, 2026 00:49
@zhangchiqing zhangchiqing requested a review from a team as a code owner February 4, 2026 00:49
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

🤖 Fix all issues with AI agents
In `@engine/access/ingestion/engine.go`:
- Line 412: The call to ingestion.New() should defensively validate
builder.AccessMetrics to avoid nil pointer deref at
e.accessMetrics.UpdateIngestionFinalizedBlockHeight(block.Height); wrap the
argument with notNil(builder.AccessMetrics) in the ingestion.New(...) invocation
(same place other deps like CollectionSyncer/CollectionIndexer use notNil()) so
ingestion receives a non-nil validated AccessMetrics implementation.

return fmt.Errorf("could not request collections for block: %w", err)
}
e.collectionExecutedMetric.BlockFinalized(block)
e.accessMetrics.UpdateIngestionFinalizedBlockHeight(block.Height)
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

🧩 Analysis chain

🏁 Script executed:

# Find the ingestion package structure and New() function signature
fd -n "engine.go" engine/access/ingestion/ | head -5

Repository: onflow/flow-go

Length of output: 288


🏁 Script executed:

# Get the constructor signature for ingestion.New()
rg -n -A 30 "^func New\(" engine/access/ingestion/ --type=go | head -60

Repository: onflow/flow-go

Length of output: 5989


🏁 Script executed:

# Find all callers of ingestion.New()
rg -n "ingestion\.New\(" --type=go

Repository: onflow/flow-go

Length of output: 407


🏁 Script executed:

# Also check for pattern 'New(' in the ingestion package to find constructor
rg -n "func.*New.*(" engine/access/ingestion/engine.go --type=go

Repository: onflow/flow-go

Length of output: 132


🏁 Script executed:

# Read the ingestion.New() constructor to see if it validates accessMetrics
sed -n '87,200p' engine/access/ingestion/engine.go

Repository: onflow/flow-go

Length of output: 4208


🏁 Script executed:

# Check the first caller in access_test.go
sed -n '770,800p' engine/access/access_test.go

Repository: onflow/flow-go

Length of output: 800


🏁 Script executed:

# Check the second test caller
sed -n '1035,1065p' engine/access/access_test.go

Repository: onflow/flow-go

Length of output: 682


🏁 Script executed:

# Check the third test caller
sed -n '1308,1338p' engine/access/access_test.go

Repository: onflow/flow-go

Length of output: 755


🏁 Script executed:

# Check the cmd/consensus/main.go caller
sed -n '550,575p' cmd/consensus/main.go

Repository: onflow/flow-go

Length of output: 760


🏁 Script executed:

# Check the cmd/access/node_builder caller
sed -n '2260,2290p' cmd/access/node_builder/access_node_builder.go

Repository: onflow/flow-go

Length of output: 928


🏁 Script executed:

# Check what notNil() does in access_node_builder to understand defensive pattern
rg -B 5 -A 5 "func notNil" cmd/access/node_builder/access_node_builder.go | head -20

Repository: onflow/flow-go

Length of output: 526


🏁 Script executed:

# Look at module.AccessMetrics definition to see if it's interface or concrete
rg -n "type AccessMetrics" --type=go

Repository: onflow/flow-go

Length of output: 171


🏁 Script executed:

# Examine the interface definition
rg -B 2 -A 10 "type AccessMetrics interface" --type=go

Repository: onflow/flow-go

Length of output: 712


🏁 Script executed:

# Verify if builder.AccessMetrics could be nil by checking how it's initialized
rg -B 10 "builder.AccessMetrics =" cmd/access/node_builder/access_node_builder.go | head -30

Repository: onflow/flow-go

Length of output: 413


🏁 Script executed:

# Check if AccessMetrics is initialized in all code paths
rg -n "AccessMetrics" cmd/access/node_builder/access_node_builder.go | grep -E "(AccessMetrics\s*=|AccessMetrics:)" | head -20

Repository: onflow/flow-go

Length of output: 222


🏁 Script executed:

# Check the exact context around the ingestion.New() call to see if AccessMetrics is wrapped with notNil()
sed -n '2265,2290p' cmd/access/node_builder/access_node_builder.go

Repository: onflow/flow-go

Length of output: 689


🏁 Script executed:

# Also check line 2132 context to understand which component that's for
sed -n '2120,2145p' cmd/access/node_builder/access_node_builder.go

Repository: onflow/flow-go

Length of output: 1432


Add notNil() wrapper for builder.AccessMetrics in ingestion.New() call to ensure defensive validation.

At line 2280 in cmd/access/node_builder/access_node_builder.go, builder.AccessMetrics is passed to ingestion.New() without the notNil() validation, unlike other critical dependencies in the same call (CollectionSyncer, CollectionIndexer, collectionExecutedMetric, TxResultErrorMessagesCore). Since accessMetrics is an interface type that can be nil, and the code at line 412 in engine/access/ingestion/engine.go calls e.accessMetrics.UpdateIngestionFinalizedBlockHeight(block.Height) without a nil check, this creates a potential nil pointer dereference. Wrap builder.AccessMetrics with notNil() to align with the project's defensive pattern used consistently for other critical dependencies.

🤖 Prompt for AI Agents
In `@engine/access/ingestion/engine.go` at line 412, The call to ingestion.New()
should defensively validate builder.AccessMetrics to avoid nil pointer deref at
e.accessMetrics.UpdateIngestionFinalizedBlockHeight(block.Height); wrap the
argument with notNil(builder.AccessMetrics) in the ingestion.New(...) invocation
(same place other deps like CollectionSyncer/CollectionIndexer use notNil()) so
ingestion receives a non-nil validated AccessMetrics implementation.

@codecov-commenter
Copy link

codecov-commenter commented Feb 4, 2026

Codecov Report

❌ Patch coverage is 15.90909% with 37 lines in your changes missing coverage. Please review.

Files with missing lines Patch % Lines
module/mock/access_metrics.go 0.00% 20 Missing ⚠️
module/metrics/access.go 0.00% 8 Missing ⚠️
engine/access/ingestion/engine.go 50.00% 5 Missing and 2 partials ⚠️
cmd/access/node_builder/access_node_builder.go 0.00% 1 Missing ⚠️
module/metrics/noop.go 0.00% 1 Missing ⚠️

📢 Thoughts on this report? Let us know!

Comment on lines +576 to +579
cluster := new(protocolmock.Cluster)
epoch := new(protocolmock.CommittedEpoch)
epochs := new(protocolmock.EpochQuery)
snap := new(protocolmock.Snapshot)
Copy link
Contributor

Choose a reason for hiding this comment

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

Is it intentional that these use new(module.mockObject) rather than module.NewMockObject(s.T())? This way the expectations aren't checked by default, which has bitten me once before.

@zhangchiqing zhangchiqing added this pull request to the merge queue Feb 4, 2026
Merged via the queue into master with commit b447018 Feb 4, 2026
61 checks passed
@zhangchiqing zhangchiqing deleted the leo/access-ingestion-error-handle branch February 4, 2026 01:53
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.

4 participants