diff --git a/render/Dockerfile b/render/Dockerfile index a968086f..aa3ee100 100644 --- a/render/Dockerfile +++ b/render/Dockerfile @@ -5,7 +5,7 @@ # # An argument so an unreleased build can be tried locally: # docker build --build-arg SQLFLOW_IMAGE=turbolytics/sql-flow: render -ARG SQLFLOW_IMAGE=turbolytics/sql-flow:v2026.09.19.1 +ARG SQLFLOW_IMAGE=turbolytics/sql-flow:v2026.09.21 FROM ${SQLFLOW_IMAGE} # psql and pg_isready apply the migrations. curl sends the install events. diff --git a/render/Makefile b/render/Makefile index 2358f78c..02eb3088 100644 --- a/render/Makefile +++ b/render/Makefile @@ -1,5 +1,5 @@ # Must match the Dockerfile and docker-compose.yml. -SQLFLOW_IMAGE ?= turbolytics/sql-flow:v2026.09.19.1 +SQLFLOW_IMAGE ?= turbolytics/sql-flow:v2026.09.21 .PHONY: validate rollups up test psql clean diff --git a/render/README.md b/render/README.md index c949c99d..57190a8d 100644 --- a/render/README.md +++ b/render/README.md @@ -47,13 +47,13 @@ of both: ```json {"name": "install.deployed", "type": "count", "value": 1, "dimensions": {"install_id": "3f0c1b7e-…", "source": "render", - "template": "render-metrics", "sqlflow_version": "v2026.09.19.1"}} + "template": "render-metrics", "sqlflow_version": "v2026.09.21"}} ``` ```json {"name": "install.first_request", "type": "count", "value": 1, "dimensions": {"install_id": "3f0c1b7e-…", "source": "render", - "template": "render-metrics", "sqlflow_version": "v2026.09.19.1"}} + "template": "render-metrics", "sqlflow_version": "v2026.09.21"}} ``` | Field | What it is | diff --git a/render/bin/telemetry.sh b/render/bin/telemetry.sh index 80dfb36f..7f9f96f3 100755 --- a/render/bin/telemetry.sh +++ b/render/bin/telemetry.sh @@ -31,7 +31,7 @@ TEMPLATE=render-metrics sql() { psql "$SQLFLOW_POSTGRES_URI" -X -Atq -v ON_ERROR_STOP=1 -c "$1" 2>/dev/null; } -# The first line of `sqlflow --version` is "sqlflow v2026.09.19.1". +# The first line of `sqlflow --version` is "sqlflow v2026.09.21". VERSION="$(sqlflow --version 2>/dev/null | awk 'NR==1 {print $2}')" case "$VERSION" in ''|*[!A-Za-z0-9._-]*) VERSION=unknown ;; esac diff --git a/render/docker-compose.yml b/render/docker-compose.yml index feb326b7..f7d14eb6 100644 --- a/render/docker-compose.yml +++ b/render/docker-compose.yml @@ -38,7 +38,7 @@ services: build: context: . args: - SQLFLOW_IMAGE: ${SQLFLOW_IMAGE:-turbolytics/sql-flow:v2026.09.19.1} + SQLFLOW_IMAGE: ${SQLFLOW_IMAGE:-turbolytics/sql-flow:v2026.09.21} depends_on: postgres: condition: service_healthy @@ -71,7 +71,7 @@ services: build: context: . args: - SQLFLOW_IMAGE: ${SQLFLOW_IMAGE:-turbolytics/sql-flow:v2026.09.19.1} + SQLFLOW_IMAGE: ${SQLFLOW_IMAGE:-turbolytics/sql-flow:v2026.09.21} depends_on: postgres: condition: service_healthy @@ -93,7 +93,7 @@ services: build: context: . args: - SQLFLOW_IMAGE: ${SQLFLOW_IMAGE:-turbolytics/sql-flow:v2026.09.19.1} + SQLFLOW_IMAGE: ${SQLFLOW_IMAGE:-turbolytics/sql-flow:v2026.09.21} entrypoint: /app/bin/serve.sh depends_on: postgres: diff --git a/render/pipeline.yml b/render/pipeline.yml index a3ec7870..af6c631c 100644 --- a/render/pipeline.yml +++ b/render/pipeline.yml @@ -9,6 +9,12 @@ # SQLFLOW_WEBHOOK_HMAC_SECRET required under hmac # SQLFLOW_WEBHOOK_PORT default 8001 # SQLFLOW_WEBHOOK_MAX_BODY_BYTES default 26214400, 25 MiB +# SQLFLOW_TURBOSTATS_REPORT_TO optional. A control plane's report URL, such +# as https://ingest.turbolytics.io/v1/turbostats. +# Empty, the default, reports nowhere. +# SQLFLOW_TURBOSTATS_KEY required with a report URL: the credential +# the control plane issued +# SQLFLOW_TURBOSTATS_ID default sqlflow-metrics-ingest # SQLFLOW_WRITER_ID required. bin/entrypoint.sh makes one per # process start. Never set it by hand: two # processes that share one replace each @@ -94,6 +100,16 @@ tables: pipeline: name: render-metrics + # Reports this instance to a control plane when + # SQLFLOW_TURBOSTATS_REPORT_TO is set, signed with SQLFLOW_TURBOSTATS_KEY. + # Empty, the default, reports nowhere. The values are quoted so an unset + # variable renders as an empty string, which the pipeline schema reads as + # off, and not as null, which it rejects. Needs SQLFlow v2026.09.21 or later. + turbostats: + id: "{{ SQLFLOW_TURBOSTATS_ID|default('sqlflow-metrics-ingest') }}" + report_to: "{{ SQLFLOW_TURBOSTATS_REPORT_TO|default('') }}" + key: "{{ SQLFLOW_TURBOSTATS_KEY|default('') }}" + batch_size: 500 # A lone curl is a partial batch. It waits this long, then the minute has to # close, so a first metric is readable after one to two minutes. diff --git a/render/serve.yml b/render/serve.yml index ba9aab26..3f22540d 100644 --- a/render/serve.yml +++ b/render/serve.yml @@ -7,6 +7,12 @@ # deploy asks for it. bin/serve.sh refuses one that # would need encoding in a URL. # SQLFLOW_SERVE_PORT default 8080 +# SQLFLOW_TURBOSTATS_REPORT_TO optional. A control plane's report URL, such +# as https://ingest.turbolytics.io/v1/turbostats. +# Empty, the default, reports nowhere. +# SQLFLOW_TURBOSTATS_KEY required with a report URL: the credential +# the control plane issued +# SQLFLOW_TURBOSTATS_ID default sqlflow-metrics-api # # The metric and metric_total grains are written by hand. sqlflow rollup # generates a dataset of at most one dimension, folded, and a series here is @@ -41,6 +47,16 @@ commands: serve: name: render-metrics-api + # Reports this instance to a control plane when + # SQLFLOW_TURBOSTATS_REPORT_TO is set, signed with SQLFLOW_TURBOSTATS_KEY. + # Empty, the default, reports nowhere. The values are quoted so an unset + # variable renders as an empty string, which the serve schema reads as + # off, and not as null, which it rejects. Needs SQLFlow v2026.09.21 or later. + turbostats: + id: "{{ SQLFLOW_TURBOSTATS_ID|default('sqlflow-metrics-api') }}" + report_to: "{{ SQLFLOW_TURBOSTATS_REPORT_TO|default('') }}" + key: "{{ SQLFLOW_TURBOSTATS_KEY|default('') }}" + http: addr: "0.0.0.0:{{ SQLFLOW_SERVE_PORT|default('8080') }}" diff --git a/render/test/e2e.sh b/render/test/e2e.sh index 2964f36e..6bed718b 100755 --- a/render/test/e2e.sh +++ b/render/test/e2e.sh @@ -10,7 +10,7 @@ API="http://127.0.0.1:${API_HOST_PORT:-8080}" SECRET=local-secret # The tag the image is built from, which an install reports as its version. # make test passes it. Must match the Dockerfile's default. -: "${SQLFLOW_IMAGE:=turbolytics/sql-flow:v2026.09.19.1}" +: "${SQLFLOW_IMAGE:=turbolytics/sql-flow:v2026.09.21}" export SQLFLOW_IMAGE GRAINS="1m 5m 15m 1h 6h 1d"