Skip to content
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

Fix ledger range calculation #217

Merged
merged 77 commits into from
Jul 2, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
77 commits
Select commit Hold shift + click to select a range
efab5b3
Add LedgerRangeReader interface
aditya1702 Jun 14, 2024
9966f57
Add GetLedgerRange implementation for ledgers table
aditya1702 Jun 14, 2024
aad48ab
Use GetLedgerRange from the ledgers table for getHealth and getFeeStats
aditya1702 Jun 14, 2024
9e82846
Change varible name
aditya1702 Jun 14, 2024
d198238
Add GetLedgerRange method for transactions table
aditya1702 Jun 14, 2024
f12d930
Add test for GetLedgerRange for ledgers table
aditya1702 Jun 14, 2024
4a2f78c
Merge branch 'main' into fix-rpc-bugs
aditya1702 Jun 14, 2024
3c9c7a6
Add GetLedgerRange in ConstantLedgerReader
aditya1702 Jun 14, 2024
38d6ef5
Merge remote-tracking branch 'origin/fix-rpc-bugs' into fix-rpc-bugs
aditya1702 Jun 14, 2024
fc56898
Add test for GetLedgerRange in transactions table
aditya1702 Jun 14, 2024
a27a176
Fix nil pointer bugs in getTransactions
aditya1702 Jun 14, 2024
ef35cb8
Remove latest ledger assertion
aditya1702 Jun 17, 2024
1d9140c
Comment out the latest ledger assertion
aditya1702 Jun 17, 2024
1b35c07
Remove GetLedgerRange from meta table and use ledgerRangeGetter for g…
aditya1702 Jun 17, 2024
420adc0
Remove ledgerCloseTime
aditya1702 Jun 17, 2024
2343207
Revert newline change
aditya1702 Jun 17, 2024
662b2a8
Merge branch 'main' into fix-rpc-bugs
aditya1702 Jun 17, 2024
1dc4020
Revert
aditya1702 Jun 17, 2024
b3cd836
Merge remote-tracking branch 'origin/fix-rpc-bugs' into fix-rpc-bugs
aditya1702 Jun 17, 2024
81bdf1e
Remove assertions
aditya1702 Jun 17, 2024
f7d1b22
revert
aditya1702 Jun 17, 2024
c8d0a83
Change interface name
aditya1702 Jun 18, 2024
d9df979
insert txns during integration test setup - 1
aditya1702 Jun 20, 2024
6db04d5
insert txns during integration test setup - 2
aditya1702 Jun 20, 2024
c32c544
Merge branch 'refs/heads/main' into fix-rpc-bugs
aditya1702 Jun 20, 2024
00d16d5
insert txns during integration test setup - 3
aditya1702 Jun 20, 2024
ef1605a
Merge branch 'refs/heads/main' into fix-rpc-bugs
aditya1702 Jun 20, 2024
ff24237
Fix linting errors
aditya1702 Jun 20, 2024
29d588e
Fix linting errors - 2
aditya1702 Jun 20, 2024
e377bf5
Fix linting errors - 3
aditya1702 Jun 20, 2024
fd66565
Fix linting errors - 4
aditya1702 Jun 20, 2024
056f712
Fix linting errors - 5
aditya1702 Jun 20, 2024
31dda7d
Revert
aditya1702 Jun 21, 2024
5a815b6
Revert-2
aditya1702 Jun 21, 2024
bf6ec28
change camel-case naming
aditya1702 Jun 21, 2024
ab4f82e
change camel-case naming - 2
aditya1702 Jun 21, 2024
569ca78
Fix linting errrors - 6
aditya1702 Jun 21, 2024
894f5c2
Simplify ledger range query
aditya1702 Jun 21, 2024
eb4ccac
Simplify ledger range query - 2
aditya1702 Jun 21, 2024
66df55f
Simplify ledger range query - 3
aditya1702 Jun 24, 2024
bb9b338
Add benchmarking for GetLedgerRange
aditya1702 Jun 24, 2024
7e7ab78
Fix linter issues - 6
aditya1702 Jun 24, 2024
961b84f
Remove else condition
aditya1702 Jun 24, 2024
93906e4
Optimise the GetLedgerRange query
aditya1702 Jun 25, 2024
ee6a893
Fix intrange linter
aditya1702 Jun 25, 2024
52fb803
Use require.NoError
aditya1702 Jun 25, 2024
545bd69
Move comment to definition
aditya1702 Jun 25, 2024
5b7b8f1
Fix intrange linter - 2
aditya1702 Jun 25, 2024
9aa8584
Fix nomnd linter
aditya1702 Jun 25, 2024
bc524bf
Merge branch 'main' into fix-rpc-bugs
aditya1702 Jun 25, 2024
5eb006d
Fix intrange linter
aditya1702 Jun 26, 2024
f8466da
Remove db/util.go and add txMeta methods to infrastructure
aditya1702 Jun 26, 2024
50cd4c7
forgot to gci files again :/
aditya1702 Jun 26, 2024
4643089
Remove migration FIXME
aditya1702 Jun 26, 2024
2691eab
Fix linter checks
aditya1702 Jun 26, 2024
c809003
Add migration for lcm sequence index
aditya1702 Jun 26, 2024
29c67c2
Revert transaction_test.go changes
aditya1702 Jun 26, 2024
b5b2598
Add newline
aditya1702 Jun 26, 2024
e88d3b6
Add GetLedgerRange implementation in meta table
aditya1702 Jun 26, 2024
335ca10
Use new GetLedgerRange for getHealth and getFeeStats
aditya1702 Jun 26, 2024
f1e1795
Add GetLedgerRange to ConstantLedgerReader
aditya1702 Jun 26, 2024
61a5b88
Remove GetLedgerRange from transactions code and use the meta table one
aditya1702 Jun 27, 2024
3888218
Merge branch 'refs/heads/main' into fix-rpc-bugs
aditya1702 Jun 27, 2024
1bf294c
Remove unnecessary file changes
aditya1702 Jun 27, 2024
8ef9f02
Fix linting errors
aditya1702 Jun 27, 2024
7293e6c
Fix linting errors - 2
aditya1702 Jun 27, 2024
9ea2af2
Fix linting errors - 3
aditya1702 Jun 27, 2024
beb0a93
Fix linting errors - 4
aditya1702 Jun 27, 2024
6bd7761
Merge branch 'main' into fix-rpc-bugs
aditya1702 Jun 28, 2024
8c54b87
Remove unnecessary constants
aditya1702 Jun 28, 2024
8481ced
Use sq.Select instead of sq.Expr
aditya1702 Jun 28, 2024
0db4c66
Add nolint
aditya1702 Jun 28, 2024
c9f98a8
Add nolint - 2
aditya1702 Jun 28, 2024
e2cfaaa
Handle single row case in ledger range
aditya1702 Jun 28, 2024
b21593c
Remove index migration
aditya1702 Jul 1, 2024
a865983
Order the ledger results in ASC order
aditya1702 Jul 1, 2024
db77122
exchange the expected and actual values in assert
aditya1702 Jul 1, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
38 changes: 36 additions & 2 deletions cmd/soroban-rpc/internal/db/ledger.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,9 @@ import (
"fmt"

sq "github.com/Masterminds/squirrel"

"github.com/stellar/go/xdr"

"github.com/stellar/soroban-rpc/cmd/soroban-rpc/internal/ledgerbucketwindow"
)

