Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
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
22 changes: 21 additions & 1 deletion docs/runbooks/scenario-runner.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@

The scenario runner executes end-to-end managed-warehouse flows against a configured dev environment. The first smoke scenario provisions a warehouse, waits for readiness, runs `SELECT 1` over PGWire with managed-hostname SNI, then deprovisions and verifies cleanup.

The frozen metadata scenario provisions the same fresh dev warehouse shape, creates read-only views over frozen persons/events parquet supplied by `DUCKGRES_SCENARIO_FROZEN_S3_URI`, validates the dataset manifest row, runs lightweight metadata/user-exploration queries, then deprovisions.
The frozen metadata and perf scenarios provision the same fresh dev warehouse shape, create read-only views over frozen persons/events parquet supplied by `DUCKGRES_SCENARIO_FROZEN_S3_URI`, run their workload, then deprovision.

## Required Environment

Expand Down Expand Up @@ -36,6 +36,13 @@ Frozen dataset scenarios additionally require:
export DUCKGRES_SCENARIO_FROZEN_S3_URI="s3://<dev-managed-bucket>/frozen_v1/"
```

Frozen perf scenarios additionally require the Flight SQL address because the perf catalog exercises both PGWire and Flight:

```bash
export DUCKGRES_SCENARIO_FLIGHT_ADDR="<flight-sql-address>"
export DUCKGRES_SCENARIO_FLIGHT_INSECURE_SKIP_VERIFY="true"
```

Do not commit concrete dev endpoints, secrets, org IDs, or private bucket names.

## Run
Expand All @@ -58,6 +65,12 @@ Run frozen metadata exploration:
just scenario-frozen-metadata
```

Run frozen perf queries:

```bash
just scenario-frozen-perf
```

Run a specific scenario file:

