Skip to content

services: Implement call to getLedgers and start parsing the xdr.LedgerCloseMeta #197

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

Open
wants to merge 18 commits into
base: main
Choose a base branch
from

Conversation

marcelosalloum
Copy link
Collaborator

@marcelosalloum marcelosalloum commented Jun 18, 2025

What

Update the indexer code to stream from {RPC}.getLedgers and break down the results from ledgerRange -> ledgers -> transactions.

Also, (pseudo) process the ledgers in parallel.

Why

As part of the new indexer implementation.

Future Work

  • evaluate transactions, filter by participant account & store in the DB
  • evaluate operations, filter by participant account & store in the DB
  • generate state changes
  • unlock channel accounts

I haven't added many tests to the ingest service yet, since this code will still evolve quite a bit.

Issue that this PR addresses

Partially address #128

Checklist

PR Structure

  • It is not possible to break this PR down into smaller PRs.
  • This PR does not mix refactoring changes with feature changes.
  • This PR's title starts with name of package that is most changed in the PR, or all if the changes are broad or impact many packages.

Thoroughness

  • This PR adds tests for the new functionality or fixes.
  • All updated queries have been tested (refer to this check if the data set returned by the updated query is expected to be same as the original one).

Release

  • This is not a breaking change.
  • This is ready to be tested in development.
  • The new functionality is gated with a feature flag if this is not ready for production.

@marcelosalloum marcelosalloum self-assigned this Jun 18, 2025
@marcelosalloum marcelosalloum requested a review from Copilot June 18, 2025 23:46
Copilot

This comment was marked as outdated.

Comment on lines +300 to +309
mu := sync.Mutex{}
var allTxHashes []string
var errs []error

// Submit tasks to the pool
for _, ledger := range getLedgersResponse.Ledgers {
ledger := ledger // Create a new variable to avoid closure issues
pool.Submit(func() {
txHashes, err := m.processLedger(ctx, ledger)
mu.Lock()
if err != nil {
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 plan to use toid to order the transactions in the database. It's calculated like this.

@marcelosalloum marcelosalloum marked this pull request as ready for review June 19, 2025 00:18
@marcelosalloum marcelosalloum changed the title [WIP] services: Implement call to getLedgers and start parsing the xdr.LedgerCloseMeta services: Implement call to getLedgers and start parsing the xdr.LedgerCloseMeta Jun 19, 2025
@marcelosalloum marcelosalloum changed the title services: Implement call to getLedgers and start parsing the xdr.LedgerCloseMeta services: Implement call to getLedgers and start parsing the xdr.LedgerCloseMeta Jun 23, 2025
@marcelosalloum marcelosalloum added this to the Wallet Backend Indexer milestone Jun 23, 2025
@aditya1702 aditya1702 removed this from the Wallet Backend Indexer milestone Jun 23, 2025
func (m *ingestService) fetchNextLedgersBatch(ctx context.Context, startLedger uint32) (GetLedgersResponse, error) {
rpcHealth, err := m.rpcService.GetHealth()
if err != nil {
return GetLedgersResponse{}, fmt.Errorf("getting rpc health: %w", err)
Copy link
Contributor

Choose a reason for hiding this comment

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

In cases where RPC becomes unhealthy for a few mins and then healthy again, this would stop the ingestion and terminate right? How about we terminate ingestion process only if RPC stays unhealthy for a certain threshold. Otherwise we keep letting it try and fetch the ledgers batch until then.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Hm, good point! I'll work on that 👍

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Done in dc0f8a5

if latestLedgerSynced >= rpcNewestLedger {
return LedgerSeqRange{}, true
}
ledgerRange.StartLedger = max(latestLedgerSynced+1, rpcOldestLedger)
Copy link
Contributor

Choose a reason for hiding this comment

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

If we do a max() here would that mean if wallet-backend is at 5 and rpc's oldest ledger is at 8, it would skip ingesting 6, 7? If we are ingesting using GetLedgers by getting ledgers in bulk, do we want to allow WB catch up to RPC instead of skipping ledgers?

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

If we do a max() here would that mean if wallet-backend is at 5 and rpc's oldest ledger is at 8, it would skip ingesting 6, 7?

I think we don't have another option here, right? If we have [oldestLedger: 8, newestLedger: 20], we can only ingest within that window, as the RPC doesn't have anything older than ledger 8 available for ingestion.

log.Ctx(ctx).Debugf("🚧 Got %d transactions for ledger %d", len(transactions), xdrLedgerCloseMeta.LedgerSequence())
txHashes := make([]string, 0, len(transactions))
for _, tx := range transactions {
txHashes = append(txHashes, tx.Hash.HexString())
Copy link
Contributor

Choose a reason for hiding this comment

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

Since this is still in TODO, I am assuming this will return a list of types.Transaction structs instead of tx hash strings? Because what would be the use of the tx hashes?

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Yes, you're right! I'm returning txHashes because I'd like to log something short. But the output will definitely be actual objects!

@marcelosalloum marcelosalloum force-pushed the marcelo/start-indexer branch from 866e082 to 169087f Compare June 24, 2025 22:55
@marcelosalloum marcelosalloum force-pushed the marcelo/start-indexer branch from 169087f to 5369197 Compare June 24, 2025 22:58
Copy link

@Copilot 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 implements a new getLedgers RPC endpoint in the indexer’s RPC service, adds parallel ledger batch ingestion logic to the ingest service (with advisory locking), and introduces a dedicated IngestStoreModel to track the ingestion cursor.

  • Added GetLedgers to RPCService, updated error handling, and tests in rpc_service_test.go.
  • Extended ingest service with a new Run loop to fetch and process ledger batches in parallel.
  • Introduced IngestStoreModel and updated Models to support ingest cursor storage.

Reviewed Changes

Copilot reviewed 15 out of 16 changed files in this pull request and generated 1 comment.

Show a summary per file
File Description
internal/services/rpc_service.go Added GetLedgers method and updated sendRPCRequest error message
internal/services/rpc_service_test.go New tests for GetLedgers and error path adjustments
internal/services/ingest.go New Run logic with batch fetching, parallel processing, and locking
internal/services/mocks.go Added GetLedgers mock method
internal/services/ingest_test.go Tests for getLedgerSeqRange and getLedgerTransactions
internal/indexer/types/types.go Changed Transaction.LedgerNumber from int64 to uint32
internal/data/ingest_store.go New IngestStoreModel for cursor persistence
internal/data/models.go Registered IngestStoreModel in Models
Comments suppressed due to low confidence (5)

internal/indexer/types/types.go:21

  • Changing LedgerNumber from int64 to uint32 is a breaking change in the API contract; downstream consumers expecting signed integers may be impacted.
	LedgerNumber    uint32    `json:"ledgerNumber,omitempty" db:"ledger_number"`

internal/services/rpc_service.go:147

  • [nitpick] Consider adding a comment for the GetLedgersResponse type alias to document its purpose and linkage to protocol.GetLedgersResponse.
type GetLedgersResponse protocol.GetLedgersResponse

internal/services/ingest_test.go:36

  • Consider adding tests for fetchNextLedgersBatch to cover cases where RPC indicates ingestion is in sync (ErrAlreadyInSync) and RPC call failures for more complete coverage.
func Test_getLedgerSeqRange(t *testing.T) {

internal/services/rpc_service.go:161

  • Missing import for "encoding/json"; the call to json.Unmarshal will not compile without importing the package.
	err = json.Unmarshal(resultBytes, &result)

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