const (
Expand All @@ -18,6 +19,7 @@ type StreamLedgerFn func(xdr.LedgerCloseMeta) error
type LedgerReader interface {
GetLedger(ctx context.Context, sequence uint32) (xdr.LedgerCloseMeta, bool, error)
StreamAllLedgers(ctx context.Context, f StreamLedgerFn) error
GetLedgerRange(ctx context.Context) (ledgerbucketwindow.LedgerRange, error)
}

type LedgerWriter interface {
Expand Down Expand Up @@ -65,10 +67,42 @@ func (r ledgerReader) GetLedger(ctx context.Context, sequence uint32) (xdr.Ledge
case 1:
return results[0], true, nil
default:
return xdr.LedgerCloseMeta{}, false, fmt.Errorf("multiple lcm entries (%d) for sequence %d in table %q", len(results), sequence, ledgerCloseMetaTableName)
return xdr.LedgerCloseMeta{}, false, fmt.Errorf("multiple lcm entries (%d) for sequence %d in table %q",
len(results), sequence, ledgerCloseMetaTableName)
}
}

// GetLedgerRange pulls the min/max ledger sequence numbers from the meta table.
func (r ledgerReader) GetLedgerRange(ctx context.Context) (ledgerbucketwindow.LedgerRange, error) {
query := sq.Select("lcm.meta").
From(ledgerCloseMetaTableName + " as lcm").
Where(sq.Or{
sq.Expr("lcm.sequence = (?)", sq.Select("MIN(sequence)").From(ledgerCloseMetaTableName)),
sq.Expr("lcm.sequence = (?)", sq.Select("MAX(sequence)").From(ledgerCloseMetaTableName)),
aditya1702 marked this conversation as resolved.
Show resolved Hide resolved
}).OrderBy("lcm.sequence ASC")

var lcms []xdr.LedgerCloseMeta
if err := r.db.Select(ctx, &lcms, query); err != nil {
return ledgerbucketwindow.LedgerRange{}, fmt.Errorf("couldn't query ledger range: %w", err)
}

// Empty DB
if len(lcms) == 0 {
return ledgerbucketwindow.LedgerRange{}, nil
}

return ledgerbucketwindow.LedgerRange{
FirstLedger: ledgerbucketwindow.LedgerInfo{
Sequence: lcms[0].LedgerSequence(),
CloseTime: lcms[0].LedgerCloseTime(),
},
LastLedger: ledgerbucketwindow.LedgerInfo{
Sequence: lcms[len(lcms)-1].LedgerSequence(),
CloseTime: lcms[len(lcms)-1].LedgerCloseTime(),
},
aditya1702 marked this conversation as resolved.
Show resolved Hide resolved
}, nil
}

type ledgerWriter struct {
stmtCache *sq.StmtCache
}
Expand Down
136 changes: 119 additions & 17 deletions cmd/soroban-rpc/internal/db/ledger_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -44,24 +44,24 @@ func assertLedgerRange(t *testing.T, reader LedgerReader, start, end uint32) {
allLedgers = append(allLedgers, txmeta)
return nil
})
assert.NoError(t, err)
require.NoError(t, err)
for i := start - 1; i <= end+1; i++ {
ledger, exists, err := reader.GetLedger(context.Background(), i)
assert.NoError(t, err)
require.NoError(t, err)
if i < start || i > end {
assert.False(t, exists)
continue
}
assert.True(t, exists)
ledgerBinary, err := ledger.MarshalBinary()
assert.NoError(t, err)
require.NoError(t, err)
expected := createLedger(i)
expectedBinary, err := expected.MarshalBinary()
assert.NoError(t, err)
require.NoError(t, err)
assert.Equal(t, expectedBinary, ledgerBinary)

ledgerBinary, err = allLedgers[0].MarshalBinary()
assert.NoError(t, err)
require.NoError(t, err)
assert.Equal(t, expectedBinary, ledgerBinary)
allLedgers = allLedgers[1:]
}
Expand All @@ -74,45 +74,147 @@ func TestLedgers(t *testing.T) {

reader := NewLedgerReader(db)
_, exists, err := reader.GetLedger(context.Background(), 1)
assert.NoError(t, err)
require.NoError(t, err)
assert.False(t, exists)

for i := 1; i <= 10; i++ {
ledgerSequence := uint32(i)
tx, err := NewReadWriter(logger, db, daemon, 150, 15, passphrase).NewTx(context.Background())
assert.NoError(t, err)
assert.NoError(t, tx.LedgerWriter().InsertLedger(createLedger(ledgerSequence)))
assert.NoError(t, tx.Commit(ledgerSequence))
require.NoError(t, err)
require.NoError(t, tx.LedgerWriter().InsertLedger(createLedger(ledgerSequence)))
require.NoError(t, tx.Commit(ledgerSequence))
// rolling back after a commit is a no-op
assert.NoError(t, tx.Rollback())
require.NoError(t, tx.Rollback())
}

assertLedgerRange(t, reader, 1, 10)

ledgerSequence := uint32(11)
tx, err := NewReadWriter(logger, db, daemon, 150, 15, passphrase).NewTx(context.Background())
assert.NoError(t, err)
assert.NoError(t, tx.LedgerWriter().InsertLedger(createLedger(ledgerSequence)))
assert.NoError(t, tx.Commit(ledgerSequence))
require.NoError(t, err)
require.NoError(t, tx.LedgerWriter().InsertLedger(createLedger(ledgerSequence)))
require.NoError(t, tx.Commit(ledgerSequence))

assertLedgerRange(t, reader, 1, 11)

ledgerSequence = uint32(12)
tx, err = NewReadWriter(logger, db, daemon, 150, 5, passphrase).NewTx(context.Background())
assert.NoError(t, err)
assert.NoError(t, tx.LedgerWriter().InsertLedger(createLedger(ledgerSequence)))
assert.NoError(t, tx.Commit(ledgerSequence))
require.NoError(t, err)
require.NoError(t, tx.LedgerWriter().InsertLedger(createLedger(ledgerSequence)))
require.NoError(t, tx.Commit(ledgerSequence))

assertLedgerRange(t, reader, 8, 12)
}