```bash
Expand All @@ -72,6 +85,13 @@ The frozen metadata scenario uses:
- `tests/scenario/sql/setup_frozen_views.sql`
- `tests/scenario/sql/metadata_catalog.yaml`

The frozen perf scenario uses:

- `tests/scenario/scenarios/posthog_frozen_perf.yaml`
- `tests/perf/queries/ducklake_frozen.yaml`

Perf artifacts are written under `artifacts/scenario/<run_id>/perf/` using the existing `tests/perf/core` artifact schema, including `query_results.csv`, `summary.json`, and `server_metrics.prom`.

`DUCKGRES_SCENARIO_FROZEN_S3_URI` must point at a dev-owned frozen dataset prefix with `persons/` and `events/` parquet children.
The provisioned Duckgres worker role also needs read/list access to that prefix; the runner process only supplies the URI, while the worker performs the S3 reads during `read_parquet`.

Expand Down
5 changes: 5 additions & 0 deletions justfile
Original file line number Diff line number Diff line change
Expand Up @@ -379,6 +379,11 @@ scenario-smoke:
scenario-frozen-metadata:
./scripts/scenario_run.sh tests/scenario/scenarios/posthog_frozen_metadata.yaml

# Run the dev frozen dataset perf scenario
[group('test')]
scenario-frozen-perf:
./scripts/scenario_run.sh tests/scenario/scenarios/posthog_frozen_perf.yaml

# Lint (matches CI — uses golangci-lint, not go vet)
[group('test')]
lint:
Expand Down
29 changes: 26 additions & 3 deletions scripts/scenario_run.sh
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,8 @@ Optional environment:
DUCKGRES_SCENARIO_RUN_ID
DUCKGRES_SCENARIO_PG_PORT
DUCKGRES_SCENARIO_PG_CONNECT_TIMEOUT
DUCKGRES_SCENARIO_FLIGHT_ADDR (required by frozen perf scenarios)
DUCKGRES_SCENARIO_FLIGHT_INSECURE_SKIP_VERIFY
DUCKGRES_SCENARIO_MAX_RUNTIME
DUCKGRES_SCENARIO_GO_TEST_TIMEOUT
DUCKGRES_SCENARIO_FROZEN_S3_URI (required by frozen dataset scenarios)
Expand Down Expand Up @@ -75,12 +77,36 @@ root_relative_path() {
esac
}

scenario_file="$(root_relative_path "$scenario_file")"
output_base="$(root_relative_path "$output_base")"

scenario_required_env() {
awk '
/^[^[:space:]]/ { in_required = 0 }
/^required_env:[[:space:]]*$/ { in_required = 1; next }
in_required && /^[[:space:]]*-[[:space:]]*/ {
value = $0
sub(/^[[:space:]]*-[[:space:]]*/, "", value)
gsub(/^["'\'']|["'\'']$/, "", value)
if (value != "") print value
}
' "$1"
}

required=(
DUCKGRES_SCENARIO_API_BASE
DUCKGRES_SCENARIO_INTERNAL_SECRET
DUCKGRES_SCENARIO_PG_HOST
DUCKGRES_SCENARIO_SNI_SUFFIX
)
if [ -f "$scenario_file" ]; then
while IFS= read -r name; do
required+=("$name")
done < <(scenario_required_env "$scenario_file")
elif [ "$check_env_only" -eq 1 ]; then
echo "Scenario file not found: $scenario_file" >&2
exit 2
fi
missing=()
for name in "${required[@]}"; do
if [ -z "${!name:-}" ]; then
Expand All @@ -101,9 +127,6 @@ if [ "$check_env_only" -eq 1 ]; then
exit 0
fi

scenario_file="$(root_relative_path "$scenario_file")"
output_base="$(root_relative_path "$output_base")"

args=(
go test -count=1 ./tests/scenario
-timeout "$go_test_timeout"
Expand Down
32 changes: 28 additions & 4 deletions tests/perf/drivers/flight/driver.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,17 +24,33 @@ type Driver struct {
exec Executor
}

type ConnectionConfig struct {
Addr string
ServerName string
Username string
Password string
InsecureSkipVerify bool
}

func NewWithExecutor(exec Executor) *Driver {
return &Driver{exec: exec}
}

func NewFromAddress(addr, username, password string, insecureSkipVerify bool) (*Driver, error) {
tlsCfg := &tls.Config{InsecureSkipVerify: insecureSkipVerify} // test/perf env only
return NewFromConfig(ConnectionConfig{
Addr: addr,
Username: username,
Password: password,
InsecureSkipVerify: insecureSkipVerify,
})
}

func NewFromConfig(cfg ConnectionConfig) (*Driver, error) {
client, err := flightsql.NewClient(
addr,
cfg.Addr,
nil,
nil,
grpc.WithTransportCredentials(credentials.NewTLS(tlsCfg)),
grpc.WithTransportCredentials(credentials.NewTLS(tlsConfigForConnection(cfg))),
grpc.WithDefaultCallOptions(
grpc.MaxCallRecvMsgSize(flightclient.MaxGRPCMessageSize),
grpc.MaxCallSendMsgSize(flightclient.MaxGRPCMessageSize),
Expand All @@ -44,7 +60,7 @@ func NewFromAddress(addr, username, password string, insecureSkipVerify bool) (*
return nil, fmt.Errorf("create Flight SQL client: %w", err)
}

token, err := bootstrapSessionToken(client, username, password)
token, err := bootstrapSessionToken(client, cfg.Username, cfg.Password)
if err != nil {
_ = client.Close()
return nil, err
Expand All @@ -58,6 +74,14 @@ func NewFromAddress(addr, username, password string, insecureSkipVerify bool) (*
}, nil
}

func tlsConfigForConnection(cfg ConnectionConfig) *tls.Config {
tlsCfg := &tls.Config{InsecureSkipVerify: cfg.InsecureSkipVerify} // test/perf env only
if cfg.ServerName != "" {
tlsCfg.ServerName = cfg.ServerName
}
return tlsCfg
}

func (d *Driver) Protocol() core.Protocol {
return core.ProtocolFlight
}
Expand Down
13 changes: 13 additions & 0 deletions tests/perf/drivers/flight/driver_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -34,3 +34,16 @@ func TestDriverUsesDuckhogVariant(t *testing.T) {
t.Fatalf("expected duckhog SQL, got %q", exec.lastQuery)
}
}

func TestTLSConfigUsesExplicitServerName(t *testing.T) {
cfg := tlsConfigForConnection(ConnectionConfig{
ServerName: "scenario-org.dev.example",
InsecureSkipVerify: true,
})
if cfg.ServerName != "scenario-org.dev.example" {
t.Fatalf("server name = %q, want explicit managed hostname", cfg.ServerName)
}
if !cfg.InsecureSkipVerify {
t.Fatal("expected insecure skip verify to pass through")
}
}
Loading
Loading