Skip to content

Commit 18909a2

Browse files
committed
test: improved stress test command
1 parent b4b9e5d commit 18909a2

File tree

1 file changed

+37
-16
lines changed

1 file changed

+37
-16
lines changed

.claude/commands/stress-test-sync-sqlitecloud.md

Lines changed: 37 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -106,11 +106,16 @@ Create a bash script at `/tmp/stress_test_concurrent.sh` that:
106106
2. **Defines a worker function** that runs in a subshell for each database:
107107
- Each worker logs all output to `/tmp/sync_concurrent_<N>.log`
108108
- Each iteration does:
109-
a. **DELETE all rows**`cloudsync_network_sync(100, 10)`
110-
b. **INSERT <ROWS> rows** (in a single BEGIN/COMMIT transaction) → `cloudsync_network_sync(100, 10)`
111-
c. **UPDATE all rows**`cloudsync_network_sync(100, 10)`
112-
- Each session must: `.load` the extension, call `cloudsync_network_init()`, `cloudsync_network_set_token()` (if RLS), do the work, call `cloudsync_terminate()`
113-
- Include labeled output lines like `[DB<N>][iter <I>] deleted/inserted/updated, count=<C>` for grep-ability
109+
a. **UPDATE all/some rows** (e.g., `UPDATE <table> SET value = value + 1;`)
110+
b. **DELETE a few rows** (e.g., `DELETE FROM <table> WHERE rowid IN (SELECT rowid FROM <table> ORDER BY RANDOM() LIMIT 10);`)
111+
c. **Sync using the 3-step send/check/check pattern:**
112+
1. `SELECT cloudsync_network_send_changes();` — send local changes to the server
113+
2. `SELECT cloudsync_network_check_changes();` — ask the server to prepare a payload of remote changes
114+
3. Sleep 1 second (outside sqlite3, between two separate sqlite3 invocations)
115+
4. `SELECT cloudsync_network_check_changes();` — download the prepared payload, if any
116+
- Each sqlite3 session must: `.load` the extension, call `cloudsync_network_init()`/`cloudsync_network_init_custom()`, `cloudsync_network_set_apikey()`/`cloudsync_network_set_token()` (depending on RLS mode), do the work, call `cloudsync_terminate()`
117+
- **Timing**: Log the wall-clock execution time (in milliseconds) for each `cloudsync_network_send_changes()`, `cloudsync_network_check_changes()` call. Use bash `date +%s%3N` before and after each sqlite3 invocation that calls a network function, and compute the delta. Log lines like: `[DB<N>][iter <I>] send_changes: 123ms`, `[DB<N>][iter <I>] check_changes_1: 45ms`, `[DB<N>][iter <I>] check_changes_2: 67ms`
118+
- Include labeled output lines like `[DB<N>][iter <I>] updated count=<C>, deleted count=<D>` for grep-ability
114119

115120
3. **Launches all workers in parallel** using `&` and collects PIDs
116121

@@ -124,9 +129,11 @@ Create a bash script at `/tmp/stress_test_concurrent.sh` that:
124129
6. **Prints final verdict**: PASS (0 errors) or FAIL (errors detected)
125130

126131
**Important script details:**
127-
- Use `echo -e` to pipe generated INSERT SQL (with `\n` separators) into sqlite3
128-
- Row IDs should be unique across databases and iterations: `db<N>_r<I>_<J>`
132+
- Use `echo -e` to pipe generated SQL (with `\n` separators) into sqlite3
133+
- During database initialization (Step 1), insert `ROWS` initial rows per database in a single transaction so each DB starts with data to update/delete. Row IDs should be unique across databases: `db<N>_r<J>`
129134
- User IDs for rows must match the token's userId for RLS to work
135+
- The sync pattern requires **separate sqlite3 invocations** for send_changes and each check_changes call (with a 1-second sleep between the two check_changes calls), so that timing can be measured per-call from bash
136+
- **stderr capture**: All sqlite3 invocations must redirect both stdout and stderr to the log file. Use `>> "$LOG" 2>&1` (in this order — stdout redirect first, then stderr to stdout). For timed calls that capture output in a variable, redirect stderr to the log file separately: `RESULT=$(echo -e "$SQL" | $SQLITE3 "$DB" 2>> "$LOG")` and then echo `$RESULT` to the log as well. This ensures "Runtime error" messages from sqlite3 are never lost.
130137
- Use `/bin/bash` (not `/bin/sh`) for arrays and process management
131138

