Readyset Is Ready for MySQL 9.7 - Three Commands With rdst

8 min read

16 days ago

Readyset Is Ready for MySQL 9.7 - Three Commands With rdst

MySQL 9.7.0 LTS landed today, and we had the pleasure of watching the announcement live — and we were the first ones to download. If you've already pulled the binaries and you're poking at it in a lab, here's the news: Readyset already supports it.

The point isn't that Readyset happens to work with 9.7. The point is that we were prepared for the launch. Our compatibility matrix has been running 9.7 pre-release builds, so the same readysettech/readyset:latest image you can docker pull right now already accepts 9.7 as an upstream. Today's job is to prove it, not to port it.

Step 1 - Register MySQL 9.7 as an rdst target

rdst stores connection profiles in ~/.rdst/config.toml. One non-interactive command adds the target, and the add-with-verify path handshakes against the server to confirm it's reachable:

$ pipx install rdst $ export MYSQL97_PASSWORD=readyset $ rdst configure add \     --target mysql97 \     --engine mysql \     --host mysql97.example.com \     --port 3306 \     --user readyset \     --database demo \     --password-env MYSQL97_PASSWORD \     --confirm

Expected output:

╭────────────────────────────── Connection Test ──────────────────────────────╮ │ ✅ Connected successfully!                                                  │ │ Server: MySQL 9.7.0                                                         │ ╰─────────────────────────────────────────────────────────────────────────────╯ ╭────────────────────── Target 'mysql97' ─────────────────────────────────────╮ │ ✅ has been added.                                                          │ ╰─────────────────────────────────────────────────────────────────────────────╯

That "Server: MySQL 9.7.0" string is the quiet proof: the rdst MySQL driver handshakes cleanly against a 9.7 GA server, reads the version, and moves on. No protocol-version surprises, no "unsupported server" warnings.

One MySQL 9.7 gotcha that bites everyone, not just Readyset. The mysql_native_password plugin is gone from 9.7 — you can't even INSTALL PLUGIN it back in, because the shared object no longer exposes the symbol the server expects. Create the Readyset user with caching_sha2_password (the default since 8.0). Readyset has supported that for years, so there's nothing to change on our side:

CREATE USER IF NOT EXISTS 'readyset'@'%'   IDENTIFIED WITH caching_sha2_password BY 'readyset'; GRANT ALL ON *.* TO 'readyset'@'%' WITH GRANT OPTION; FLUSH PRIVILEGES;

If you have service accounts still sitting on the old plugin, plan that migration into the upgrade.

Step 2 - Deploy Readyset in Docker with rdst cache deploy

This is the command that would have taken half a page in a hand-rolled guide. Now it's one line:

$ rdst cache deploy --target mysql97 --mode docker --port 3307 🚀 Deploying Readyset for target 'mysql97' (docker)...   Preparing deployment...   Deploying ReadySet (docker)... Pulling and starting ReadySet container (this may take a while)...   Registering cache target... ╭────────────────────────── Deploy Complete ──────────────────────────────────╮ │ ReadySet deployed successfully                                              │ │                                                                             │ │   ✅ Connection endpoint:                                                   │ │     mysql://[email protected]:3307/demo                                    │ │                                                                             │ │   Cache target: mysql97-cache                                        │ │   Container:    rdst-readyset-mysql97                                │ ╰─────────────────────────────────────────────────────────────────────────────╯

Under the hood, rdst pulled readysettech/readyset:latest, wired UPSTREAM_DB_URL to the target we just registered and exposed port 3307 for SQL, and registered a second target — mysql97-cache — that points at the freshly-started Readyset.

The Readyset container's startup log shows it connecting to 9.7 on the first try — no version negotiation drama:

$ docker logs rdst-readyset-mysql97 2>&1 | tail readyset: Spawning HTTP request server task readyset_adapter::http_router: Adapter listening for HTTP connections addr=0.0.0.0:6034 readyset: Query logs are enabled. Spawning query logger readyset_server::http_router: Server listening for HTTP connections addr=127.0.0.1:6033 readyset_server::controller: won leader election, creating Leader readyset_server::controller::inner: received registration payload from worker invalidation_sidecar:schema_catalog_invalidate: schema_catalog::handle:   Invalidating all cached query state due to schema change

From rdst's point of view there are now two live targets, both verified:

$ rdst configure list                          Database Targets ╭─────────────────────┬────────┬──────────────────────┬──────────┐ │ Target              │ Engine │ Connection           │ Verified │ ├─────────────────────┼────────┼──────────────────────┼──────────┤ │ mysql97         │ MySQL  │ mysql97.example.com:3306 │   Yes    │ │ mysql97-cache   │ MySQL  │ 127.0.0.1:3307/demo      │   Yes    │ ╰─────────────────────┴────────┴──────────────────────┴──────────╯
$ rdst configure test mysql97-cache ╭──────────── Connection Test: mysql97-cache ────────────╮ │ ✅ Connection successful (Server: MySQL 9.7.0)                │ ╰───────────────────────────────────────────────────────────────╯

Readyset advertises itself as MySQL 9.7.0 because that's the upstream it's fronting — the version string is passed through verbatim, so any client that inspects VERSION() or @@version sees the real thing, not some proxy-identity placeholder.

Step 3 - rdst query cache-compare against real 9.7.0 GA

