Skip to content

Commit a356b55

Browse files
authored
services/horizon/docker: cdp enabled verify range image (#5736)
1 parent 032c5f9 commit a356b55

File tree

5 files changed

+269
-158
lines changed

5 files changed

+269
-158
lines changed

.github/workflows/horizon.yml

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -124,8 +124,7 @@ jobs:
124124
runs-on: ubuntu-22.04
125125
env:
126126
GO_VERSION: 1.23.4
127-
STELLAR_CORE_VERSION: 22.3.0-2485.e643061a4.focal
128-
CAPTIVE_CORE_STORAGE_PATH: /tmp
127+
STELLAR_CORE_VERSION: 22.3.0-2485.e643061a4.jammy
129128
steps:
130129
- uses: actions/checkout@v3
131130
with:
@@ -134,7 +133,7 @@ jobs:
134133

135134
- name: Build and test the Verify Range Docker image
136135
run: |
137-
docker build --build-arg="GO_VERSION=$GO_VERSION" -f services/horizon/docker/verify-range/Dockerfile -t stellar/horizon-verify-range services/horizon/docker/verify-range/
136+
docker build --build-arg="GO_VERSION=$GO_VERSION" --build-arg="STELLAR_CORE_VERSION=$STELLAR_CORE_VERSION" -f services/horizon/docker/verify-range/Dockerfile -t stellar/horizon-verify-range services/horizon/docker/verify-range/
138137
# Any range should do for basic testing, this range was chosen pretty early in history so that it only takes a few mins to run
139138
docker run -e BRANCH=$(git rev-parse HEAD) -e FROM=10000063 -e TO=10000127 stellar/horizon-verify-range
140139

services/horizon/cmd/db.go

Lines changed: 3 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -44,16 +44,14 @@ var (
4444
parallelWorkers uint
4545
retries uint
4646
retryBackoffSeconds uint
47-
ledgerBackendStr string
4847
storageBackendConfigPath string
4948
ledgerBackendType ingest.LedgerBackendType
5049
)
5150

5251
// generateLedgerBackendOpt creates a reusable ConfigOption for ledgerbackend parameter
53-
func generateLedgerBackendOpt(configKey *string, backendTypeVar *ingest.LedgerBackendType) *support.ConfigOption {
52+
func generateLedgerBackendOpt(backendTypeVar *ingest.LedgerBackendType) *support.ConfigOption {
5453
return &support.ConfigOption{
5554
Name: "ledgerbackend",
56-
ConfigKey: configKey,
5755
OptType: types.String,
5856
Required: false,
5957
FlagDefault: ingest.CaptiveCoreBackend.String(),
@@ -70,7 +68,6 @@ func generateLedgerBackendOpt(configKey *string, backendTypeVar *ingest.LedgerBa
7068
default:
7169
return fmt.Errorf("invalid ledger backend: %s, must be 'captive-core' or 'datastore'", val)
7270
}
73-
*co.ConfigKey.(*string) = val
7471
return nil
7572
},
7673
}
@@ -160,14 +157,8 @@ func ingestRangeCmdOpts() support.ConfigOptions {
160157
FlagDefault: uint(5),
161158
Usage: "[optional] backoff seconds between reingest retries",
162159
},
163-
generateLedgerBackendOpt(&ledgerBackendStr, &ledgerBackendType),
164-
{
165-
Name: "datastore-config",
166-
ConfigKey: &storageBackendConfigPath,
167-
OptType: types.String,
168-
Required: false,
169-
Usage: "[optional] Specify the path to the datastore config file (required for datastore backend)",
170-
},
160+
generateLedgerBackendOpt(&ledgerBackendType),
161+
generateDatastoreConfigOpt(&storageBackendConfigPath),
171162
}
172163
}
173164

services/horizon/cmd/ingest.go

Lines changed: 18 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,6 @@ var ingestBuildStateSequence uint32
2424
var ingestBuildStateSkipChecks bool
2525
var ingestVerifyFrom, ingestVerifyTo, ingestVerifyDebugServerPort uint32
2626
var ingestVerifyState bool
27-
var ingestVerifyLedgerBackendStr string
2827
var ingestVerifyStorageBackendConfigPath string
2928
var ingestVerifyLedgerBackendType ingest.LedgerBackendType
3029
var processVerifyRangeFn = processVerifyRange
@@ -81,14 +80,8 @@ var ingestVerifyRangeCmdOpts = support.ConfigOptions{
8180
FlagDefault: uint32(0),
8281
Usage: "[optional] opens a net/http/pprof server at given port",
8382
},
84-
generateLedgerBackendOpt(&ingestVerifyLedgerBackendStr, &ingestVerifyLedgerBackendType),
85-
{
86-
Name: "datastore-config",
87-
ConfigKey: &ingestVerifyStorageBackendConfigPath,
88-
OptType: types.String,
89-
Required: false,
90-
Usage: "[optional] Specify the path to the datastore config file (required for datastore backend)",
91-
},
83+
generateLedgerBackendOpt(&ingestVerifyLedgerBackendType),
84+
generateDatastoreConfigOpt(&ingestVerifyStorageBackendConfigPath),
9285
}
9386

