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

getLedgerEntries: optionally use high-performance Core server #353

Merged
merged 30 commits into from
Feb 7, 2025
Merged
Show file tree
Hide file tree
Changes from 6 commits
Commits
Show all changes
30 commits
Select commit Hold shift + click to select a range
681c353
Replace getLedgerEntries DB queries with Core fetches
Shaptic Jan 28, 2025
7b0efca
Merge branch 'main' into migrate-getledgerentries
2opremio Feb 3, 2025
41dd850
Bump stellar/go dependency
2opremio Feb 5, 2025
1cc589d
Fix typo
2opremio Feb 5, 2025
a471392
Fix docker compose file
2opremio Feb 5, 2025
65a43d4
Restore the db-based implementation and choose dynamically
2opremio Feb 5, 2025
d34282f
Fix bug in daemon
2opremio Feb 5, 2025
33018b6
Add infrastructure for testing the new Core http query server
2opremio Feb 5, 2025
a2f574a
Add initial getLedgerEntries test using Core
2opremio Feb 5, 2025
9891b39
Workaround core bug by enabling the http port
2opremio Feb 6, 2025
79bbbf5
Fix nil dereference bug
2opremio Feb 6, 2025
32077d6
Migrate the rest of the tests to using core
2opremio Feb 6, 2025
c55a95c
Refactor result formatting
2opremio Feb 6, 2025
583120e
Fix bug in obtention of entries from Core
2opremio Feb 6, 2025
8ae30ba
Sort entries in response according to request order
2opremio Feb 6, 2025
de200a1
Only test the query server from protocol 23 onwards
2opremio Feb 6, 2025
372638d
Appease the linter
2opremio Feb 6, 2025
2026291
Revert unwanted change
2opremio Feb 6, 2025
d28373f
Remove TODO
2opremio Feb 6, 2025
b9cf90d
Correct Debian package name
2opremio Feb 6, 2025
9be46b9
Fix test
2opremio Feb 6, 2025
c1a14cb
Another attempt at fixing the package
2opremio Feb 6, 2025
f008b22
Appease the linter
2opremio Feb 6, 2025
13d28da
We still can't upgrade to protocol 23
2opremio Feb 6, 2025
dda2a98
Enable debug printouts for integration tests
2opremio Feb 6, 2025
1a9e7cb
Make sure all ports are allocated at once to minimize clashes
2opremio Feb 6, 2025
82a419e
Merge branch 'main' into migrate-getledgerentries
2opremio Feb 6, 2025
1a53f82
Revert "Enable debug printouts for integration tests"
2opremio Feb 6, 2025
dc869a1
Revert "Make sure all ports are allocated at once to minimize clashes"
2opremio Feb 6, 2025
b91aee5
Fix bug
2opremio Feb 6, 2025
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
10 changes: 5 additions & 5 deletions .github/workflows/stellar-rpc.yml
Original file line number Diff line number Diff line change
Expand Up @@ -98,16 +98,16 @@ jobs:
strategy:
matrix:
os: [ ubuntu-20.04, ubuntu-22.04 ]
protocol-version: [ 21, 22 ]
protocol-version: [ 22, 23 ]
runs-on: ${{ matrix.os }}
env:
STELLAR_RPC_INTEGRATION_TESTS_ENABLED: true
STELLAR_RPC_INTEGRATION_TESTS_CORE_MAX_SUPPORTED_PROTOCOL: ${{ matrix.protocol-version }}
STELLAR_RPC_INTEGRATION_TESTS_CAPTIVE_CORE_BIN: /usr/bin/stellar-core
PROTOCOL_21_CORE_DEBIAN_PKG_VERSION: 22.0.0-2138.721fd0a65.focal
PROTOCOL_21_CORE_DOCKER_IMG: stellar/stellar-core:22.0.0-2138.721fd0a65.focal
PROTOCOL_22_CORE_DEBIAN_PKG_VERSION: 22.0.0-2138.721fd0a65.focal
PROTOCOL_22_CORE_DOCKER_IMG: stellar/stellar-core:22.0.0-2138.721fd0a65.focal
PROTOCOL_22_CORE_DEBIAN_PKG_VERSION: 22.1.0-2194.0241e79f7.focal
PROTOCOL_22_CORE_DOCKER_IMG: stellar/stellar-core:22.1.0-2194.0241e79f7.focal
PROTOCOL_23_CORE_DEBIAN_PKG_VERSION: 22.1.1-2251.ac9f21ac7.focal-do-not-use-in-prd
PROTOCOL_23_CORE_DOCKER_IMG: stellar/unsafe-stellar-core:22.1.1-2251.ac9f21ac7.focal-do-not-use-in-prd

