Skip to content

Commit f0f192a

Browse files
committed
docs: add Database Schema Recommendations
1 parent fc8fc6b commit f0f192a

File tree

2 files changed

+149
-0
lines changed

2 files changed

+149
-0
lines changed

API.md

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,18 @@ Before initialization, `cloudsync_init` performs schema sanity checks to ensure
4444
- All primary key columns must be `NOT NULL`.
4545
- All non-primary key `NOT NULL` columns must have a `DEFAULT` value.
4646

47+
**Schema Design Considerations:**
48+
49+
When designing your database schema for SQLite Sync, follow these essential requirements:
50+
51+
- **Primary Keys**: Use TEXT primary keys with `cloudsync_uuid()` for globally unique identifiers. Avoid auto-incrementing integers.
52+
- **Column Constraints**: All NOT NULL columns (except primary keys) must have DEFAULT values to prevent synchronization errors.
53+
- **UNIQUE Constraints**: In multi-tenant scenarios, use composite UNIQUE constraints (e.g., `UNIQUE(tenant_id, email)`) instead of global uniqueness.
54+
- **Foreign Key Compatibility**: Be aware of potential conflicts during CRDT merge operations and RLS policy interactions.
55+
- **Trigger Compatibility**: Triggers may cause duplicate operations or be called multiple times due to column-by-column processing.
56+
57+
For comprehensive guidelines, see the [Database Schema Recommendations](../README.md#database-schema-recommendations) section in the README.
58+
4759
The function supports three overloads:
4860
- `cloudsync_init(table_name)`: Uses the default 'cls' CRDT algorithm.
4961
- `cloudsync_init(table_name, crdt_algo)`: Specifies a CRDT algorithm ('cls', 'dws', 'aws', 'gos').
@@ -373,6 +385,8 @@ SELECT cloudsync_network_has_unsent_changes();
373385

374386
**Returns:** None.
375387

388+
**Errors:** See [Network Errors](#network-errors) for common error conditions.
389+
376390
**Example:**
377391

378392
```sql
@@ -397,6 +411,8 @@ On success, it returns `SQLITE_OK`, and the return value indicates how many chan
397411

398412
**Returns:** The number of changes downloaded. Errors are reported via the SQLite return code.
399413

414+
**Errors:** See [Network Errors](#network-errors) for common error conditions.
415+
400416
**Example:**
401417

402418
```sql
@@ -419,6 +435,8 @@ SELECT cloudsync_network_check_changes();
419435

420436
**Returns:** The number of changes downloaded. Errors are reported via the SQLite return code.
421437

438+
**Errors:** See [Network Errors](#network-errors) for common error conditions.
439+
422440
**Example:**
423441

424442
```sql
@@ -460,3 +478,21 @@ SELECT cloudsync_network_reset_sync_version();
460478
```sql
461479
SELECT cloudsync_network_logout();
462480
```
481+
482+
---
483+
484+
### Network Errors
485+
486+
Network functions may encounter specific errors during synchronization:
487+
488+
#### Device Limit Exceeded
489+
490+
If the device limit for your current plan on the cloud node is exceeded, network functions return error code `SQLITE_ERROR` (1) with the error message:
491+
492+
```
493+
403 Device limit reached: You've already registered the maximum number of <n> devices allowed by your current plan.
494+
```
495+
496+
**Resolution:** To resolve this error, you can:
497+
- [Upgrade to a higher plan](https://www.sqlite.ai/pricing) to increase your device limit
498+
- Remove unused devices from the OffSync section of your database in the [SQLite Cloud dashboard](https://dashboard.sqlitecloud.io/)

README.md

Lines changed: 113 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,12 @@ In simple terms, CRDTs make it possible for multiple users to **edit shared data
2020
- [Documentation](#documentation)
2121
- [Installation](#installation)
2222
- [Getting Started](#getting-started)
23+
- [Database Schema Recommendations](#database-schema-recommendations)
24+
- [Primary Key Requirements](#primary-key-requirements)
25+
- [Column Constraint Guidelines](#column-constraint-guidelines)
26+
- [UNIQUE Constraint Considerations](#unique-constraint-considerations)
27+
- [Foreign Key Compatibility](#foreign-key-compatibility)
28+
- [Trigger Compatibility](#trigger-compatibility)
2329
- [License](#license)
2430

2531
## Key Features
@@ -251,6 +257,113 @@ Use SQLite-AI alongside:
251257
* **[SQLite-Vector](https://github.com/sqliteai/sqlite-vector)** – vector search from SQL
252258
* **[SQLite-JS](https://github.com/sqliteai/sqlite-js)** – define SQLite functions in JavaScript
253259

260+
## Database Schema Recommendations
261+
262+
When designing your database schema for SQLite Sync, follow these best practices to ensure optimal CRDT performance and conflict resolution:
263+
264+
### Primary Key Requirements
265+
266+
- **Use globally unique identifiers**: Always use TEXT primary keys with UUIDs, ULIDs, or similar globally unique identifiers
267+
- **Avoid auto-incrementing integers**: Integer primary keys can cause conflicts across multiple devices
268+
- **Use `cloudsync_uuid()`**: The built-in function generates UUIDv7 identifiers optimized for distributed systems
269+
- **All primary keys must be explicitly declared as `NOT NULL`**.
270+
271+
```sql
272+
-- ✅ Recommended: Globally unique TEXT primary key
273+
CREATE TABLE users (
274+
id TEXT PRIMARY KEY NOT NULL, -- Use cloudsync_uuid()
275+
name TEXT NOT NULL,
276+
email TEXT UNIQUE NOT NULL
277+
);
278+
279+
-- ❌ Avoid: Auto-incrementing integer primary key
280+
CREATE TABLE users (
281+
id INTEGER PRIMARY KEY AUTOINCREMENT, -- Causes conflicts
282+
name TEXT NOT NULL,
283+
email TEXT UNIQUE NOT NULL
284+
);
285+
```
286+
287+
### Column Constraint Guidelines
288+
289+
- **Provide DEFAULT values**: All `NOT NULL` columns (except primary keys) must have `DEFAULT` values
290+
- **Consider nullable columns**: For optional data, use nullable columns instead of empty strings
291+
292+
```sql
293+
-- ✅ Recommended: Proper constraints and defaults
294+
CREATE TABLE tasks (
295+
id TEXT PRIMARY KEY,
296+
title TEXT NOT NULL DEFAULT '',
297+
status TEXT NOT NULL DEFAULT 'pending',
298+
priority INTEGER NOT NULL DEFAULT 1,
299+
created_at INTEGER NOT NULL DEFAULT (strftime('%s', 'now')),
300+
assigned_to TEXT -- Nullable for optional assignment
301+
);
302+
```
303+
304+
### UNIQUE Constraint Considerations
305+
306+
When converting from single-tenant to multi-tenant database schemas with Row-Level Security, **UNIQUE constraints must be globally unique** across all tenants in the cloud database. For columns that should only be unique within a tenant, use composite UNIQUE constraints.
307+
308+
```sql
309+
-- ❌ Single-tenant: Unique email per database
310+
CREATE TABLE users (
311+
id TEXT PRIMARY KEY,
312+
email TEXT UNIQUE NOT NULL -- Problem: Not unique across tenants
313+
);
314+
315+
-- ✅ Multi-tenant: Composite unique constraint
316+
CREATE TABLE users (
317+
id TEXT PRIMARY KEY,
318+
tenant_id TEXT NOT NULL,
319+
email TEXT NOT NULL,
320+
UNIQUE(tenant_id, email) -- Unique email per tenant
321+
);
322+
```
323+
324+
### Foreign Key Compatibility
325+
326+
When using foreign key constraints with SQLite Sync, be aware that interactions with the CRDT merge algorithm and Row-Level Security policies may cause constraint violations.
327+
328+
#### Potential Conflicts
329+
330+
**CRDT Merge Algorithm and DEFAULT Values**
331+
332+
- CRDT changes are applied column-by-column during synchronization
333+
- Columns may be temporarily assigned DEFAULT values during the merge process
334+
- If a foreign key column has a DEFAULT value, that value must exist in the referenced table
335+
336+
**Row-Level Security and CASCADE Actions**
337+
- RLS policies may block operations required for maintaining referential integrity
338+
- CASCADE DELETE/UPDATE operations may fail if RLS prevents access to related rows
339+
340+
#### Recommendations
341+
342+
**Database Design Patterns**
343+
- Prefer application-level cascade logic over database-level CASCADE actions
344+
- Design RLS policies to accommodate referential integrity operations
345+
- Use nullable foreign keys where appropriate to avoid DEFAULT value issues
346+
- Alternatively, ensure DEFAULT values for foreign key columns exist in their referenced tables
347+
348+
**Testing and Validation**
349+
- Test synchronization scenarios with foreign key constraints enabled
350+
- Monitor for constraint violations during sync operations in development
351+
352+
### Trigger Compatibility
353+
354+
Be aware that certain types of triggers can cause errors during synchronization due to SQLite Sync's merge logic.
355+
356+
**Duplicate Operations**
357+
- If a trigger modifies a table that is also synchronized with SQLite Sync, changes performed by the trigger may be applied twice during the merge operation
358+
- This can lead to constraint violations or unexpected data states depending on the table's constraints
359+
360+
**Column-by-Column Processing**
361+
- SQLite Sync applies changes column-by-column during synchronization
362+
- UPDATE triggers may be called multiple times for a single row as each column is processed
363+
- This can result in unexpected trigger behavior
364+
365+
366+
254367
## License
255368

256369
This project is licensed under the [Elastic License 2.0](./LICENSE.md). You can use, copy, modify, and distribute it under the terms of the license for non-production use. For production or managed service use, please [contact SQLite Cloud, Inc](mailto:[email protected]) for a commercial license.

0 commit comments

Comments
 (0)