132139
Run the script with a 10-minute timeout.
@@ -140,13 +147,25 @@ After the test completes, provide a detailed breakdown:
140147
3. **Timeline analysis**: do errors cluster at specific iterations or spread evenly?
141148
4. **Read full log files** if errors are found — show the first and last 30 lines of each log with errors
142149

143-
### Step 7: Optional — Verify Data Integrity
150+
### Step 7: Final Sync and Data Integrity Verification
144151

145-
If the test passes (or even if some errors occurred), verify the final state:
152+
After all workers have terminated, perform a **final sync on every local database** to ensure all databases converge to the same state. Then verify data integrity.
146153

147-
1. Check each local SQLite database for row count
148-
2. Check SQLiteCloud (as admin) for total row count
149-
3. If RLS is enabled, verify no cross-user data leakage
154+
1. **Final sync loop** (max 10 retries): Repeat the following until all local databases have the same row count, or the retry limit is reached:
155+
a. For each local database (sequentially):
156+
- Load the extension, call `cloudsync_network_init`/`cloudsync_network_init_custom`, authenticate with `cloudsync_network_set_apikey`/`cloudsync_network_set_token`
157+
- Run `SELECT cloudsync_network_sync(100, 10);` to sync remaining changes
158+
- Call `cloudsync_terminate()`
159+
b. After syncing all databases, query `SELECT COUNT(*) FROM <table>` on each database
160+
c. If all row counts are identical, convergence is achieved — break out of the loop
161+
d. Otherwise, log the round number and the distinct row counts, then repeat from (a)
162+
e. If the retry limit is reached without convergence, report it as a failure
163+
164+
2. **Row count verification**: Report the final row counts. All databases should have the same number of rows. Also check SQLiteCloud (as admin) for total row count.
165+
166+
3. **Row content verification**: Pick one random row ID from the first database (`SELECT id FROM <table> ORDER BY RANDOM() LIMIT 1;`). Then query that same row (`SELECT id, user_id, name, value FROM <table> WHERE id = '<random_id>';`) on **every** local database. Compare the results — all databases must return identical column values for that row. Report the row ID, the expected values, and any mismatches.
167+
168+
4. If RLS is enabled, verify no cross-user data leakage.
150169

151170
## Output Format
152171

@@ -157,8 +176,8 @@ Report the test results including:
157176
| Concurrent databases | N |
158177
| Rows per iteration | ROWS |
159178
| Iterations per database | ITERATIONS |
160-
| Total CRUD operations | N × ITERATIONS × (DELETE_ALL + ROWS inserts + ROWS updates) |
161-
| Total sync operations | N × ITERATIONS × 6 (3 sends + 3 checks) |
179+
| Total CRUD operations | N × ITERATIONS × (UPDATE_ALL + DELETE_FEW) |
180+
| Total sync operations | N × ITERATIONS × 3 (1 send_changes + 2 check_changes) |
162181
| Duration | start to finish time |
163182
| Total errors | count |
164183
| Error types | categorized list |
@@ -175,13 +194,15 @@ If errors are found, include:
175194
The test **PASSES** if:
176195
1. All workers complete all iterations
177196
2. Zero `error`, `locked`, `SQLITE_BUSY`, or HTTP 500 responses in any log
178-
3. Final row counts are consistent
197+
3. After the final sync, all local databases have the same row count
198+
4. A randomly selected row has identical content across all local databases
179199

180200
The test **FAILS** if:
181201
1. Any worker crashes or fails to complete
182202
2. Any `database is locked` or `SQLITE_BUSY` errors appear
183203
3. Server returns 500 errors under concurrent load
184-
4. Data corruption or inconsistent row counts
204+
4. Row counts differ across local databases after the final sync loop exhausts all retries
205+
5. Row content differs across local databases (data corruption)
185206

186207
## Important Notes
187208

0 commit comments

Comments
 (0)