Skip to content

Commit 5bf267f

Browse files
Round 66: savepoint, ATTACH, site_id collision test suites
- TASK-175: Savepoint sync tests (15/16 pass, 1 divergence → TASK-181) - TASK-176: ATTACH database CRR tests (15/15 pass, full parity) - TASK-180: Site ID collision tests (13/13 pass, behavior documented) New test files: - zig/harness/test-savepoint-sync.sh - zig/harness/test-attach-crr.sh - zig/harness/test-site-id-collision.sh Discovered bug: Zig db_version not advancing with savepoint + crsql_changes (tracked in TASK-181)
1 parent aec19f4 commit 5bf267f

12 files changed

+3243
-140
lines changed

.tasks/DELEGATE_WORK_HANDOFF.md

Lines changed: 134 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -68,6 +68,140 @@ Artifacts:
6868

6969
---
7070

71+
## Round 2025-12-23 (66) — Savepoint, ATTACH, site_id collision test suites (3 tasks)
72+
73+
**Tasks executed**
74+
- `.tasks/done/TASK-175-savepoint-during-sync.md`
75+
- `.tasks/done/TASK-176-attach-database-crr.md`
76+
- `.tasks/done/TASK-180-site-id-collision.md`
77+
78+
**Commits**
79+
- (pending commit)
80+
81+
**Environment**
82+
- OS: darwin (macOS ARM64)
83+
- Tooling: nix, zig (via nix), bash
84+
85+
**Commands run (exact)**
86+
```bash
87+
bash zig/harness/test-savepoint-sync.sh
88+
bash zig/harness/test-attach-crr.sh
89+
bash zig/harness/test-site-id-collision.sh
90+
```
91+
92+
**Outputs (paste)**
93+
94+
<details>
95+
<summary>TASK-175: Savepoint sync tests (15/16 pass, 1 divergence)</summary>
96+
97+
```text
98+
==================================================================
99+
SAVEPOINT SYNC TEST SUMMARY
100+
==================================================================
101+
PASSED: 15
102+
FAILED: 1
103+
SKIPPED: 0
104+
DIVERGENCES: 1
105+
==================================================================
106+
107+
WARNING: 1 divergence(s) between Zig and Rust/C oracle
108+
```
109+
110+
**Tests Created (8 scenarios):**
111+
1. Basic Savepoint with Rollback — PASS
112+
2. Nested Savepoints (sp1 -> sp2) — PASS
113+
3. RELEASE SAVEPOINT (keeps changes) — PASS
114+
4. Multiple Savepoints with Partial Rollback — PASS
115+
5. rows_impacted After Partial Rollback — PASS
116+
6. Clock Entries After Savepoint Rollback — PASS
117+
7. db_version After Savepoint Rollback — **FAIL (divergence)**
118+
8. Rollback to Savepoint Then Add More Data — PASS
119+
120+
**Divergence found:** When changes are applied via `crsql_changes` within a transaction that uses savepoints, the Zig implementation fails to advance db_version after COMMIT.
121+
- Rust/C: db_version advances 0 -> 1
122+
- Zig: db_version stays at 0 (BUG)
123+
124+
**Follow-up task created:** `.tasks/triage/TASK-181-zig-dbversion-savepoint-bug.md`
125+
</details>
126+
127+
<details>
128+
<summary>TASK-176: ATTACH CRR tests (15/15 pass)</summary>
129+
130+
```text
131+
╔═══════════════════════════════════════════════════════════════════════╗
132+
║ ATTACH CRR TEST SUMMARY ║
133+
╠═══════════════════════════════════════════════════════════════════════╣
134+
║ PASSED: 15 ║
135+
║ FAILED: 0 ║
136+
║ SKIPPED: 0 ║
137+
║ DIVERGENCES: 0 ║
138+
╚═══════════════════════════════════════════════════════════════════════╝
139+
140+
All implemented ATTACH CRR tests PASSED
141+
```
142+
143+
**Tests Created (10 scenarios):**
144+
1. Create main.db and other.db with CRR tables
145+
2. ATTACH other.db and query attached tables
146+
3. Query crsql_changes from attached database
147+
4. Verify site_id is per-database (not per-connection)
148+
5. site_id scope when querying through ATTACH
149+
6. Sync from other.db to main.db using crsql_changes
150+
7. Verify main.db has complete merged data
151+
8. Cross-database INSERT through ATTACH
152+
9. DETACH and verify data persistence
153+
10. db_version tracking with attached databases
154+
155+
**Key Findings:**
156+
- crsql_changes from attached DB works fully
157+
- site_id is per-database (not per-connection)
158+
- Cross-database sync is fully supported
159+
</details>
160+
161+
<details>
162+
<summary>TASK-180: Site ID collision tests (13/13 pass)</summary>
163+
164+
```text
165+
Site ID Collision Test Summary: 13 passed, 0 failed, 0 skipped
166+
Behavior documented: See BEHAVIORAL OBSERVATIONS above
167+
```
168+
169+
**Tests Created (7 scenarios):**
170+
1. Basic Setup — copying preserves site_id
171+
2. Independent Changes — both copies make different changes
172+
3. Sync Between Colliding Site IDs
173+
4. Bidirectional Sync
174+
5. Concurrent Inserts (Same PK)
175+
6. Delete/Resurrection with collision
176+
7. Zig vs Rust/C Parity — full collision scenario
177+
178+
**Behavioral Documentation:**
179+
- cr-sqlite does NOT detect or reject same-site_id changes
180+
- Changes merge using normal CRDT rules
181+
- Convergence uses col_version comparison + value tie-breaker
182+
- Risk: col_version collisions cause unpredictable tie-breaking
183+
- Zig and Rust/C behave identically under site_id collision
184+
</details>
185+
186+
**Files created**
187+
- `zig/harness/test-savepoint-sync.sh` (new, ~38KB)
188+
- `zig/harness/test-attach-crr.sh` (new, ~32KB)
189+
- `zig/harness/test-site-id-collision.sh` (new, ~42KB)
190+
191+
**Reproduction steps (clean checkout)**
192+
1. `git clone <repo> && cd cr-sqlite`
193+
2. `make -C zig build` — build Zig extension
194+
3. `bash zig/harness/test-savepoint-sync.sh` — verify 15/16 pass
195+
4. `bash zig/harness/test-attach-crr.sh` — verify 15/15 pass
196+
5. `bash zig/harness/test-site-id-collision.sh` — verify 13/13 pass
197+
198+
**Known gaps / unverified claims**
199+
- **1 divergence discovered**: db_version not advancing with savepoint + crsql_changes (tracked in TASK-181)
200+
- CI integration not verified (local runs only)
201+
- No coverage captured
202+
203+
---
204+
71205
## Round 2025-12-22 (63) — Fix 3 critical divergences from Round 62 (3 tasks)
72206

