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 ledger range to getHealth endpoint #133

Merged
merged 4 commits into from
Apr 12, 2024
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
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
7 changes: 7 additions & 0 deletions cmd/soroban-rpc/internal/events/events.go
Original file line number Diff line number Diff line change
Expand Up @@ -264,3 +264,10 @@ func readEvents(networkPassphrase string, ledgerCloseMeta xdr.LedgerCloseMeta) (
}
return events, err
}

// GetLedgerRange returns the first and latest ledger available in the store.
func (m *MemoryStore) GetLedgerRange() ledgerbucketwindow.LedgerRange {
m.lock.RLock()
defer m.lock.RUnlock()
return m.eventsByLedger.GetLedgerRange()
}
2 changes: 1 addition & 1 deletion cmd/soroban-rpc/internal/jsonrpc.go
Original file line number Diff line number Diff line change
Expand Up @@ -143,7 +143,7 @@ func NewJSONRPCHandler(cfg *config.Config, params HandlerParams) Handler {
}{
{
methodName: "getHealth",
underlyingHandler: methods.NewHealthCheck(params.TransactionStore, cfg.MaxHealthyLedgerLatency),
underlyingHandler: methods.NewHealthCheck(params.TransactionStore, params.EventStore, cfg.MaxHealthyLedgerLatency),
longName: "get_health",
queueLimit: cfg.RequestBacklogGetHealthQueueLimit,
requestDurationLimit: cfg.MaxGetHealthExecutionDuration,
Expand Down
29 changes: 29 additions & 0 deletions cmd/soroban-rpc/internal/ledgerbucketwindow/ledgerbucketwindow.go
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,35 @@ func (w *LedgerBucketWindow[T]) Len() uint32 {
return uint32(len(w.buckets))
}

type LedgerInfo struct {
Sequence uint32
CloseTime int64
}

type LedgerRange struct {
FirstLedger LedgerInfo
LastLedger LedgerInfo
}

func (w *LedgerBucketWindow[T]) GetLedgerRange() LedgerRange {
length := w.Len()
if length == 0 {
return LedgerRange{}
}
firstBucket := w.Get(0)
lastBucket := w.Get(length - 1)
return LedgerRange{
FirstLedger: LedgerInfo{
Sequence: firstBucket.LedgerSeq,
CloseTime: firstBucket.LedgerCloseTimestamp,
},
LastLedger: LedgerInfo{
Sequence: lastBucket.LedgerSeq,
CloseTime: lastBucket.LedgerCloseTimestamp,
},
}
}

// Get obtains a bucket from the window
func (w *LedgerBucketWindow[T]) Get(i uint32) *LedgerBucket[T] {
length := w.Len()
Expand Down
29 changes: 23 additions & 6 deletions cmd/soroban-rpc/internal/methods/health.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,24 +8,36 @@ import (
"github.com/creachadair/jrpc2"
"github.com/creachadair/jrpc2/handler"

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

type HealthCheckResult struct {
Status string `json:"status"`
Status string `json:"status"`
LatestLedger uint32 `json:"latestLedger"`
FirstLedger uint32 `json:"firstLedger"`
2opremio marked this conversation as resolved.
Show resolved Hide resolved
}

// NewHealthCheck returns a health check json rpc handler
func NewHealthCheck(txStore *transactions.MemoryStore, maxHealthyLedgerLatency time.Duration) jrpc2.Handler {
func NewHealthCheck(txStore *transactions.MemoryStore, evStore *events.MemoryStore, maxHealthyLedgerLatency time.Duration) jrpc2.Handler {
return handler.New(func(ctx context.Context) (HealthCheckResult, error) {
ledgerInfo := txStore.GetLatestLedger()
if ledgerInfo.Sequence < 1 {
txLedgerRange := txStore.GetLedgerRange()
evLedgerRange := evStore.GetLedgerRange()
if txLedgerRange.FirstLedger.Sequence < 1 || evLedgerRange.FirstLedger.Sequence < 1 {
return HealthCheckResult{}, jrpc2.Error{
Code: jrpc2.InternalError,
Message: "data stores are not initialized",
}
}
lastKnownLedgerCloseTime := time.Unix(ledgerInfo.CloseTime, 0)
mergedRange := evLedgerRange
if txLedgerRange.FirstLedger.Sequence < mergedRange.FirstLedger.Sequence {
mergedRange.FirstLedger = txLedgerRange.FirstLedger
}
if txLedgerRange.LastLedger.Sequence > mergedRange.LastLedger.Sequence {
mergedRange.LastLedger = txLedgerRange.LastLedger
}

lastKnownLedgerCloseTime := time.Unix(mergedRange.LastLedger.CloseTime, 0)
lastKnownLedgerLatency := time.Since(lastKnownLedgerCloseTime)
if lastKnownLedgerLatency > maxHealthyLedgerLatency {
roundedLatency := lastKnownLedgerLatency.Round(time.Second)
Expand All @@ -35,6 +47,11 @@ func NewHealthCheck(txStore *transactions.MemoryStore, maxHealthyLedgerLatency t
Message: msg,
}
}
return HealthCheckResult{Status: "healthy"}, nil
result := HealthCheckResult{
Status: "healthy",
LatestLedger: mergedRange.LastLedger.Sequence,
FirstLedger: mergedRange.FirstLedger.Sequence,
}
return result, nil
})
}
45 changes: 7 additions & 38 deletions cmd/soroban-rpc/internal/transactions/transactions.go
Original file line number Diff line number Diff line change
Expand Up @@ -140,11 +140,6 @@ func (m *MemoryStore) IngestTransactions(ledgerCloseMeta xdr.LedgerCloseMeta) er
return nil
}

type LedgerInfo struct {
Sequence uint32
CloseTime int64
}

type Transaction struct {
Result []byte // XDR encoded xdr.TransactionResult
Meta []byte // XDR encoded xdr.TransactionMeta
Expand All @@ -153,48 +148,22 @@ type Transaction struct {
FeeBump bool
ApplicationOrder int32
Successful bool
Ledger LedgerInfo
}

type StoreRange struct {
FirstLedger LedgerInfo
LastLedger LedgerInfo
Ledger ledgerbucketwindow.LedgerInfo
}

// GetLatestLedger returns the latest ledger available in the store.
func (m *MemoryStore) GetLatestLedger() LedgerInfo {
// GetLedgerRange returns the first and latest ledger available in the store.
func (m *MemoryStore) GetLedgerRange() ledgerbucketwindow.LedgerRange {
m.lock.RLock()
defer m.lock.RUnlock()
if m.transactionsByLedger.Len() > 0 {
lastBucket := m.transactionsByLedger.Get(m.transactionsByLedger.Len() - 1)
return LedgerInfo{
Sequence: lastBucket.LedgerSeq,
CloseTime: lastBucket.LedgerCloseTimestamp,
}
}
return LedgerInfo{}
return m.transactionsByLedger.GetLedgerRange()
}

// GetTransaction obtains a transaction from the store and whether it's present and the current store range
func (m *MemoryStore) GetTransaction(hash xdr.Hash) (Transaction, bool, StoreRange) {
func (m *MemoryStore) GetTransaction(hash xdr.Hash) (Transaction, bool, ledgerbucketwindow.LedgerRange) {
startTime := time.Now()
m.lock.RLock()
defer m.lock.RUnlock()
var storeRange StoreRange
if m.transactionsByLedger.Len() > 0 {
firstBucket := m.transactionsByLedger.Get(0)
lastBucket := m.transactionsByLedger.Get(m.transactionsByLedger.Len() - 1)
storeRange = StoreRange{
FirstLedger: LedgerInfo{
Sequence: firstBucket.LedgerSeq,
CloseTime: firstBucket.LedgerCloseTimestamp,
},
LastLedger: LedgerInfo{
Sequence: lastBucket.LedgerSeq,
CloseTime: lastBucket.LedgerCloseTimestamp,
},
}
}
storeRange := m.transactionsByLedger.GetLedgerRange()
internalTx, ok := m.transactions[hash]
if !ok {
return Transaction{}, false, storeRange
Expand Down Expand Up @@ -229,7 +198,7 @@ func (m *MemoryStore) GetTransaction(hash xdr.Hash) (Transaction, bool, StoreRan
FeeBump: internalTx.feeBump,
Successful: internalTx.successful,
ApplicationOrder: internalTx.applicationOrder,
Ledger: LedgerInfo{
Ledger: ledgerbucketwindow.LedgerInfo{
Sequence: internalTx.bucket.LedgerSeq,
CloseTime: internalTx.bucket.LedgerCloseTimestamp,
},
Expand Down
11 changes: 6 additions & 5 deletions cmd/soroban-rpc/internal/transactions/transactions_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import (
"github.com/stretchr/testify/require"

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

func expectedTransaction(t *testing.T, ledger uint32, feeBump bool) Transaction {
Expand All @@ -36,16 +37,16 @@ func expectedTransaction(t *testing.T, ledger uint32, feeBump bool) Transaction
return tx
}

func expectedLedgerInfo(ledgerSequence uint32) LedgerInfo {
return LedgerInfo{
func expectedLedgerInfo(ledgerSequence uint32) ledgerbucketwindow.LedgerInfo {
return ledgerbucketwindow.LedgerInfo{
Sequence: ledgerSequence,
CloseTime: ledgerCloseTime(ledgerSequence),
}

}

func expectedStoreRange(startLedger uint32, endLedger uint32) StoreRange {
return StoreRange{
func expectedStoreRange(startLedger uint32, endLedger uint32) ledgerbucketwindow.LedgerRange {
return ledgerbucketwindow.LedgerRange{
FirstLedger: expectedLedgerInfo(startLedger),
LastLedger: expectedLedgerInfo(endLedger),
}
Expand Down Expand Up @@ -295,7 +296,7 @@ func TestIngestTransactions(t *testing.T) {

_, ok, storeRange := store.GetTransaction(txHash(1, false))
require.False(t, ok)
require.Equal(t, StoreRange{}, storeRange)
require.Equal(t, ledgerbucketwindow.LedgerRange{}, storeRange)

// Insert ledger 1
require.NoError(t, store.IngestTransactions(txMeta(1, false)))
Expand Down
Loading