func TestGetLedgerRange_NonEmptyDB(t *testing.T) {
db := NewTestDB(t)
ctx := context.TODO()

writer := NewReadWriter(logger, db, interfaces.MakeNoOpDeamon(), 10, 10, passphrase)
write, err := writer.NewTx(ctx)
require.NoError(t, err)

lcms := []xdr.LedgerCloseMeta{
txMeta(1234, true),
txMeta(1235, true),
txMeta(1236, true),
txMeta(1237, true),
}

ledgerW, txW := write.LedgerWriter(), write.TransactionWriter()
for _, lcm := range lcms {
require.NoError(t, ledgerW.InsertLedger(lcm), "ingestion failed for ledger %+v", lcm.V1)
require.NoError(t, txW.InsertTransactions(lcm), "ingestion failed for ledger %+v", lcm.V1)
}
require.NoError(t, write.Commit(lcms[len(lcms)-1].LedgerSequence()))

reader := NewLedgerReader(db)
ledgerRange, err := reader.GetLedgerRange(ctx)
require.NoError(t, err)
assert.Equal(t, uint32(1334), ledgerRange.FirstLedger.Sequence)
assert.Equal(t, ledgerCloseTime(1334), ledgerRange.FirstLedger.CloseTime)
assert.Equal(t, uint32(1337), ledgerRange.LastLedger.Sequence)
assert.Equal(t, ledgerCloseTime(1337), ledgerRange.LastLedger.CloseTime)
}

