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

Add getLedgers implementation #303

Merged
merged 40 commits into from
Nov 13, 2024
Merged
Show file tree
Hide file tree
Changes from 27 commits
Commits
Show all changes
40 commits
Select commit Hold shift + click to select a range
d2f1763
Add getLedgers - 1
aditya1702 Sep 19, 2024
704ba5c
Add getLedgers - 2
aditya1702 Sep 23, 2024
d3bba51
Add getLedgers unittests
aditya1702 Sep 25, 2024
43e00fa
Add getLedgers integration tests
aditya1702 Sep 25, 2024
8f5ba83
Get all ledgers in once call from the db
aditya1702 Sep 26, 2024
99d7d3a
Add one more unittest
aditya1702 Sep 26, 2024
c5e71fe
gci files
aditya1702 Sep 26, 2024
b865020
change quelimit and request duration limit
aditya1702 Sep 26, 2024
f6eda45
Merge branch 'main' into get-ledgers
aditya1702 Sep 26, 2024
935d305
Update cmd/soroban-rpc/internal/methods/get_ledgers.go
aditya1702 Sep 30, 2024
9498a68
Update cmd/soroban-rpc/internal/methods/get_ledgers.go
aditya1702 Sep 30, 2024
d0f3816
Pre-alloc array in BatchGetLedgers
aditya1702 Sep 30, 2024
02604f3
Return default error for json conversion
aditya1702 Sep 30, 2024
1918bb8
Merge remote-tracking branch 'origin/get-ledgers' into get-ledgers
aditya1702 Sep 30, 2024
28d5afb
Use strconv.Itoa
aditya1702 Sep 30, 2024
524cbf6
Use xdr2json.ConvertInterface instead of marshalling
aditya1702 Oct 1, 2024
e807acf
Move db setup to another function
aditya1702 Oct 1, 2024
07e1c4a
Add benchmarking for BatchGetLedgers
aditya1702 Oct 1, 2024
7fac004
Small changes
aditya1702 Oct 1, 2024
b9282c9
Small changes - 2
aditya1702 Oct 1, 2024
58e6d2c
Update benchmark code for BatchGetLedgers
aditya1702 Oct 2, 2024
f89c5f1
Revert PaginationOptions changes (will do in separate PR)
aditya1702 Oct 2, 2024
ab8c4e5
Revert PaginationOptionschanges - 2
aditya1702 Oct 2, 2024
a8a24e2
Revert PaginationOptionschanges - 3
aditya1702 Oct 2, 2024
6417f93
Revert PaginationOptionschanges - 4
aditya1702 Oct 2, 2024
3325577
Revert PaginationOptionschanges - 5
aditya1702 Oct 2, 2024
2c906e9
refactor unittest to remove duplication
aditya1702 Oct 2, 2024
44b9ab9
Merge branch 'main' into get-ledgers
Shaptic Oct 7, 2024
72b136e
Update cmd/soroban-rpc/internal/methods/get_ledgers.go
aditya1702 Oct 11, 2024
b6e1f5f
Rename LedgerCloseMeta to LedgerMetadata
aditya1702 Nov 1, 2024
f305de9
Rename LedgerCloseMeta to LedgerMetadata - 2
aditya1702 Nov 1, 2024
7bc1d6f
Only pass ledger meta to extract the json for header too
aditya1702 Nov 1, 2024
686545d
Pass ledger pointer instead of entire object
aditya1702 Nov 1, 2024
526b534
Update cmd/soroban-rpc/internal/methods/get_ledgers.go
aditya1702 Nov 5, 2024
a9c2324
Use a read tx for GetLedgerRange and BatchGetLedgers
aditya1702 Nov 8, 2024
9d653e3
Merge remote-tracking branch 'refs/remotes/upstream/main' into get-le…
aditya1702 Nov 8, 2024
0be9cc5
Fix lint
aditya1702 Nov 8, 2024
ca4d6d6
Convert ReadDB to private interface
aditya1702 Nov 8, 2024
2a0cec2
Remove BatchGetLedgers from LedgerReader
aditya1702 Nov 11, 2024
6adaff0
Merge branch 'main' into get-ledgers
aditya1702 Nov 12, 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
4 changes: 4 additions & 0 deletions cmd/soroban-rpc/internal/config/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ type Config struct {
CoreRequestTimeout time.Duration
DefaultEventsLimit uint
DefaultTransactionsLimit uint
DefaultLedgersLimit uint
EventLedgerRetentionWindow uint32
FriendbotURL string
HistoryArchiveURLs []string
Expand All @@ -35,6 +36,7 @@ type Config struct {
LogLevel logrus.Level
MaxEventsLimit uint
MaxTransactionsLimit uint
MaxLedgersLimit uint
MaxHealthyLedgerLatency time.Duration
NetworkPassphrase string
PreflightWorkerCount uint
Expand All @@ -54,6 +56,7 @@ type Config struct {
RequestBacklogGetLedgerEntriesQueueLimit uint
RequestBacklogGetTransactionQueueLimit uint
RequestBacklogGetTransactionsQueueLimit uint
RequestBacklogGetLedgersQueueLimit uint
RequestBacklogSendTransactionQueueLimit uint
RequestBacklogSimulateTransactionQueueLimit uint
RequestBacklogGetFeeStatsTransactionQueueLimit uint
Expand All @@ -67,6 +70,7 @@ type Config struct {
MaxGetLedgerEntriesExecutionDuration time.Duration
MaxGetTransactionExecutionDuration time.Duration
MaxGetTransactionsExecutionDuration time.Duration
MaxGetLedgersExecutionDuration time.Duration
MaxSendTransactionExecutionDuration time.Duration
MaxSimulateTransactionExecutionDuration time.Duration
MaxGetFeeStatsExecutionDuration time.Duration
Expand Down
35 changes: 35 additions & 0 deletions cmd/soroban-rpc/internal/config/options.go
Original file line number Diff line number Diff line change
Expand Up @@ -304,6 +304,28 @@ func (cfg *Config) options() Options {
return nil
},
},
{
Name: "max-ledgers-limit",
Usage: "Maximum amount of ledgers allowed in a single getLedgers response",
ConfigKey: &cfg.MaxLedgersLimit,
DefaultValue: uint(200),
},
{
Name: "default-ledgers-limit",
Usage: "Default cap on the amount of ledgers included in a single getLedgers response",
ConfigKey: &cfg.DefaultLedgersLimit,
DefaultValue: uint(50),
Validate: func(_ *Option) error {
if cfg.DefaultLedgersLimit > cfg.MaxLedgersLimit {
return fmt.Errorf(
"default-ledgers-limit (%v) cannot exceed max-ledgers-limit (%v)",
cfg.DefaultLedgersLimit,
cfg.MaxLedgersLimit,
)
}
return nil
},
},
{
Name: "max-healthy-ledger-latency",
Usage: "maximum ledger latency (i.e. time elapsed since the last known ledger closing time) considered to be healthy" +
Expand Down Expand Up @@ -394,6 +416,13 @@ func (cfg *Config) options() Options {
DefaultValue: uint(1000),
Validate: positive,
},
{
TomlKey: strutils.KebabToConstantCase("request-backlog-get-ledgers-queue-limit"),
Usage: "Maximum number of outstanding getLedgers requests",
ConfigKey: &cfg.RequestBacklogGetLedgersQueueLimit,
DefaultValue: uint(1000),
Validate: positive,
},
{
TomlKey: strutils.KebabToConstantCase("request-backlog-send-transaction-queue-limit"),
Usage: "Maximum number of outstanding SendTransaction requests",
Expand Down Expand Up @@ -475,6 +504,12 @@ func (cfg *Config) options() Options {
ConfigKey: &cfg.MaxGetTransactionsExecutionDuration,
DefaultValue: 5 * time.Second,
},
{
TomlKey: strutils.KebabToConstantCase("max-get-ledgers-execution-duration"),
Usage: "The maximum duration of time allowed for processing a getLedgers request. When that time elapses, the rpc server would return -32001 and abort the request's execution",
ConfigKey: &cfg.MaxGetLedgersExecutionDuration,
DefaultValue: 5 * time.Second,
},
{
TomlKey: strutils.KebabToConstantCase("max-send-transaction-execution-duration"),
Usage: "The maximum duration of time allowed for processing a sendTransaction request. When that time elapses, the rpc server would return -32001 and abort the request's execution",
Expand Down
20 changes: 20 additions & 0 deletions cmd/soroban-rpc/internal/db/ledger.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ type StreamLedgerFn func(xdr.LedgerCloseMeta) error

type LedgerReader interface {
GetLedger(ctx context.Context, sequence uint32) (xdr.LedgerCloseMeta, bool, error)
BatchGetLedgers(ctx context.Context, sequence uint32, batchSize uint) ([]xdr.LedgerCloseMeta, error)
StreamAllLedgers(ctx context.Context, f StreamLedgerFn) error
aditya1702 marked this conversation as resolved.
Show resolved Hide resolved
GetLedgerRange(ctx context.Context) (ledgerbucketwindow.LedgerRange, error)
StreamLedgerRange(ctx context.Context, startLedger uint32, endLedger uint32, f StreamLedgerFn) error
Expand Down Expand Up @@ -103,6 +104,25 @@ func (r ledgerReader) GetLedger(ctx context.Context, sequence uint32) (xdr.Ledge
}
}

// BatchGetLedgers fetches ledgers in batches from the db.
func (r ledgerReader) BatchGetLedgers(ctx context.Context, sequence uint32,
batchSize uint,
) ([]xdr.LedgerCloseMeta, error) {
sql := sq.Select("meta").
From(ledgerCloseMetaTableName).
Where(sq.And{
sq.GtOrEq{"sequence": sequence},
sq.LtOrEq{"sequence": sequence + uint32(batchSize) - 1},
})

results := make([]xdr.LedgerCloseMeta, 0, batchSize)
if err := r.db.Select(ctx, &results, sql); err != nil {
return nil, err
}

return results, nil
}

// GetLedgerRange pulls the min/max ledger sequence numbers from the meta table.
func (r ledgerReader) GetLedgerRange(ctx context.Context) (ledgerbucketwindow.LedgerRange, error) {
r.db.cache.RLock()
Expand Down
57 changes: 38 additions & 19 deletions cmd/soroban-rpc/internal/db/ledger_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -183,25 +183,8 @@ func TestGetLedgerRange_EmptyDB(t *testing.T) {
}

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]))
reader := NewLedgerReader(db)
testDB, lcms := setupBenchmarkingDB(b)
reader := NewLedgerReader(testDB)

b.ResetTimer()
for range b.N {
Expand All @@ -212,6 +195,20 @@ func BenchmarkGetLedgerRange(b *testing.B) {
}
}

func BenchmarkBatchGetLedgers(b *testing.B) {
testDB, lcms := setupBenchmarkingDB(b)
reader := NewLedgerReader(testDB)
batchSize := uint(200) // using the current maximum value for getLedgers endpoint

b.ResetTimer()
for range b.N {
ledgers, err := reader.BatchGetLedgers(context.TODO(), 1334, batchSize)
require.NoError(b, err)
assert.Equal(b, lcms[0].LedgerSequence(), ledgers[0].LedgerSequence())
assert.Equal(b, lcms[batchSize-1].LedgerSequence(), ledgers[batchSize-1].LedgerSequence())
}
}

func NewTestDB(tb testing.TB) *DB {
tmp := tb.TempDir()
dbPath := path.Join(tmp, "db.sqlite")
Expand All @@ -222,3 +219,25 @@ func NewTestDB(tb testing.TB) *DB {
})
return db
}

func setupBenchmarkingDB(b *testing.B) (*DB, []xdr.LedgerCloseMeta) {
testDB := NewTestDB(b)
logger := log.DefaultLogger
writer := NewReadWriter(logger, testDB, interfaces.MakeNoOpDeamon(),
100, 1_000_000, passphrase)
write, err := writer.NewTx(context.TODO())
require.NoError(b, err)

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]))
return testDB, lcms
}
6 changes: 6 additions & 0 deletions cmd/soroban-rpc/internal/db/mocks.go
Original file line number Diff line number Diff line change
Expand Up @@ -97,6 +97,12 @@ func (m *MockLedgerReader) GetLedger(_ context.Context, sequence uint32) (xdr.Le
return *lcm, true, nil
}