steps:
- uses: actions/checkout@v4
Expand Down
13 changes: 8 additions & 5 deletions cmd/stellar-rpc/internal/config/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,11 +14,14 @@ type Config struct {

Strict bool

StellarCoreURL string
CaptiveCoreStoragePath string
StellarCoreBinaryPath string
CaptiveCoreConfigPath string
CaptiveCoreHTTPPort uint
StellarCoreURL string
CaptiveCoreStoragePath string
StellarCoreBinaryPath string
CaptiveCoreConfigPath string
CaptiveCoreHTTPPort uint
CaptiveCoreHTTPQueryPort uint
CaptiveCoreHTTPQueryThreadPoolSize uint
CaptiveCoreHTTPQuerySnapshotLedgers uint

Endpoint string
AdminEndpoint string
Expand Down
24 changes: 22 additions & 2 deletions cmd/stellar-rpc/internal/config/options.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,8 @@ const (
OneDayOfLedgers = 17280
SevenDayOfLedgers = OneDayOfLedgers * 7

defaultHTTPEndpoint = "localhost:8000"
defaultHTTPEndpoint = "localhost:8000"
defaultCaptiveCoreHTTPPort = 11626 // regular queries like /info
)

// TODO: refactor and remove the linter exceptions
Expand Down Expand Up @@ -60,6 +61,7 @@ func (cfg *Config) options() Options {
Usage: "Admin endpoint to listen and serve on. WARNING: this should not be accessible from the Internet and does not use TLS. \"\" (default) disables the admin server",
ConfigKey: &cfg.AdminEndpoint,
},
// TODO: should be gave a similar stellar-core-query-url parameter or should we assume that all queries will happen to the local captive core?
2opremio marked this conversation as resolved.
Show resolved Hide resolved
{
Name: "stellar-core-url",
Usage: "URL used to query Stellar Core (local captive core by default)",
Expand All @@ -84,7 +86,25 @@ func (cfg *Config) options() Options {
Name: "stellar-captive-core-http-port",
Usage: "HTTP port for Captive Core to listen on (0 disables the HTTP server)",
ConfigKey: &cfg.CaptiveCoreHTTPPort,
DefaultValue: uint(11626),
DefaultValue: uint(defaultCaptiveCoreHTTPPort),
},
{
Name: "stellar-captive-core-http-query-port",
Usage: "HTTP port for Captive Core to listen on for high-performance queries like /getledgerentry (0 disables the HTTP server, must not conflict with CAPTIVE_CORE_HTTP_PORT)",
ConfigKey: &cfg.CaptiveCoreHTTPQueryPort,
DefaultValue: uint(0), // Disabled by default, although it normally uses 11628
},
{
Name: "stellar-captive-core-http-query-thread-pool-size",
Usage: "Number of threads to use by Captive Core's high-performance query server",
ConfigKey: &cfg.CaptiveCoreHTTPQueryThreadPoolSize,
DefaultValue: uint(runtime.NumCPU()), //nolint:gosec
},
{
Name: "stellar-captive-core-http-query-snapshot-ledgers",
Usage: "Size of ledger history in Captive Core's high-performance query server (don't touch unless you know what you are doing)",
ConfigKey: &cfg.CaptiveCoreHTTPQuerySnapshotLedgers,
DefaultValue: uint(4),
},
{
Name: "log-level",
Expand Down
1 change: 1 addition & 0 deletions cmd/stellar-rpc/internal/config/test.soroban.rpc.config
Original file line number Diff line number Diff line change
Expand Up @@ -8,4 +8,5 @@ STELLAR_CORE_BINARY_PATH="/usr/bin/stellar-core"
HISTORY_ARCHIVE_URLS=["http://localhost:1570"]
DB_PATH="/opt/stellar/stellar-rpc/rpc_db.sqlite"
STELLAR_CAPTIVE_CORE_HTTP_PORT=0
STELLAR_CAPTIVE_CORE_HTTP_QUERY_PORT=11628
CHECKPOINT_FREQUENCY=64
40 changes: 34 additions & 6 deletions cmd/stellar-rpc/internal/daemon/daemon.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
import (
"context"
"errors"
"fmt"
"net"
"net/http"
"net/http/pprof"
Expand Down Expand Up @@ -49,6 +50,7 @@
type Daemon struct {
core *ledgerbackend.CaptiveStellarCore
coreClient *CoreClientWithMetrics
coreQueryingClient interfaces.FastCoreClient
ingestService *ingest.Service
db *db.DB
jsonRPCHandler *internal.Handler
Expand Down Expand Up @@ -120,6 +122,16 @@

// newCaptiveCore creates a new captive core backend instance and returns it.
func newCaptiveCore(cfg *config.Config, logger *supportlog.Entry) (*ledgerbackend.CaptiveStellarCore, error) {
var queryServerParams *ledgerbackend.HTTPQueryServerParams
if cfg.CaptiveCoreHTTPPort != 0 {
Shaptic marked this conversation as resolved.
Show resolved Hide resolved
// Only try to enable the server if the port passed is non-zero
queryServerParams = &ledgerbackend.HTTPQueryServerParams{
Port: uint16(cfg.CaptiveCoreHTTPQueryPort),

Check failure on line 129 in cmd/stellar-rpc/internal/daemon/daemon.go

View workflow job for this annotation

GitHub Actions / golangci-lint

G115: integer overflow conversion uint -> uint16 (gosec)
ThreadPoolSize: uint16(cfg.CaptiveCoreHTTPQueryThreadPoolSize),

Check failure on line 130 in cmd/stellar-rpc/internal/daemon/daemon.go

View workflow job for this annotation

GitHub Actions / golangci-lint

G115: integer overflow conversion uint -> uint16 (gosec)
SnapshotLedgers: uint16(cfg.CaptiveCoreHTTPQuerySnapshotLedgers),

Check failure on line 131 in cmd/stellar-rpc/internal/daemon/daemon.go

View workflow job for this annotation

GitHub Actions / golangci-lint

G115: integer overflow conversion uint -> uint16 (gosec)
}
}

captiveCoreTomlParams := ledgerbackend.CaptiveCoreTomlParams{
HTTPPort: &cfg.CaptiveCoreHTTPPort,
HistoryArchiveURLs: cfg.HistoryArchiveURLs,
Expand All @@ -129,6 +141,7 @@
EnforceSorobanDiagnosticEvents: true,
EnforceSorobanTransactionMetaExtV1: true,
CoreBinaryPath: cfg.StellarCoreBinaryPath,
HTTPQueryServerParams: queryServerParams,
}
captiveCoreToml, err := ledgerbackend.NewCaptiveCoreTomlFromFile(cfg.CaptiveCoreConfigPath, captiveCoreTomlParams)
if err != nil {
Expand Down Expand Up @@ -156,12 +169,13 @@
metricsRegistry := prometheus.NewRegistry()

daemon := &Daemon{
logger: logger,
core: core,
db: mustOpenDatabase(cfg, logger, metricsRegistry),
done: make(chan struct{}),
metricsRegistry: metricsRegistry,
coreClient: newCoreClientWithMetrics(createStellarCoreClient(cfg), metricsRegistry),
logger: logger,
core: core,
db: mustOpenDatabase(cfg, logger, metricsRegistry),
done: make(chan struct{}),
metricsRegistry: metricsRegistry,
coreClient: newCoreClientWithMetrics(createStellarCoreClient(cfg), metricsRegistry),
coreQueryingClient: createHighperfStellarCoreClient(cfg),
}

feewindows := daemon.mustInitializeStorage(cfg)
Expand Down Expand Up @@ -235,6 +249,17 @@
}
}

func createHighperfStellarCoreClient(cfg *config.Config) interfaces.FastCoreClient {
// It doesn't make sense to create a client if the local server is not enabled
if cfg.CaptiveCoreHTTPQueryPort == 0 {
return nil
}
return &stellarcore.Client{
URL: fmt.Sprintf("http://localhost:%d", cfg.CaptiveCoreHTTPQueryPort),
Shaptic marked this conversation as resolved.
Show resolved Hide resolved
HTTP: &http.Client{Timeout: cfg.CoreRequestTimeout},
}
}

func createIngestService(cfg *config.Config, logger *supportlog.Entry, daemon *Daemon,
feewindows *feewindow.FeeWindows, historyArchive *historyarchive.ArchiveInterface,
) *ingest.Service {
Expand Down Expand Up @@ -486,3 +511,6 @@
return
}
}

// Ensure the daemon conforms to the interface
var _ interfaces.Daemon = (*Daemon)(nil)
6 changes: 6 additions & 0 deletions cmd/stellar-rpc/internal/daemon/interfaces/interfaces.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import (

"github.com/stellar/go/ingest/ledgerbackend"
proto "github.com/stellar/go/protocols/stellarcore"
"github.com/stellar/go/xdr"
)

// Daemon defines the interface that the Daemon would be implementing.
Expand All @@ -16,10 +17,15 @@ type Daemon interface {
MetricsRegistry() *prometheus.Registry
MetricsNamespace() string
CoreClient() CoreClient
FastCoreClient() FastCoreClient
GetCore() *ledgerbackend.CaptiveStellarCore
}

type CoreClient interface {
Info(ctx context.Context) (*proto.InfoResponse, error)
SubmitTransaction(ctx context.Context, txBase64 string) (*proto.TXResponse, error)
}

type FastCoreClient interface {
GetLedgerEntries(ctx context.Context, ledgerSeq uint32, keys ...xdr.LedgerKey) (proto.GetLedgerEntryResponse, error)
}
9 changes: 9 additions & 0 deletions cmd/stellar-rpc/internal/daemon/interfaces/noOpDaemon.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@

"github.com/stellar/go/ingest/ledgerbackend"
proto "github.com/stellar/go/protocols/stellarcore"
"github.com/stellar/go/xdr"
)

// TODO: deprecate and rename to stellar_rpc
Expand Down Expand Up @@ -41,6 +42,10 @@
return d.coreClient
}

func (d *NoOpDaemon) FastCoreClient() FastCoreClient {
return d.coreClient
}

func (d *NoOpDaemon) GetCore() *ledgerbackend.CaptiveStellarCore {
return d.core
}
Expand All @@ -54,3 +59,7 @@
func (s noOpCoreClient) SubmitTransaction(context.Context, string) (*proto.TXResponse, error) {
return &proto.TXResponse{Status: proto.PreflightStatusOk}, nil
}

func (s noOpCoreClient) GetLedgerEntries(context.Context, uint32, ...xdr.LedgerKey) (proto.GetLedgerEntryResponse, error) {

Check failure on line 63 in cmd/stellar-rpc/internal/daemon/interfaces/noOpDaemon.go

View workflow job for this annotation

GitHub Actions / golangci-lint

The line is 123 characters long, which exceeds the maximum of 120 characters. (lll)
return proto.GetLedgerEntryResponse{}, nil
}
4 changes: 4 additions & 0 deletions cmd/stellar-rpc/internal/daemon/metrics.go
Original file line number Diff line number Diff line change
Expand Up @@ -111,6 +111,10 @@ func (d *Daemon) CoreClient() interfaces.CoreClient {
return d.coreClient
}

func (d *Daemon) FastCoreClient() interfaces.FastCoreClient {
return d.coreQueryingClient
}

func (d *Daemon) GetCore() *ledgerbackend.CaptiveStellarCore {
return d.core
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,4 +21,4 @@ PUBLIC_KEY="GD5KD2KEZJIGTC63IGW6UMUSMVUVG5IHG64HUTFWCHVZH2N2IBOQN7PS"

# should be "core" when running RPC in a container or "localhost:port" when running RPC in the host
ADDRESS="${CORE_HOST_PORT}"
QUALITY="MEDIUM"
QUALITY="MEDIUM"
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,8 @@ services:
# Note: Please keep the image pinned to an immutable tag matching the Captive Core version.
# This avoids implicit updates which break compatibility between
# the Core container and captive core.
image: ${CORE_IMAGE:-stellar/stellar-core:22.0.0-2138.721fd0a65.focal}
image: ${CORE_IMAGE:-stellar/unsafe-stellar-core:22.1.1-2251.ac9f21ac7.focal-do-not-use-in-prd}

depends_on:
- core-postgres
environment:
Expand All @@ -23,6 +24,8 @@ services:
- "127.0.0.1:0:11625"
# http
- "127.0.0.1:0:11626"
# high-perf http
- "127.0.0.1:0:11628"
# history archive
- "127.0.0.1:0:1570"
entrypoint: /usr/bin/env
Expand Down
23 changes: 13 additions & 10 deletions cmd/stellar-rpc/internal/integrationtest/infrastructure/test.go
Original file line number Diff line number Diff line change
Expand Up @@ -302,6 +302,7 @@
coreBinaryPath string
captiveCoreConfigPath string
captiveCoreStoragePath string
captiveCoreQueryPort int

Check failure on line 305 in cmd/stellar-rpc/internal/integrationtest/infrastructure/test.go

View workflow job for this annotation

GitHub Actions / golangci-lint

field `captiveCoreQueryPort` is unused (unused)
archiveURL string
sqlitePath string
}
Expand All @@ -316,16 +317,18 @@
"CAPTIVE_CORE_CONFIG_PATH": vars.captiveCoreConfigPath,
"CAPTIVE_CORE_STORAGE_PATH": vars.captiveCoreStoragePath,
"STELLAR_CAPTIVE_CORE_HTTP_PORT": "0",
"FRIENDBOT_URL": FriendbotURL,
"NETWORK_PASSPHRASE": StandaloneNetworkPassphrase,
"HISTORY_ARCHIVE_URLS": vars.archiveURL,
"LOG_LEVEL": "debug",
"DB_PATH": vars.sqlitePath,
"INGESTION_TIMEOUT": "10m",
"HISTORY_RETENTION_WINDOW": strconv.Itoa(config.OneDayOfLedgers),
"CHECKPOINT_FREQUENCY": strconv.Itoa(checkpointFrequency),
"MAX_HEALTHY_LEDGER_LATENCY": "10s",
"PREFLIGHT_ENABLE_DEBUG": "true",
// TODO: allow setting it for getledgerentry tests
// "STELLAR_CAPTIVE_CORE_HTTP_QUERY_PORT": strconv.Itoa(vars.captiveCoreQueryPort),
"FRIENDBOT_URL": FriendbotURL,
"NETWORK_PASSPHRASE": StandaloneNetworkPassphrase,
"HISTORY_ARCHIVE_URLS": vars.archiveURL,
"LOG_LEVEL": "debug",
"DB_PATH": vars.sqlitePath,
"INGESTION_TIMEOUT": "10m",
"HISTORY_RETENTION_WINDOW": strconv.Itoa(config.OneDayOfLedgers),
"CHECKPOINT_FREQUENCY": strconv.Itoa(checkpointFrequency),
"MAX_HEALTHY_LEDGER_LATENCY": "10s",
"PREFLIGHT_ENABLE_DEBUG": "true",
}
}