func TestGetLedgerRange_SingleDBRow(t *testing.T) {
db := NewTestDB(t)
ctx := context.TODO()

writer := NewReadWriter(logger, db, interfaces.MakeNoOpDeamon(), 10, 10, passphrase)
write, err := writer.NewTx(ctx)
require.NoError(t, err)

lcms := []xdr.LedgerCloseMeta{
txMeta(1234, true),
}

ledgerW, txW := write.LedgerWriter(), write.TransactionWriter()
for _, lcm := range lcms {
require.NoError(t, ledgerW.InsertLedger(lcm), "ingestion failed for ledger %+v", lcm.V1)
require.NoError(t, txW.InsertTransactions(lcm), "ingestion failed for ledger %+v", lcm.V1)
}
require.NoError(t, write.Commit(lcms[len(lcms)-1].LedgerSequence()))

reader := NewLedgerReader(db)
ledgerRange, err := reader.GetLedgerRange(ctx)
require.NoError(t, err)
assert.Equal(t, uint32(1334), ledgerRange.FirstLedger.Sequence)
assert.Equal(t, ledgerCloseTime(1334), ledgerRange.FirstLedger.CloseTime)
assert.Equal(t, uint32(1334), ledgerRange.LastLedger.Sequence)
assert.Equal(t, ledgerCloseTime(1334), ledgerRange.LastLedger.CloseTime)
}

