mirror of
https://github.com/HeyPuter/puter.git
synced 2026-10-09 21:31:40 +00:00
docs: drop the replica-lag testing runbook from the open-source tree
It describes our local two-server MySQL replication setup, which is internal tooling rather than anything a self-hoster or contributor needs. It now lives in the heyputer repo alongside the other internal docs. The suite it documented stays: the header comment now states the env-var gate directly instead of pointing at a file this repo no longer carries.
This commit is contained in:
1 parent
79e496e79d
commit
d6b7c8ff52
2 files changed
+2
-109
No files matched your search
@@ -1,107 +0,0 @@
|
||||
# Testing against a lagging read replica
|
||||
|
||||
Some bugs only exist between a primary and a replica that has not caught up:
|
||||
a row is deleted on the primary, a reader is served the old copy by the
|
||||
replica, and writes referencing it then fail their foreign keys.
|
||||
|
||||
Local development runs on sqlite, which has neither a replica nor foreign-key
|
||||
enforcement, so neither half of that can happen. Unit tests inject the driver
|
||||
error instead. This setup runs the real thing: two MySQL servers in actual
|
||||
replication, where `STOP REPLICA` freezes the follower on demand.
|
||||
|
||||
## Setup
|
||||
|
||||
Assumes the usual local `puter-mysql` container (mysql:8, port 3306,
|
||||
`root`/`puter`). Start a follower on 3307:
|
||||
|
||||
```bash
|
||||
docker run -d --name puter-mysql-replica \
|
||||
-e MYSQL_ROOT_PASSWORD=puter -e MYSQL_DATABASE=puter \
|
||||
-p 3307:3306 mysql:8 \
|
||||
--server-id=2 --log-bin=mysql-bin --relay-log=relay-bin
|
||||
|
||||
until docker exec puter-mysql-replica mysqladmin -uroot -pputer ping >/dev/null 2>&1
|
||||
do sleep 2; done
|
||||
```
|
||||
|
||||
Give the primary a replication user:
|
||||
|
||||
```bash
|
||||
docker exec puter-mysql mysql -uroot -pputer -e "
|
||||
CREATE USER IF NOT EXISTS 'repl'@'%' IDENTIFIED BY 'replpass';
|
||||
GRANT REPLICATION SLAVE ON *.* TO 'repl'@'%';
|
||||
FLUSH PRIVILEGES;"
|
||||
```
|
||||
|
||||
Seed the follower and point it at the primary. `--source-data=2` records the
|
||||
binlog coordinates the dump was taken at, which is where the follower starts:
|
||||
|
||||
```bash
|
||||
docker exec puter-mysql mysqldump -uroot -pputer \
|
||||
--source-data=2 --single-transaction --databases puter > /tmp/primary.sql
|
||||
|
||||
grep -m1 'CHANGE REPLICATION SOURCE' /tmp/primary.sql
|
||||
# -- CHANGE REPLICATION SOURCE TO SOURCE_LOG_FILE='binlog.000006', SOURCE_LOG_POS=717382;
|
||||
|
||||
docker exec -i puter-mysql-replica mysql -uroot -pputer < /tmp/primary.sql
|
||||
|
||||
PRIMARY_IP=$(docker inspect puter-mysql \
|
||||
--format '{{.NetworkSettings.Networks.bridge.IPAddress}}')
|
||||
|
||||
docker exec puter-mysql-replica mysql -uroot -pputer -e "
|
||||
STOP REPLICA; RESET REPLICA ALL;
|
||||
CHANGE REPLICATION SOURCE TO
|
||||
SOURCE_HOST='${PRIMARY_IP}', SOURCE_PORT=3306,
|
||||
SOURCE_USER='repl', SOURCE_PASSWORD='replpass',
|
||||
SOURCE_LOG_FILE='<file from above>', SOURCE_LOG_POS=<pos from above>,
|
||||
GET_SOURCE_PUBLIC_KEY=1;
|
||||
START REPLICA;"
|
||||
```
|
||||
|
||||
`GET_SOURCE_PUBLIC_KEY=1` is required because MySQL 8 defaults to
|
||||
`caching_sha2_password` and this link has no TLS. Confirm both threads are up:
|
||||
|
||||
```bash
|
||||
docker exec puter-mysql-replica mysql -uroot -pputer -e "SHOW REPLICA STATUS\G" \
|
||||
| grep -E 'Replica_IO_Running|Replica_SQL_Running:|Last_Error'
|
||||
```
|
||||
|
||||
## Running
|
||||
|
||||
```bash
|
||||
PUTER_TEST_REPLICA_LAG=1 npx vitest run \
|
||||
--config src/backend/vitest.config.ts \
|
||||
src/backend/stores/replicaLag.integration.test.ts
|
||||
```
|
||||
|
||||
Without the env var the suite skips, so it stays inert in CI and for anyone
|
||||
without the containers. It builds its own throwaway database
|
||||
(`puter_replica_lag_verify`), runs the MySQL migrations into it, and drops it
|
||||
afterwards — your dev data is never touched.
|
||||
|
||||
Container names are overridable via `PUTER_TEST_REPLICA_PRIMARY` and
|
||||
`PUTER_TEST_REPLICA_FOLLOWER`.
|
||||
|
||||
## Writing a case
|
||||
|
||||
`freeze()` stops replication; everything after it exists only on the primary.
|
||||
An `afterEach` thaws unconditionally, so a failing assertion cannot strand the
|
||||
follower and starve later cases of their fixture rows.
|
||||
|
||||
```ts
|
||||
const app = await makeApp(server, user.id);
|
||||
await settle(); // let the follower catch up
|
||||
await server.stores.app.getByUid(app.uid); // warm the cache
|
||||
|
||||
freeze(); // follower is now behind
|
||||
await server.stores.app.delete(app.id); // primary only
|
||||
|
||||
expect(await server.stores.app.getByUid(app.uid)).toBeNull();
|
||||
```
|
||||
|
||||
## Teardown
|
||||
|
||||
```bash
|
||||
docker rm -f puter-mysql-replica
|
||||
docker exec puter-mysql mysql -uroot -pputer -e "DROP USER IF EXISTS 'repl'@'%';"
|
||||
```
|
||||
@@ -25,8 +25,8 @@
|
||||
* unit tests inject them, since sqlite has neither a replica nor FK
|
||||
* enforcement.
|
||||
*
|
||||
* Needs both containers, so it is opt-in and skipped in CI — setup and usage in
|
||||
* doc/testing-replica-lag.md.
|
||||
* Needs both containers, so it is opt-in behind `PUTER_TEST_REPLICA_LAG` and
|
||||
* skipped everywhere it is unset, CI included.
|
||||
*/
|
||||
|
||||
import { execFileSync } from 'node:child_process';
|
||||
|
||||
Reference in new issue
Block a user