9487
var stressTestNumTransactions, stressTestChangesPerTransaction int
@@ -130,10 +123,6 @@ func DefineIngestCommands(rootCmd *cobra.Command, horizonConfig *horizon.Config,
130123
return err
131124
}
132125

133-
if err := horizon.ApplyFlags(horizonConfig, horizonFlags, horizon.ApplyOptions{RequireCaptiveCoreFullConfig: false}); err != nil {
134-
return err
135-
}
136-
137126
if ingestVerifyDebugServerPort != 0 {
138127
go func() {
139128
log.Infof("Starting debug server at: %d", ingestVerifyDebugServerPort)
@@ -157,6 +146,7 @@ func DefineIngestCommands(rootCmd *cobra.Command, horizonConfig *horizon.Config,
157146
}
158147

159148
storageBackendConfig := ingest.StorageBackendConfig{}
149+
noCaptiveCore := false
160150
if ingestVerifyLedgerBackendType == ingest.BufferedStorageBackend {
161151
if ingestVerifyStorageBackendConfigPath == "" {
162152
return fmt.Errorf("datastore-config file path is required with datastore backend")
@@ -165,8 +155,12 @@ func DefineIngestCommands(rootCmd *cobra.Command, horizonConfig *horizon.Config,
165155
if storageBackendConfig, err = loadStorageBackendConfig(ingestVerifyStorageBackendConfigPath); err != nil {
166156
return err
167157
}
158+
noCaptiveCore = true
168159
}
169160

161+
if err := horizon.ApplyFlags(horizonConfig, horizonFlags, horizon.ApplyOptions{NoCaptiveCore: noCaptiveCore, RequireCaptiveCoreFullConfig: false}); err != nil {
162+
return err
163+
}
170164
err := processVerifyRangeFn(horizonConfig, horizonFlags, storageBackendConfig)
171165
if err != nil {
172166
return err
@@ -396,3 +390,14 @@ func processVerifyRange(horizonConfig *horizon.Config, horizonFlags config.Confi
396390
ingestVerifyState,
397391
)
398392
}
393+
394+
// generateDatastoreConfigOpt returns a *support.ConfigOption for the datastore-config flag
395+
func generateDatastoreConfigOpt(configKey *string) *support.ConfigOption {
396+
return &support.ConfigOption{
397+
Name: "datastore-config",
398+
ConfigKey: configKey,
399+
OptType: types.String,
400+
Required: false,
401+
Usage: "[optional] Specify the path to the datastore config file (required for datastore backend)",
402+
}
403+
}

services/horizon/docker/verify-range/README.md

Lines changed: 70 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,11 @@
22

33
This docker image allows running multiple instances of `horizon ingest verify-command` on a single machine or running it in [AWS Batch](https://aws.amazon.com/batch/).
44

5+
## Data directory
6+
The image by default stores all outputs of db, captive core, and buffered storage processes
7+
under the `/data` directory at runtime in the container. Therefore it is strongly recommended
8+
to provide an external volume mount to the container for `/data` of at least 300-500GB. Specify this at docker run time via - `docker run -v /host/volume:/data`
9+
510
## Env variables
611

712
### Running locally
@@ -14,16 +19,51 @@ This docker image allows running multiple instances of `horizon ingest verify-co
1419

1520
### Running in AWS Batch
1621

17-
| Name | Description |
18-
|----------------------|----------------------------------------------------------------------|
19-
| `BRANCH` | Git branch to build (useful for testing PRs) |
20-
| `BATCH_START_LEDGER` | First ledger of the AWS Batch Job, must be a checkpoint ledger or 1. |
21-
| `BATCH_SIZE` | Size of the batch, must be multiple of 64. |
22+
| Name | Description |
23+
|-----------------------------|----------------------------------------------------------------------|
24+
| `AWS_BATCH_JOB_ARRAY_INDEX` | The zero based index of a single batch Job. |
25+
| `BATCH_START_LEDGER` | The `FROM` ledger of the requested ledger range to verify. |
26+
| `BATCH_SIZE` | Size of the batch, must be multiple of 64. |
27+
28+
29+
### Datastore and GCP Credentials Usage
30+
31+
This image supports connecting to GCS buckets for ledger data instead of captive core. To use this feature configure the container with these additional settings:
2232

23-
#### Example
33+
#### GCP Credentials
34+
- Purpose: To access GCS buckets the image needs GCP credentials.
35+
- Two options are available to provide this to container:
36+
- As an environment variable:
37+
- Pass the GCP JSON credentials as a string in a `GCP_CREDS` environment variable:
38+
```sh
39+
docker run -e GCP_CREDS='{...}' ...
40+
```
41+
- As a volume mount:
42+
- Mount the GCP json credentials file on host to the container, e.g.:
43+
```sh
44+
docker run -v /host/path/credentials.json:/tmp/credentials.json -e GOOGLE_APPLICATION_CREDENTIALS=/tmp/credentials.json ...
45+
```
2446

25-
When you start 10 jobs with `BATCH_START_LEDGER=63` and `BATCH_SIZE=64`
26-
it will run the following ranges:
47+
#### GCS Datastore settings
48+
- Purpose: Defines the GCS bucket name and ledger partioning used on the buckets. These settings are referenced as a single toml file at runtime. Here is an example [datastore_config.toml](../../../galexie/config.example.toml)
49+
- Two options are available to provide this to container:
50+
- As an environment variable:
51+
- Pass the datastore TOML config as a string(including line breaks, tabs) in the `DATASTORE_CONFIG_PLAIN` environment variable:
52+
```sh
53+
docker run -e DATASTORE_CONFIG_PLAIN='[buffered_storage_backend_config]\nbuffer_size = 5\n ...'
54+
```
55+
- As a volume mount:
56+
- Mount the datastore toml config file from host to the container, e.g.:
57+
```sh
58+
docker run -v /host/path/datastore-config.toml:/tmp/datastore-config.toml -e DATASTORE_CONFIG=/tmp/datastore-config.toml
59+
```
60+
61+
### Examples of running container
62+
63+
#### Batch jobs example
64+
When run from aws batch, given `BATCH_START_LEDGER=63` and `BATCH_SIZE=64`
65+
it will generate runner jobs and give them each a `AWS_BATCH_JOB_ARRAY_INDEX`.
66+
The verify-range container will then generate the associated ledger ranges per each job:
2767

2868
| `AWS_BATCH_JOB_ARRAY_INDEX` | `FROM` | `TO` |
2969
|-----------------------------|--------|------|
@@ -32,13 +72,25 @@ it will run the following ranges:
3272
| 2 | 191 | 255 |
3373
| 3 | 255 | 319 |
3474

35-
## Tips when using AWS Batch
36-
37-
* In "Job definition" set vCPUs to 2 and Memory to 4096. This represents the `c5.large` instances Horizon should be using.
38-
* In "Compute environments":
39-
* Set instance type to "c5.large".
40-
* Set "Maximum vCPUs" to 2x the number of instances you want to start (because "c5.large" has 2 vCPUs). Ex. 10 vCPUs = 5 x "c5.large" instances.
41-
* Use spot instances! It's much cheaper and speed of testing will be the same in 99% of cases.
42-
* You need to publish the image if there are any changes in `Dockerfile` or one of the scripts.
43-
* When batch processing is over check if instances have been terminated. Sometimes AWS doesn't terminate them.
44-
* Make sure the job timeout is set to a larger value if you verify larger ranges. Default is just 100 seconds.
75+
#### `docker run` example
76+
Running the verify-range image as local container with `docker run`
77+
* run verify range and use captive core to get ledgers from pubnet:
78+
```
79+
docker run -e FROM=63 \
80+
-e TO=127 \
81+
-e BRANCH=<target version> \
82+
-e BASE_BRANCH=master \
83+
verify-range:latest
84+
```
85+
* run verify range with gcs datastore to use precomputed ledger metadata from buckets, captive core is not used:
86+
```
87+
docker run -e FROM=63 \
88+
-e TO=127 \
89+
-e BRANCH=<target version> \
90+
-e BASE_BRANCH=master \
91+
-v /host/path/to/gcreds.json:/tmp/gcp.json \
92+
-e GOOGLE_APPLICATION_CREDENTIALS=/tmp/gcp.json \
93+
-v /host/path/to/datastore-config.tom:/tmp/datastore-config.toml \
94+
-e DATASTORE_CONFIG=/tmp/datastore-config.toml \
95+
verify-range:latest
96+
```

0 commit comments

Comments
 (0)