Before benchmarking, I grew the orders table to ~200k rows (the seed schema is tiny on purpose, so there's nothing to cache). Then the command I actually wanted to run:

$ rdst query cache-compare \     "SELECT c.name, COUNT(o.id) AS orders, SUM(o.total) AS spent      FROM customers c      JOIN orders o ON o.customer_id = c.id      WHERE o.status = 'delivered'      GROUP BY c.id, c.name ORDER BY spent DESC" \     --target mysql97 --count 100 --skip-warning Query registered (hash: f7e239c0) Preparing cache for comparison... Running 100 iterations against upstream (mysql97)... Running 100 iterations against cache (mysql97-cache)...

Result:

MetricUpstream (MySQL 9.7.0)Cache (local Readyset)Improvement
Min50.92 ms1.11 ms45.9x
Avg63.18 ms1.56 ms40.5x
P5059.45 ms1.39 ms42.8x
P9595.84 ms2.77 ms34.6x
P99119.76 ms3.63 ms33.0x
Max119.76 ms3.63 ms33.0x

63 ms → 1.56 ms average. Upstream MySQL 9.7.0, scanning ~200k rows to build a per-customer aggregate, lands around 60–100 ms per iteration. The same query through Readyset — running as a Docker container on my laptop — lands in under 2 ms on average, with P99 at 3.63 ms (still comfortably below the upstream minimum of 50.9 ms). Every iteration after the first miss is served from the cache.

A caveat worth stating. Caching earns its keep when there's real work to skip. On a trivial point-lookup against a handful of rows, the cache overhead can outweigh the savings. That's why the demo table has 200k rows, and why the "cache beats upstream" story above is honest rather than synthetic.

Step 4 - Persist the cache and inspect it

cache-compare creates a temporary cache for the duration of the test and cleans up after itself. Make it permanent with one more command:

$ rdst cache add \     "SELECT c.name, COUNT(o.id) AS orders, SUM(o.total) AS spent      FROM customers c      JOIN orders o ON o.customer_id = c.id      WHERE o.status = 'delivered'      GROUP BY c.id, c.name ORDER BY spent DESC" \     --target mysql97-cache ╭────────────────── Cache Created ──────────────────╮ │ Shallow cache created successfully                │ │   Hash: f7e239c0ec88                              │ ╰───────────────────────────────────────────────────╯
$ rdst cache show --target mysql97-cache ╭──────────┬────────────────────┬──────────────────────────┬─────────┬──────╮ │ Hash     │ Cache Name         │ Query                    │ Type    │ TTL  │ ├──────────┼────────────────────┼──────────────────────────┼─────────┼──────┤ │ f7e239c0 │ q_fc381ca24cf972e5 │ SELECT c.name, COUNT(...)│ shallow │ 600s │ ╰──────────┴────────────────────┴──────────────────────────┴─────────┴──────╯

rdst analyze rounds it out — it queries MySQL 9.7 to look at the plan and checks Readyset for an existing cache of the same query:

$ rdst analyze -q "SELECT c.name, COUNT(o.id) AS orders, SUM(o.total) AS spent     FROM customers c JOIN orders o ON o.customer_id = c.id     WHERE o.status = 'delivered'     GROUP BY c.id, c.name ORDER BY spent DESC" --target mysql97 ╭────────────────────── 🚀 Readyset Performance ──────────────────────╮ │  Status: CACHEABLE ✓                                                │ │  Confidence: high                                                   │ │  Method: readyset_shallow                                           │ │                                                                     │ │  CACHE PERFORMANCE                                                  │ │  Cached query time: 0.60ms                                          │ │                                                                     │ │  Explanation: Query is already cached in Readyset                   │ │  (query_id: q_fc381ca24cf972e5)                                     │ ╰─────────────────────────────────────────────────────────────────────╯

The whole script, end to end

For copy-paste:

# 0. one-time - macOS pipx install rdst # 1. register MySQL 9.7 as a target export MYSQL97_PASSWORD=readyset rdst configure add --target mysql97 --engine mysql \   --host mysql97.example.com --port 3306 --user readyset --database demo \   --password-env MYSQL97_PASSWORD --confirm # 2. deploy Readyset locally in Docker, pointed at that target rdst cache deploy --target mysql97 --mode docker --port 3307 # 3. see the speedup rdst query cache-compare "<your SQL here>" --target mysql97 --count 100

Three steps from "nothing installed" to "sub-2-ms cached reads off a real 9.7.0 GA upstream."

What "ready on day one" means for you

  • The same readysettech/readyset image you docker pull today already talks to MySQL 9.7.0 GA. No custom build, no feature flag, no waiting for a compatibility release.
  • rdst cache deploy encodes the correct config (caching_sha2_password auth, shallow cache mode, metrics endpoint) so you don't hand-roll a readyset.conf.
  • You can drive it from a laptop. Your upstream can be on prem, in a VM, or in the cloud — as long as rdst configure test succeeds, rdst cache deploy will happily stand up a local Readyset pointed at it.
  • The one MySQL 9.7 operational change that matters is also the one that has nothing to do with Readyset: accounts still on mysql_native_password need to migrate to caching_sha2_password before the upgrade. Plan that into your rollout.

Every future MySQL release gets the same treatment: pre-GA builds on our compatibility runners, the Docker image updated before the announcement, and the rdst flow above working the hour the release hits. The goal is for this post to stay accurate, just with a different version number each time.