Skip to content

Commit

Permalink
Add ledger range to getHealth endpoint
Browse files Browse the repository at this point in the history
  • Loading branch information
2opremio committed Apr 11, 2024
1 parent ce972f2 commit 1a74baa
Show file tree
Hide file tree
Showing 6 changed files with 73 additions and 50 deletions.
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"`
}

// 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

0 comments on commit 1a74baa

Please sign in to comment.