func TestGetLedgerRange_EmptyDB(t *testing.T) {
db := NewTestDB(t)
ctx := context.TODO()

reader := NewLedgerReader(db)
ledgerRange, err := reader.GetLedgerRange(ctx)
require.NoError(t, err)
assert.Equal(t, uint32(0), ledgerRange.FirstLedger.Sequence)
assert.Equal(t, int64(0), ledgerRange.FirstLedger.CloseTime)
assert.Equal(t, uint32(0), ledgerRange.LastLedger.Sequence)
assert.Equal(t, int64(0), ledgerRange.LastLedger.CloseTime)
}

func BenchmarkGetLedgerRange(b *testing.B) {
db := NewTestDB(b)
logger := log.DefaultLogger
writer := NewReadWriter(logger, db, interfaces.MakeNoOpDeamon(), 100, 1_000_000, passphrase)
write, err := writer.NewTx(context.TODO())
require.NoError(b, err)

// create 100k tx rows
lcms := make([]xdr.LedgerCloseMeta, 0, 100_000)
for i := range cap(lcms) {
lcms = append(lcms, txMeta(uint32(1234+i), i%2 == 0))
}

ledgerW, txW := write.LedgerWriter(), write.TransactionWriter()
for _, lcm := range lcms {
require.NoError(b, ledgerW.InsertLedger(lcm))
require.NoError(b, txW.InsertTransactions(lcm))
}
require.NoError(b, write.Commit(lcms[len(lcms)-1].LedgerSequence()))
reader := NewLedgerReader(db)

b.ResetTimer()
for range b.N {
ledgerRange, err := reader.GetLedgerRange(context.TODO())
require.NoError(b, err)
assert.Equal(b, lcms[0].LedgerSequence(), ledgerRange.FirstLedger.Sequence)
assert.Equal(b, lcms[len(lcms)-1].LedgerSequence(), ledgerRange.LastLedger.Sequence)
}
}

func NewTestDB(tb testing.TB) *DB {
tmp := tb.TempDir()
dbPath := path.Join(tmp, "db.sqlite")
db, err := OpenSQLiteDB(dbPath)
require.NoError(tb, err)
tb.Cleanup(func() {
assert.NoError(tb, db.Close())
require.NoError(tb, db.Close())
})
return db
}
56 changes: 28 additions & 28 deletions cmd/soroban-rpc/internal/db/mocks.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package db