73207
**Tasks executed**
Lines changed: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,78 @@
1+
# TASK-175 — Test savepoints during sync operations
2+
3+
## Goal
4+
Verify Zig handles savepoints correctly during sync.
5+
6+
## Status
7+
- State: done
8+
- Priority: medium (transaction correctness)
9+
10+
## Context
11+
Apps may use savepoints for partial rollback:
12+
```sql
13+
BEGIN;
14+
INSERT INTO crsql_changes ...;
15+
SAVEPOINT sp1;
16+
INSERT INTO crsql_changes ...;
17+
ROLLBACK TO sp1;
18+
COMMIT;
19+
```
20+
21+
Questions:
22+
1. Does rollback to savepoint undo clock entries?
23+
2. Is db_version correct after partial rollback?
24+
3. Is rows_impacted correct?
25+
26+
## Files to Modify
27+
- `zig/harness/test-savepoint-sync.sh` (new)
28+
29+
## Acceptance Criteria
30+
1. BEGIN transaction
31+
2. Apply some changes via crsql_changes
32+
3. SAVEPOINT sp1
33+
4. Apply more changes
34+
5. ROLLBACK TO sp1
35+
6. COMMIT
36+
7. Verify: only pre-savepoint changes applied
37+
8. Verify: clock entries correct
38+
9. Verify: db_version reflects only committed changes
39+
10. Zig and Rust/C oracle produce identical results
40+
41+
## Parent Docs / Cross-links
42+
- Related: TASK-174 (partial sync)
43+
- Gap backlog: `research/zig-cr/92-gap-backlog.md`
44+
45+
## Progress Log
46+
- 2025-12-22: Created from gap analysis.
47+
- 2025-12-23: Created test suite `zig/harness/test-savepoint-sync.sh` with 8 test cases.
48+
49+
## Completion Notes
50+
### Test Suite Created: `zig/harness/test-savepoint-sync.sh`
51+
52+
**Test Cases Implemented:**
53+
1. Basic Savepoint with Rollback - PASS (Zig + Rust)
54+
2. Nested Savepoints (sp1 -> sp2) - PASS (Zig + Rust)
55+
3. RELEASE SAVEPOINT (keeps changes) - PASS (Zig + Rust)
56+
4. Multiple Savepoints with Partial Rollback - PASS (Zig + Rust)
57+
5. rows_impacted After Partial Rollback - PASS (Zig + Rust)
58+
6. Clock Entries After Savepoint Rollback - PASS (Zig + Rust)
59+
7. db_version After Savepoint Rollback - **DIVERGENCE** (Zig FAIL, Rust PASS)
60+
8. Rollback to Savepoint Then Add More Data - PASS (Zig + Rust)
61+
62+
**Summary: 15 passed, 1 failed, 0 skipped, 1 divergence**
63+
64+
### Divergence Found (Test 7)
65+
The Zig implementation has a bug in db_version handling during savepoint operations:
66+
- **Rust/C**: db_version correctly advances from 0 -> 1 after COMMIT
67+
- **Zig**: db_version stays at 0 after COMMIT
68+
69+
This is a real bug that should be filed as a follow-up task. The db_version should advance
70+
when changes are committed, even when using savepoints.
71+
72+
### Answers to Original Questions:
73+
1. **Does rollback to savepoint undo clock entries?** YES - verified in Test 6
74+
2. **Is db_version correct after partial rollback?** NO (Zig bug) - db_version doesn't advance
75+
3. **Is rows_impacted correct?** YES - verified in Test 5 (both impls return 3 after rollback, 0 after commit)
76+
77+
### Follow-up Required
78+
File new task for Zig db_version bug when using savepoints with crsql_changes inserts.
Lines changed: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,78 @@
1+
# TASK-176 — Test ATTACH database with CRR tables
2+
3+
## Goal
4+
Verify Zig handles attached databases with CRR tables.
5+
6+
## Status
7+
- State: done
8+
- Priority: medium (multi-database patterns)
9+
10+
## Context
11+
SQLite supports attaching multiple databases:
12+
```sql
13+
ATTACH 'other.db' AS other;
14+
SELECT * FROM other.crsql_changes;
15+
```
16+
17+
Questions:
18+
1. Can you query crsql_changes from attached DB?
19+
2. Is site_id scoped per-database or per-connection?
20+
3. Can you sync between main and attached CRRs?
21+
22+
## Files to Modify
23+
- `zig/harness/test-attach-crr.sh` (new)
24+
25+
## Acceptance Criteria
26+
1. Create main.db with CRR table
27+
2. Create other.db with CRR table
28+
3. ATTACH other.db
29+
4. Query other.crsql_changes
30+
5. Verify: changes are scoped to other.db
31+
6. Verify: site_id is per-database
32+
7. Sync from other to main via crsql_changes
33+
8. Zig and Rust/C oracle produce identical results
34+
35+
## Parent Docs / Cross-links
36+
- Gap backlog: `research/zig-cr/92-gap-backlog.md`
37+
38+
## Progress Log
39+
- 2025-12-22: Created from gap analysis.
40+
- 2025-12-23: Implemented test suite `zig/harness/test-attach-crr.sh`.
41+
42+
## Completion Notes
43+
**Date:** 2025-12-23
44+
45+
**Test Suite Created:** `zig/harness/test-attach-crr.sh`
46+
47+
**Test Results:** All 15 tests PASSED, 0 failed, 0 skipped, 0 divergences
48+
49+
**Findings (answers to original questions):**
50+
51+
1. **Can you query crsql_changes from attached DB?**
52+
- YES. `SELECT * FROM other.crsql_changes` works when `other.db` is attached.
53+
- Both Zig and Rust/C implementations support this.
54+
55+
2. **Is site_id scoped per-database or per-connection?**
56+
- **Per-database.** Each database file has its own persistent site_id.
57+
- `crsql_site_id()` returns the main connection's DB site_id even when attached DBs are present.
58+
- site_id is stable across connections to the same database file.
59+
60+
3. **Can you sync between main and attached CRRs?**
61+
- YES. Changes can be exported from one DB via `crsql_changes` and applied to another via `INSERT INTO crsql_changes`.
62+
- Cross-database INSERT through ATTACH also works (`INSERT INTO other.items ...`).
63+
64+
**Test Coverage:**
65+
1. Create main.db and other.db with CRR tables ✓
66+
2. ATTACH other.db and query attached tables ✓
67+
3. Query other.crsql_changes from attached database ✓
68+
4. Verify site_id is per-database (different site_ids for different DBs) ✓
69+
5. site_id stable across connections to same DB ✓
70+
6. site_id scope when querying through ATTACH ✓
71+
7. Sync from other.db to main.db using crsql_changes ✓
72+
8. Verify main.db has complete merged data (4 items) ✓
73+
9. Cross-database INSERT through ATTACH ✓
74+
10. Cross-db INSERT persistence ✓
75+
11. DETACH and verify data persistence ✓
76+
12. db_version tracking with attached databases ✓
77+
78+
**Oracle Parity:** Full parity confirmed between Zig and Rust/C implementations on all tests.
Lines changed: 81 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,81 @@
1+
# TASK-180 — Test site_id collision handling
2+
3+
## Goal
4+
Document behavior when two databases have the same site_id.
5+
6+
## Status
7+
- State: done
8+
- Priority: medium (edge case, security)
9+
10+
## Context
11+
What happens if:
12+
1. You copy a database file (both have same site_id)
13+
2. Both copies make changes
14+
3. You try to sync them
15+
16+
This could happen accidentally (backup restored) or maliciously.
17+
18+
## Files to Modify
19+
- `zig/harness/test-site-id-collision.sh` (new)
20+
21+
## Acceptance Criteria
22+
1. Create database with CRR, insert data ✅
23+
2. Copy database file (now two DBs with same site_id) ✅
24+
3. Make different changes in each copy ✅
25+
4. Attempt to sync between them ✅
26+
5. Document behavior: ✅
27+
- Does it detect collision? NO - cr-sqlite does not detect same-site_id
28+
- Does it error or corrupt? Neither - applies normal CRDT merge rules
29+
- What's the recommended recovery? Regenerate site_id on one copy
30+
6. Zig and Rust/C oracle produce identical results ✅
31+
32+
## Parent Docs / Cross-links
33+
- Gap backlog: `research/zig-cr/92-gap-backlog.md`
34+
35+
## Progress Log
36+
- 2025-12-22: Created from gap analysis.
37+
- 2025-12-23: Implemented test suite with 7 test scenarios.
38+
39+
## Completion Notes
40+
- Date: 2025-12-23
41+
- Created `zig/harness/test-site-id-collision.sh` with comprehensive test suite
42+
43+
### Test Results Summary
44+
- **13 passed, 0 failed, 0 skipped, 0 divergences**
45+
46+
### Documented Behavior
47+
When two databases have the same site_id (e.g., from copying a database file):
48+
49+
1. **Detection**: cr-sqlite does NOT detect or reject same-site_id changes
50+
2. **Merging**: Changes are applied using normal CRDT merge rules
51+
3. **Convergence**: Both copies converge using:
52+
- col_version comparison (higher wins)
53+
- Value comparison as tie-breaker
54+
4. **Risk**: With same site_id, col_versions may collide causing
55+
unpredictable tie-breaking based on value comparison
56+
5. **Internal divergence**: When both copies make changes at same col_version,
57+
internal divergence is expected (each copy may end up with different values
58+
depending on sync order)
59+
60+
### Key Test Scenarios
61+
1. Basic setup - copying preserves site_id ✅
62+
2. Independent changes on both copies ✅
63+
3. Sync changes between colliding site IDs ✅
64+
4. Bidirectional sync (convergence test) ✅
65+
5. Concurrent inserts with same PK ✅
66+
6. Delete/resurrection with same site_id ✅
67+
7. Zig vs Rust/C parity (full collision scenario) ✅
68+
69+
### Recommended Recovery
70+
```sql
71+
-- Regenerate site_id on one copy:
72+
DELETE FROM crsql_site_id;
73+
-- (triggers regeneration on next access)
74+
75+
-- Or manually set:
76+
INSERT INTO crsql_site_id VALUES (randomblob(16));
77+
```
78+
79+
### Parity
80+
Zig and Rust/C implementations behave **identically** under site_id collision.
81+
Both exhibit the same internal divergence patterns when col_versions collide.

0 commit comments

Comments
 (0)