Expand Down
11 changes: 10 additions & 1 deletion cmd/stellar-rpc/internal/jsonrpc.go
Original file line number Diff line number Diff line change
Expand Up @@ -156,6 +156,15 @@ func NewJSONRPCHandler(cfg *config.Config, params HandlerParams) Handler {

retentionWindow := cfg.HistoryRetentionWindow

getLedgerEntriesHandler := methods.NewGetLedgerEntriesFromDBHandler(params.Logger, params.LedgerEntryReader)
if params.Daemon.FastCoreClient() != nil {
// Prioritize getting ledger entries from core if available
getLedgerEntriesHandler = methods.NewGetLedgerEntriesFromCoreHandler(
params.Logger,
params.Daemon.FastCoreClient(),
params.LedgerEntryReader)
}

handlers := []struct {
methodName string
underlyingHandler jrpc2.Handler
Expand Down Expand Up @@ -222,7 +231,7 @@ func NewJSONRPCHandler(cfg *config.Config, params HandlerParams) Handler {
},
{
methodName: protocol.GetLedgerEntriesMethodName,
underlyingHandler: methods.NewGetLedgerEntriesHandler(params.Logger, params.LedgerEntryReader),
underlyingHandler: getLedgerEntriesHandler,
longName: toSnakeCase(protocol.GetLedgerEntriesMethodName),
queueLimit: cfg.RequestBacklogGetLedgerEntriesQueueLimit,
requestDurationLimit: cfg.MaxGetLedgerEntriesExecutionDuration,
Expand Down
Loading
Loading