import (
"context"
"errors"
"io"

"github.com/prometheus/client_golang/prometheus"
Expand All @@ -12,7 +13,7 @@ import (
"github.com/stellar/soroban-rpc/cmd/soroban-rpc/internal/ledgerbucketwindow"
)

type mockTransactionHandler struct {
type MockTransactionHandler struct {
passphrase string

ledgerRange ledgerbucketwindow.LedgerRange
Expand All @@ -21,16 +22,16 @@ type mockTransactionHandler struct {
ledgerSeqToMeta map[uint32]*xdr.LedgerCloseMeta
}

func NewMockTransactionStore(passphrase string) *mockTransactionHandler {
return &mockTransactionHandler{
func NewMockTransactionStore(passphrase string) *MockTransactionHandler {
return &MockTransactionHandler{
passphrase: passphrase,
txs: make(map[string]ingest.LedgerTransaction),
txHashToMeta: make(map[string]*xdr.LedgerCloseMeta),
ledgerSeqToMeta: make(map[uint32]*xdr.LedgerCloseMeta),
}
}

func (txn *mockTransactionHandler) InsertTransactions(lcm xdr.LedgerCloseMeta) error {
func (txn *MockTransactionHandler) InsertTransactions(lcm xdr.LedgerCloseMeta) error {
txn.ledgerSeqToMeta[lcm.LedgerSequence()] = &lcm

reader, err := ingest.NewLedgerTransactionReaderFromLedgerCloseMeta(txn.passphrase, lcm)
Expand All @@ -40,7 +41,7 @@ func (txn *mockTransactionHandler) InsertTransactions(lcm xdr.LedgerCloseMeta) e

for {
tx, err := reader.Read()
if err == io.EOF {
if errors.Is(err, io.EOF) {
break
} else if err != nil {
return err
Expand All @@ -65,48 +66,47 @@ func (txn *mockTransactionHandler) InsertTransactions(lcm xdr.LedgerCloseMeta) e
return nil
}

// GetLedgerRange pulls the min/max ledger sequence numbers from the database.
func (txn *mockTransactionHandler) GetLedgerRange(ctx context.Context) (ledgerbucketwindow.LedgerRange, error) {
return txn.ledgerRange, nil
}

func (txn *mockTransactionHandler) GetTransaction(ctx context.Context, hash xdr.Hash) (
Transaction, ledgerbucketwindow.LedgerRange, error,
func (txn *MockTransactionHandler) GetTransaction(_ context.Context, hash xdr.Hash) (
Transaction, error,
) {
if tx, ok := txn.txs[hash.HexString()]; !ok {
return Transaction{}, txn.ledgerRange, ErrNoTransaction
} else {
itx, err := ParseTransaction(*txn.txHashToMeta[hash.HexString()], tx)
return itx, txn.ledgerRange, err
tx, ok := txn.txs[hash.HexString()]
if !ok {
return Transaction{}, ErrNoTransaction
}
itx, err := ParseTransaction(*txn.txHashToMeta[hash.HexString()], tx)
return itx, err
}

func (txn *mockTransactionHandler) RegisterMetrics(_, _ prometheus.Observer) {}
func (txn *MockTransactionHandler) RegisterMetrics(_, _ prometheus.Observer) {}

type mockLedgerReader struct {
txn mockTransactionHandler
type MockLedgerReader struct {
txn *MockTransactionHandler
}

func NewMockLedgerReader(txn *mockTransactionHandler) *mockLedgerReader {
return &mockLedgerReader{
txn: *txn,
func NewMockLedgerReader(txn *MockTransactionHandler) *MockLedgerReader {
return &MockLedgerReader{
txn: txn,
}
}

func (m *mockLedgerReader) GetLedger(ctx context.Context, sequence uint32) (xdr.LedgerCloseMeta, bool, error) {
func (m *MockLedgerReader) GetLedger(_ context.Context, sequence uint32) (xdr.LedgerCloseMeta, bool, error) {
lcm, ok := m.txn.ledgerSeqToMeta[sequence]
if !ok {
return xdr.LedgerCloseMeta{}, false, nil
}
return *lcm, true, nil
}

func (m *mockLedgerReader) StreamAllLedgers(ctx context.Context, f StreamLedgerFn) error {
func (m *MockLedgerReader) StreamAllLedgers(_ context.Context, _ StreamLedgerFn) error {
return nil
}

func (m *MockLedgerReader) GetLedgerRange(_ context.Context) (ledgerbucketwindow.LedgerRange, error) {
return m.txn.ledgerRange, nil
}

var (
_ TransactionReader = &mockTransactionHandler{}
_ TransactionWriter = &mockTransactionHandler{}
_ LedgerReader = &mockLedgerReader{}
_ TransactionReader = &MockTransactionHandler{}
_ TransactionWriter = &MockTransactionHandler{}
_ LedgerReader = &MockLedgerReader{}
)
Loading
Loading