From 13fafff9824d187a6d1ab546f7a7963f9de277f4 Mon Sep 17 00:00:00 2001 From: "turbolytics.io" Date: Mon, 21 Sep 2026 20:47:32 -0400 Subject: [PATCH 1/2] render: move the template's engine to SQLFlow v2026.09.21 From v2026.09.19.1. v2026.09.21 carries the TurboStats reporter, so an install can report itself to a control plane once its configs carry a turbostats block; the configs do not change here, and reporting stays off. Six references carry the tag and move together, or the template builds one engine and tests another: the Dockerfile's ARG, the Makefile, three compose build args and the e2e's default. The README's two sample telemetry payloads and the version comment in bin/telemetry.sh name it too. Evidence: make -C render validate passes, and make -C render test passes against the new image, 88 checks. It ran as its own compose project on spare ports, because another stack of this template held the default ones. If the bump is wrong, a Deploy to Render install fails its health check on first deploy. --- render/Dockerfile | 2 +- render/Makefile | 2 +- render/README.md | 4 ++-- render/bin/telemetry.sh | 2 +- render/docker-compose.yml | 6 +++--- render/test/e2e.sh | 2 +- 6 files changed, 9 insertions(+), 9 deletions(-) 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/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" From 88fab8f3fc52e60bc632644a7ab5c8c62879103f Mon Sep 17 00:00:00 2001 From: "turbolytics.io" Date: Mon, 21 Sep 2026 21:11:38 -0400 Subject: [PATCH 2/2] render: give the template's configs a turbostats block, off unless set v2026.09.21 can report an install to a control plane, but only from a turbostats block in the config; an environment variable alone turns nothing on. The template had no block, so no install of it could report. pipeline.yml and serve.yml gain one, read from SQLFLOW_TURBOSTATS_REPORT_TO, SQLFLOW_TURBOSTATS_KEY and SQLFLOW_TURBOSTATS_ID. report_to defaults to empty, which the engine reads as reporting nowhere, and skips the key and id checks. An install that sets nothing runs exactly as before, and render.yaml does not declare the variables, so a Deploy to Render user is never asked about a control plane. The values are quoted. Unquoted, an unset variable renders as null, which the pipeline schema accepts and the serve schema rejects ("got null, want string"). The block needs v2026.09.21: v2026.09.19.1 rejects it as an additional property, which is why it ships with the engine bump. Evidence: make -C render validate passes; with the variables set, both configs check the key, which proves the block is read; make -C render test passes, 88 checks, with reporting off. If this is wrong, an install fails validation at startup, or reports where nobody asked it to. --- render/pipeline.yml | 16 ++++++++++++++++ render/serve.yml | 16 ++++++++++++++++ 2 files changed, 32 insertions(+) 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') }}"