Skip to content

Commit

Permalink
services/horizon: Limit sql queries for history endpoints to retentio…
Browse files Browse the repository at this point in the history
…n history
  • Loading branch information
urvisavla committed Aug 29, 2024
1 parent f2f4a0e commit c473ca1
Show file tree
Hide file tree
Showing 5 changed files with 33 additions and 31 deletions.
20 changes: 12 additions & 8 deletions services/horizon/internal/actions/helpers.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package actions
import (
"context"
"encoding/hex"
"errors"
"fmt"
"net/http"
"net/url"
Expand All @@ -20,7 +21,6 @@ import (
"github.com/stellar/go/services/horizon/internal/db2"
"github.com/stellar/go/services/horizon/internal/ledger"
hProblem "github.com/stellar/go/services/horizon/internal/render/problem"
"github.com/stellar/go/support/errors"
"github.com/stellar/go/support/ordered"
"github.com/stellar/go/support/render/problem"
"github.com/stellar/go/toid"
Expand Down Expand Up @@ -171,7 +171,7 @@ func getLimit(r *http.Request, name string, def uint64, max uint64) (uint64, err
if asI64 <= 0 {
err = errors.New("invalid limit: non-positive value provided")
} else if asI64 > int64(max) {
err = errors.Errorf("invalid limit: value provided that is over limit max of %d", max)
err = fmt.Errorf("invalid limit: value provided that is over limit max of %d", max)
}

if err != nil {
Expand Down Expand Up @@ -221,12 +221,16 @@ func GetPageQuery(ledgerState *ledger.State, r *http.Request, opts ...Opt) (db2.

return db2.PageQuery{}, err
}
if cursor == "" && defaultTOID {
if pageQuery.Order == db2.OrderAscending {
pageQuery.Cursor = toid.AfterLedger(
ordered.Max(0, ledgerState.CurrentStatus().HistoryElder-1),
).String()
elderCursor := toid.AfterLedger(
ordered.Max(0, ledgerState.CurrentStatus().HistoryElder-1),
).String()

if defaultTOID && pageQuery.Order == db2.OrderAscending {
if cursor == "" || errors.Is(validateCursorWithinHistory(ledgerState, pageQuery), &hProblem.BeforeHistory) {
pageQuery.Cursor = elderCursor
}
} else if defaultTOID && pageQuery.Order == db2.OrderDescending {
pageQuery.ElderCursor = elderCursor
}

return pageQuery, nil
Expand Down Expand Up @@ -596,7 +600,7 @@ func countNonEmpty(params ...interface{}) (int, error) {
for _, param := range params {
switch param := param.(type) {
default:
return 0, errors.Errorf("unexpected type %T", param)
return 0, fmt.Errorf("unexpected type %T", param)
case int32:
if param != 0 {
count++
Expand Down
2 changes: 1 addition & 1 deletion services/horizon/internal/db2/history/account_signers.go
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ func (q *Q) SignersForAccounts(ctx context.Context, accounts []string) ([]Accoun
// AccountsForSigner returns a list of `AccountSigner` rows for a given signer
func (q *Q) AccountsForSigner(ctx context.Context, signer string, page db2.PageQuery) ([]AccountSigner, error) {
sql := selectAccountSigners.Where("accounts_signers.signer = ?", signer)
sql, err := page.ApplyToUsingCursor(sql, "accounts_signers.account_id", page.Cursor)
sql, err := page.ApplyRawTo(sql, "accounts_signers.account_id")
if err != nil {
return nil, errors.Wrap(err, "could not apply query to page")
}
Expand Down
8 changes: 4 additions & 4 deletions services/horizon/internal/db2/history/accounts.go
Original file line number Diff line number Diff line change
Expand Up @@ -144,7 +144,7 @@ func (q *Q) AccountsForAsset(ctx context.Context, asset xdr.Asset, page db2.Page
"trust_lines.asset_code": code,
})

sql, err := page.ApplyToUsingCursor(sql, "trust_lines.account_id", page.Cursor)
sql, err := page.ApplyRawTo(sql, "trust_lines.account_id")
if err != nil {
return nil, errors.Wrap(err, "could not apply query to page")
}
Expand All @@ -168,7 +168,7 @@ func (q *Q) AccountsForLiquidityPool(ctx context.Context, poolID string, page db
"trust_lines.liquidity_pool_id": poolID,
})

sql, err := page.ApplyToUsingCursor(sql, "trust_lines.account_id", page.Cursor)
sql, err := page.ApplyRawTo(sql, "trust_lines.account_id")
if err != nil {
return nil, errors.Wrap(err, "could not apply query to page")
}
Expand All @@ -189,7 +189,7 @@ func selectBySponsor(table, sponsor string, page db2.PageQuery) (sq.SelectBuilde
"sponsor": sponsor,
})

sql, err := page.ApplyToUsingCursor(sql, "account_id", page.Cursor)
sql, err := page.ApplyRawTo(sql, "account_id")
if err != nil {
return sql, errors.Wrap(err, "could not apply query to page")
}
Expand Down Expand Up @@ -255,7 +255,7 @@ func (q *Q) AccountEntriesForSigner(ctx context.Context, signer string, page db2
"accounts_signers.signer": signer,
})

sql, err := page.ApplyToUsingCursor(sql, "accounts_signers.account_id", page.Cursor)
sql, err := page.ApplyRawTo(sql, "accounts_signers.account_id")
if err != nil {
return nil, errors.Wrap(err, "could not apply query to page")
}
Expand Down
7 changes: 4 additions & 3 deletions services/horizon/internal/db2/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,8 @@ package db2
// PageQuery represents a portion of a Query struct concerned with paging
// through a large dataset.
type PageQuery struct {
Cursor string
Order string
Limit uint64
Cursor string
Order string
Limit uint64
ElderCursor string
}
27 changes: 12 additions & 15 deletions services/horizon/internal/db2/page_query.go
Original file line number Diff line number Diff line change
Expand Up @@ -59,20 +59,20 @@ func (p PageQuery) ApplyTo(
return sql, err
}

return p.ApplyToUsingCursor(sql, col, cursor)
return p.applyToUsingCursor(sql, col, cursor)
}

// ApplyRawTo applies the raw PageQuery.Cursor cursor to the builder
func (p PageQuery) ApplyRawTo(
sql sq.SelectBuilder,
col string,
) (sq.SelectBuilder, error) {
return p.ApplyToUsingCursor(sql, col, p.Cursor)
return p.applyToUsingCursor(sql, col, p.Cursor)
}

// ApplyToUsingCursor returns a new SelectBuilder after applying the paging effects of
// `p` to `sql`. This method allows any type of cursor by a single column
func (p PageQuery) ApplyToUsingCursor(
func (p PageQuery) applyToUsingCursor(
sql sq.SelectBuilder,
col string,
cursor interface{},
Expand All @@ -81,27 +81,24 @@ func (p PageQuery) ApplyToUsingCursor(

switch p.Order {
case "asc":
if cursor == "" {
if cursor != "" {
sql = sql.
OrderBy(fmt.Sprintf("%s asc", col))
} else {
sql = sql.
Where(fmt.Sprintf("%s > ?", col), cursor).
OrderBy(fmt.Sprintf("%s asc", col))
Where(fmt.Sprintf("%s > ?", col), cursor)
}
case "desc":
if cursor == "" {
if cursor != "" {
sql = sql.
OrderBy(fmt.Sprintf("%s desc", col))
} else {
Where(fmt.Sprintf("%s < ?", col), cursor)
}
if p.ElderCursor != "" {
sql = sql.
Where(fmt.Sprintf("%s < ?", col), cursor).
OrderBy(fmt.Sprintf("%s desc", col))
Where(fmt.Sprintf("%s > ?", col), p.ElderCursor)
}
default:
return sql, errors.Errorf("invalid order: %s", p.Order)
}

sql = sql.
OrderBy(fmt.Sprintf("%s %s", col, p.Order))

Check failure

Code scanning / CodeQL

Database query built from user-controlled sources High

This query depends on a
user-provided value
.
This query depends on a
user-provided value
.
return sql, nil
}

Expand Down

0 comments on commit c473ca1

Please sign in to comment.