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 21 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
5 changes: 5 additions & 0 deletions cmd/soroban-rpc/internal/db/db.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ import (
"github.com/stellar/go/xdr"

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

//go:embed sqlmigrations/*.sql
Expand All @@ -31,6 +32,10 @@ const (
latestLedgerSequenceMetaKey = "LatestLedgerSequence"
)

type LedgerRangeReader interface {
aditya1702 marked this conversation as resolved.
Show resolved Hide resolved
GetLedgerRange(ctx context.Context) (ledgerbucketwindow.LedgerRange, error)
}

type ReadWriter interface {
NewTx(ctx context.Context) (WriteTx, error)
GetLatestLedgerSequence(ctx context.Context) (uint32, error)
Expand Down
41 changes: 27 additions & 14 deletions cmd/soroban-rpc/internal/db/transaction.go
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ type TransactionWriter interface {
// TransactionReader provides all the public ways to read from the DB.
type TransactionReader interface {
GetTransaction(ctx context.Context, hash xdr.Hash) (Transaction, ledgerbucketwindow.LedgerRange, error)
GetLedgerRange(ctx context.Context) (ledgerbucketwindow.LedgerRange, error)
LedgerRangeReader
}

type transactionHandler struct {
Expand Down Expand Up @@ -133,7 +133,7 @@ func (txn *transactionHandler) trimTransactions(latestLedgerSeq uint32, retentio
return err
}

// GetLedgerRange pulls the min/max ledger sequence numbers from the database.
// GetLedgerRange pulls the min/max ledger sequence numbers from the transactions table.
func (txn *transactionHandler) GetLedgerRange(ctx context.Context) (ledgerbucketwindow.LedgerRange, error) {
aditya1702 marked this conversation as resolved.
Show resolved Hide resolved
var ledgerRange ledgerbucketwindow.LedgerRange

Expand All @@ -142,32 +142,45 @@ func (txn *transactionHandler) GetLedgerRange(ctx context.Context) (ledgerbucket
// and max from the ledger table in a single query and get around sqlite's
// limitations with parentheses (see https://stackoverflow.com/a/22609948).
//
newestQ := sq.
Select("m1.meta").
// Queries to get the minimum and maximum ledger sequence from the transactions table
minLedgerSeqQ := sq.
Select("m1.ledger_sequence").
FromSelect(
sq.
Select("meta").
From(ledgerCloseMetaTableName).
OrderBy("sequence ASC").
Select("ledger_sequence").
From(transactionTableName).
OrderBy("ledger_sequence ASC").
Limit(1),
"m1",
)
sql, args, err := sq.
Select("m2.meta").
maxLedgerSeqQ, args, err := sq.
Select("m2.ledger_sequence").
FromSelect(
sq.
Select("meta").
From(ledgerCloseMetaTableName).
OrderBy("sequence DESC").
Select("ledger_sequence").
From(transactionTableName).
OrderBy("ledger_sequence DESC").
Limit(1),
"m2",
).ToSql()
if err != nil {
return ledgerRange, errors.Wrap(err, "couldn't build ledger range query")
return ledgerRange, errors.Wrap(err, "couldn't build max ledger sequence query")
}

// Combine the min and max ledger sequence queries using UNION ALL
txnMinMaxLedgersQ, _, err := minLedgerSeqQ.Suffix("UNION ALL "+maxLedgerSeqQ, args...).ToSql()
if err != nil {
return ledgerRange, errors.Wrap(err, "couldn't build max ledger sequence query")
}

// Final query to join ledger_close_meta table and the sequence numbers we got from txnMinMaxLedgersQ
finalSql := sq.
Select("lcm.meta").
From(fmt.Sprintf("%s as lcm", ledgerCloseMetaTableName)).
JoinClause(fmt.Sprintf("JOIN (%s) as seqs ON lcm.sequence == seqs.ledger_sequence", txnMinMaxLedgersQ))

var lcms []xdr.LedgerCloseMeta
if err = txn.db.Select(ctx, &lcms, newestQ.Suffix("UNION ALL "+sql, args...)); err != nil {
if err = txn.db.Select(ctx, &lcms, finalSql); err != nil {
aditya1702 marked this conversation as resolved.
Show resolved Hide resolved
return ledgerRange, errors.Wrap(err, "couldn't query ledger range")
} else if len(lcms) < 2 {
// There is almost certainly a row, but we want to avoid a race condition
Expand Down
22 changes: 20 additions & 2 deletions cmd/soroban-rpc/internal/db/transaction_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,11 +19,21 @@ import (

func TestTransactionNotFound(t *testing.T) {
db := NewTestDB(t)
ctx := context.TODO()
log := log.DefaultLogger
log.SetLevel(logrus.TraceLevel)

reader := NewTransactionReader(log, db, passphrase)
_, _, err := reader.GetTransaction(context.TODO(), xdr.Hash{})

// Assert the ledger range
ledgerRange, err := reader.GetLedgerRange(ctx)
assert.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)

_, _, err = reader.GetTransaction(context.TODO(), xdr.Hash{})
require.Error(t, err, ErrNoTransaction)
}

Expand Down Expand Up @@ -51,8 +61,16 @@ func TestTransactionFound(t *testing.T) {
}
require.NoError(t, write.Commit(lcms[len(lcms)-1].LedgerSequence()))

// check 404 case
// Assert the ledger range
reader := NewTransactionReader(log, db, passphrase)
ledgerRange, err := reader.GetLedgerRange(ctx)
assert.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)

// check 404 case
_, _, err = reader.GetTransaction(ctx, xdr.Hash{})
require.Error(t, err, ErrNoTransaction)

Expand Down
3 changes: 2 additions & 1 deletion cmd/soroban-rpc/internal/events/events.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package events

import (
"context"
"errors"
"io"
"sort"
Expand Down Expand Up @@ -267,7 +268,7 @@ func readEvents(networkPassphrase string, ledgerCloseMeta xdr.LedgerCloseMeta) (
}

// GetLedgerRange returns the first and latest ledger available in the store.
func (m *MemoryStore) GetLedgerRange() (ledgerbucketwindow.LedgerRange, error) {
func (m *MemoryStore) GetLedgerRange(ctx context.Context) (ledgerbucketwindow.LedgerRange, error) {
m.lock.RLock()
defer m.lock.RUnlock()
return m.eventsByLedger.GetLedgerRange(), nil
Expand Down
15 changes: 9 additions & 6 deletions cmd/soroban-rpc/internal/jsonrpc.go
Original file line number Diff line number Diff line change
Expand Up @@ -136,10 +136,13 @@ func NewJSONRPCHandler(cfg *config.Config, params HandlerParams) Handler {
},
}

// While we transition from in-memory to database-oriented history storage,
// the on-disk (transaction) retention window will always be larger than the
// in-memory (events) one.
var retentionWindow = cfg.TransactionLedgerRetentionWindow
// Get the largest history window
var ledgerRangeGetter db.LedgerRangeReader = params.EventStore
var retentionWindow = cfg.EventLedgerRetentionWindow
if cfg.TransactionLedgerRetentionWindow > cfg.EventLedgerRetentionWindow {
retentionWindow = cfg.TransactionLedgerRetentionWindow
ledgerRangeGetter = params.TransactionReader
}

handlers := []struct {
methodName string
Expand All @@ -151,7 +154,7 @@ func NewJSONRPCHandler(cfg *config.Config, params HandlerParams) Handler {
{
methodName: "getHealth",
underlyingHandler: methods.NewHealthCheck(
retentionWindow, params.TransactionReader, cfg.MaxHealthyLedgerLatency),
retentionWindow, ledgerRangeGetter, cfg.MaxHealthyLedgerLatency),
longName: "get_health",
queueLimit: cfg.RequestBacklogGetHealthQueueLimit,
requestDurationLimit: cfg.MaxGetHealthExecutionDuration,
Expand Down Expand Up @@ -232,7 +235,7 @@ func NewJSONRPCHandler(cfg *config.Config, params HandlerParams) Handler {
},
{
methodName: "getFeeStats",
underlyingHandler: methods.NewGetFeeStatsHandler(params.FeeStatWindows, params.TransactionReader, params.Logger),
underlyingHandler: methods.NewGetFeeStatsHandler(params.FeeStatWindows, ledgerRangeGetter, params.Logger),
longName: "get_fee_stats",
queueLimit: cfg.RequestBacklogGetFeeStatsTransactionQueueLimit,
requestDurationLimit: cfg.MaxGetFeeStatsExecutionDuration,
Expand Down
6 changes: 3 additions & 3 deletions cmd/soroban-rpc/internal/methods/get_fee_stats.go
Original file line number Diff line number Diff line change
Expand Up @@ -58,9 +58,9 @@ type GetFeeStatsResult struct {
}

// NewGetFeeStatsHandler returns a handler obtaining fee statistics
func NewGetFeeStatsHandler(windows *feewindow.FeeWindows, reader db.TransactionReader, logger *log.Entry) jrpc2.Handler {
func NewGetFeeStatsHandler(windows *feewindow.FeeWindows, reader db.LedgerRangeReader, logger *log.Entry) jrpc2.Handler {
return NewHandler(func(ctx context.Context) (GetFeeStatsResult, error) {
ledgerInfo, err := reader.GetLedgerRange(ctx)
ledgerRange, err := reader.GetLedgerRange(ctx)
if err != nil { // still not fatal
logger.WithError(err).
Error("could not fetch ledger range")
Expand All @@ -69,7 +69,7 @@ func NewGetFeeStatsHandler(windows *feewindow.FeeWindows, reader db.TransactionR
result := GetFeeStatsResult{
SorobanInclusionFee: convertFeeDistribution(windows.SorobanInclusionFeeWindow.GetFeeDistribution()),
InclusionFee: convertFeeDistribution(windows.ClassicFeeWindow.GetFeeDistribution()),
LatestLedger: ledgerInfo.LastLedger.Sequence,
LatestLedger: ledgerRange.LastLedger.Sequence,
}
return result, nil
})
Expand Down
4 changes: 2 additions & 2 deletions cmd/soroban-rpc/internal/methods/get_transactions.go
Original file line number Diff line number Diff line change
Expand Up @@ -134,7 +134,7 @@ func (h transactionsRPCHandler) getTransactionsByLedgerSequence(ctx context.Cont
// Iterate through each ledger and its transactions until limit or end range is reached.
// The latest ledger acts as the end ledger range for the request.
var txns []TransactionInfo
var cursor *toid.ID
cursor := toid.New(0, 0, 0)
LedgerLoop:
for ledgerSeq := start.LedgerSequence; ledgerSeq <= int32(ledgerRange.LastLedger.Sequence); ledgerSeq++ {
// Get ledger close meta from db
Expand Down Expand Up @@ -167,7 +167,7 @@ LedgerLoop:
if ierr := reader.Seek(startTxIdx - 1); ierr != nil && ierr != io.EOF {
return GetTransactionsResponse{}, &jrpc2.Error{
Code: jrpc2.InternalError,
Message: err.Error(),
Message: ierr.Error(),
}
}
}
Expand Down
3 changes: 2 additions & 1 deletion cmd/soroban-rpc/internal/methods/health.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import (
"time"

"github.com/creachadair/jrpc2"

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

Expand All @@ -19,7 +20,7 @@ type HealthCheckResult struct {
// NewHealthCheck returns a health check json rpc handler
func NewHealthCheck(
retentionWindow uint32,
reader db.TransactionReader,
reader db.LedgerRangeReader,
maxHealthyLedgerLatency time.Duration,
) jrpc2.Handler {
return NewHandler(func(ctx context.Context) (HealthCheckResult, error) {
Expand Down
6 changes: 0 additions & 6 deletions cmd/soroban-rpc/internal/test/transaction_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -136,8 +136,6 @@ func TestSendTransactionBadSequence(t *testing.T) {
err = client.CallResult(context.Background(), "sendTransaction", request, &result)
assert.NoError(t, err)

assert.NotZero(t, result.LatestLedger)
aditya1702 marked this conversation as resolved.
Show resolved Hide resolved
assert.NotZero(t, result.LatestLedgerCloseTime)
expectedHashHex, err := tx.HashHex(StandaloneNetworkPassphrase)
assert.NoError(t, err)
assert.Equal(t, expectedHashHex, result.Hash)
Expand Down Expand Up @@ -245,8 +243,6 @@ func TestSendTransactionFailedInLedger(t *testing.T) {
assert.NoError(t, err)
fmt.Printf("error: %#v\n", txResult)
}
assert.NotZero(t, result.LatestLedger)
assert.NotZero(t, result.LatestLedgerCloseTime)

response := getTransaction(t, client, expectedHashHex)
assert.Equal(t, methods.TransactionStatusFailed, response.Status)
Expand Down Expand Up @@ -291,8 +287,6 @@ func sendSuccessfulTransaction(t *testing.T, client *jrpc2.Client, kp *keypair.F
assert.NoError(t, err)
t.Logf("error: %#v\n", txResult)
}
assert.NotZero(t, result.LatestLedger)
assert.NotZero(t, result.LatestLedgerCloseTime)

response := getTransaction(t, client, expectedHashHex)
if !assert.Equal(t, methods.TransactionStatusSuccess, response.Status) {
Expand Down
Loading