func (m *MockLedgerReader) BatchGetLedgers(_ context.Context, _ uint32,
_ uint,
) ([]xdr.LedgerCloseMeta, error) {
return []xdr.LedgerCloseMeta{}, nil
}

func (m *MockLedgerReader) StreamAllLedgers(_ context.Context, _ StreamLedgerFn) error {
return nil
}
Expand Down
71 changes: 71 additions & 0 deletions cmd/soroban-rpc/internal/integrationtest/get_ledgers_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
package integrationtest

import (
"context"
"testing"

"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"

"github.com/stellar/soroban-rpc/cmd/soroban-rpc/internal/integrationtest/infrastructure"
"github.com/stellar/soroban-rpc/cmd/soroban-rpc/internal/methods"
)

func TestGetLedgers(t *testing.T) {
test := infrastructure.NewTest(t, nil)
client := test.GetRPCLient()

// Get all ledgers
request := methods.GetLedgersRequest{
StartLedger: 8,
Pagination: &methods.LedgerPaginationOptions{
Limit: 3,
},
}
var result methods.GetLedgersResponse
err := client.CallResult(context.Background(), "getLedgers", request, &result)
require.NoError(t, err)
assert.Len(t, result.Ledgers, 3)
prevLedgers := result.Ledgers

// Get ledgers using previous result's cursor
request = methods.GetLedgersRequest{
Pagination: &methods.LedgerPaginationOptions{
Cursor: result.Cursor,
Limit: 2,
},
}
err = client.CallResult(context.Background(), "getLedgers", request, &result)
require.NoError(t, err)
assert.Len(t, result.Ledgers, 2)
assert.Equal(t, prevLedgers[len(prevLedgers)-1].Sequence+1, result.Ledgers[0].Sequence)

// Test with JSON format
request = methods.GetLedgersRequest{
StartLedger: 8,
Pagination: &methods.LedgerPaginationOptions{
Limit: 1,
},
Format: methods.FormatJSON,
}
err = client.CallResult(context.Background(), "getLedgers", request, &result)
require.NoError(t, err)
assert.NotEmpty(t, result.Ledgers[0].LedgerHeaderJSON)
assert.NotEmpty(t, result.Ledgers[0].LedgerCloseMetaJSON)

// Test invalid requests
invalidRequests := []methods.GetLedgersRequest{
{StartLedger: result.OldestLedger - 1},
{StartLedger: result.LatestLedger + 1},
{
Pagination: &methods.LedgerPaginationOptions{
Cursor: "invalid",
},
},
}

for _, req := range invalidRequests {
err = client.CallResult(context.Background(), "getLedgers", req, &result)
assert.Error(t, err)
}
}
8 changes: 8 additions & 0 deletions cmd/soroban-rpc/internal/jsonrpc.go
Original file line number Diff line number Diff line change
Expand Up @@ -199,6 +199,14 @@ func NewJSONRPCHandler(cfg *config.Config, params HandlerParams) Handler {
queueLimit: cfg.RequestBacklogGetLatestLedgerQueueLimit,
requestDurationLimit: cfg.MaxGetLatestLedgerExecutionDuration,
},
{
methodName: "getLedgers",
underlyingHandler: methods.NewGetLedgersHandler(params.LedgerReader,
cfg.MaxLedgersLimit, cfg.DefaultLedgersLimit),
longName: "get_ledgers",
queueLimit: cfg.RequestBacklogGetLedgersQueueLimit,
requestDurationLimit: cfg.MaxGetLedgersExecutionDuration,
},
{
methodName: "getLedgerEntry",
underlyingHandler: methods.NewGetLedgerEntryHandler(params.Logger, params.LedgerEntryReader),
Expand Down
6 changes: 6 additions & 0 deletions cmd/soroban-rpc/internal/methods/get_latest_ledger_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,12 @@ func (ledgerReader *ConstantLedgerReader) GetLedger(_ context.Context,
return createLedger(sequence, expectedLatestLedgerProtocolVersion, expectedLatestLedgerHashBytes), true, nil
}

func (ledgerReader *ConstantLedgerReader) BatchGetLedgers(_ context.Context, _ uint32,
_ uint,
) ([]xdr.LedgerCloseMeta, error) {
return []xdr.LedgerCloseMeta{}, nil
}

func (ledgerReader *ConstantLedgerReader) StreamAllLedgers(_ context.Context, _ db.StreamLedgerFn) error {
return nil
}
Expand Down
Loading
Loading