Skip to content

Commit e72c189

Browse files
committed
Merge remote-tracking branch 'origin/main' into concurrent-storage-batches
2 parents d12daac + b77bb2c commit e72c189

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

61 files changed

+5652
-60
lines changed

.changeset/thin-snails-compete.md

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
---
2+
'@powersync/service-core': minor
3+
'@powersync/service-module-mssql': minor
4+
'@powersync/service-module-postgres-storage': patch
5+
'@powersync/service-module-mongodb-storage': patch
6+
'@powersync/service-module-postgres': patch
7+
'@powersync/service-errors': patch
8+
'@powersync/service-module-mysql': patch
9+
'@powersync/service-image': patch
10+
---
11+
12+
- First iteration of MSSQL replication using Change Data Capture (CDC).
13+
- Supports resumable snapshot replication
14+
- Uses CDC polling for replication
15+

.github/workflows/test.yml

Lines changed: 93 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -297,3 +297,96 @@ jobs:
297297

298298
- name: Test Storage
299299
run: pnpm --filter='./modules/module-mongodb-storage' test
300+
301+
run-mssql-tests:
302+
name: MSSQL Test
303+
runs-on: ubuntu-latest
304+
needs: run-core-tests
305+
306+
env:
307+
MSSQL_SA_PASSWORD: 321strong_ROOT_password
308+
309+
strategy:
310+
fail-fast: false
311+
matrix:
312+
mssql-version: [2022, 2025]
313+
314+
steps:
315+
- uses: actions/checkout@v5
316+
317+
- name: Login to Docker Hub
318+
if: github.event_name != 'pull_request'
319+
uses: docker/login-action@v3
320+
with:
321+
username: ${{ secrets.DOCKERHUB_USERNAME }}
322+
password: ${{ secrets.DOCKERHUB_TOKEN }}
323+
324+
- name: Start MSSQL
325+
run: |
326+
docker run \
327+
--name MSSQLTestDatabase \
328+
--health-cmd="/opt/mssql-tools18/bin/sqlcmd -C -S localhost -U sa -P \"${{ env.MSSQL_SA_PASSWORD }}\" -Q \"SELECT 1;\" || exit 1" \
329+
--health-interval 5s \
330+
--health-timeout 3s \
331+
--health-retries 30 \
332+
-e ACCEPT_EULA=Y \
333+
-e MSSQL_SA_PASSWORD=${{ env.MSSQL_SA_PASSWORD }} \
334+
-e MSSQL_PID=Developer \
335+
-e MSSQL_AGENT_ENABLED=true \
336+
-p 1433:1433 \
337+
-d mcr.microsoft.com/mssql/server:${{ matrix.mssql-version }}-latest
338+
339+
- name: Wait for MSSQL to be healthy
340+
run: |
341+
timeout 120 bash -c 'until docker inspect --format="{{.State.Health.Status}}" MSSQLTestDatabase | grep -q "healthy"; do sleep 2; done'
342+
343+
- name: Initialize MSSQL database
344+
run: |
345+
docker run \
346+
--rm \
347+
--network host \
348+
-e MSSQL_SA_PASSWORD=${{ env.MSSQL_SA_PASSWORD }} \
349+
-v ${{ github.workspace }}/modules/module-mssql/ci/init-mssql.sql:/scripts/init-mssql.sql:ro \
350+
mcr.microsoft.com/mssql/server:${{ matrix.mssql-version }}-latest \
351+
/bin/bash -c "/opt/mssql-tools18/bin/sqlcmd -C -S localhost -U sa -P \"${{ env.MSSQL_SA_PASSWORD }}\" -v DATABASE=powersync -v DB_USER=sa -i /scripts/init-mssql.sql"
352+
353+
# The mongodb-github-action below doesn't use the Docker credentials for the pull.
354+
# We pre-pull, so that the image is cached.
355+
- name: Pre-pull Mongo image
356+
run: docker pull mongo:8.0
357+
358+
- name: Start MongoDB
359+
uses: supercharge/mongodb-github-action@1.12.0
360+
with:
361+
mongodb-version: '8.0'
362+
mongodb-replica-set: test-rs
363+
364+
- name: Start PostgreSQL (Storage)
365+
run: |
366+
docker run \
367+
--health-cmd pg_isready \
368+
--health-interval 10s \
369+
--health-timeout 5s \
370+
--health-retries 5 \
371+
-e POSTGRES_PASSWORD=postgres \
372+
-e POSTGRES_DB=powersync_storage_test \
373+
-p 5431:5432 \
374+
-d postgres:18
375+
376+
- name: Enable Corepack
377+
run: corepack enable
378+
- name: Setup Node.js
379+
uses: actions/setup-node@v6
380+
with:
381+
node-version-file: '.nvmrc'
382+
cache: pnpm
383+
384+
- name: Install dependencies
385+
run: pnpm install
386+
387+
- name: Build
388+
shell: bash
389+
run: pnpm build
390+
391+
- name: Test Replication
392+
run: pnpm --filter='./modules/module-mssql' test

modules/module-mongodb-storage/src/storage/implementation/MongoBucketBatch.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -146,6 +146,10 @@ export class MongoBucketBatch
146146
return this.last_checkpoint_lsn;
147147
}
148148

149+
get noCheckpointBeforeLsn() {
150+
return this.no_checkpoint_before_lsn;
151+
}
152+
149153
async flush(options?: storage.BatchBucketFlushOptions): Promise<storage.FlushedResult | null> {
150154
let result: storage.FlushedResult | null = null;
151155
// One flush may be split over multiple transactions.

modules/module-mssql/.npmignore

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
dev
2+
ci

modules/module-mssql/LICENSE

Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
1+
# Functional Source License, Version 1.1, ALv2 Future License
2+
3+
## Abbreviation
4+
5+
FSL-1.1-ALv2
6+
7+
## Notice
8+
9+
Copyright 2023-2025 Journey Mobile, Inc.
10+
11+
## Terms and Conditions
12+
13+
### Licensor ("We")
14+
15+
The party offering the Software under these Terms and Conditions.
16+
17+
### The Software
18+
19+
The "Software" is each version of the software that we make available under these Terms and Conditions, as indicated by our inclusion of these Terms and Conditions with the Software.
20+
21+
### License Grant
22+
23+
Subject to your compliance with this License Grant and the Patents, Redistribution and Trademark clauses below, we hereby grant you the right to use, copy, modify, create derivative works, publicly perform, publicly display and redistribute the Software for any Permitted Purpose identified below.
24+
25+
### Permitted Purpose
26+
27+
A Permitted Purpose is any purpose other than a Competing Use. A Competing Use means making the Software available to others in a commercial product or service that:
28+
29+
1. substitutes for the Software;
30+
2. substitutes for any other product or service we offer using the Software that exists as of the date we make the Software available; or
31+
3. offers the same or substantially similar functionality as the Software.
32+
33+
Permitted Purposes specifically include using the Software:
34+
35+
1. for your internal use and access;
36+
2. for non-commercial education;
37+
3. for non-commercial research; and
38+
4. in connection with professional services that you provide to a licensee using the Software in accordance with these Terms and Conditions.
39+
40+
### Patents
41+
42+
To the extent your use for a Permitted Purpose would necessarily infringe our patents, the license grant above includes a license under our patents. If you make a claim against any party that the Software infringes or contributes to the infringement of any patent, then your patent license to the Software ends immediately.
43+
44+
### Redistribution
45+
46+
The Terms and Conditions apply to all copies, modifications and derivatives of the Software.
47+
If you redistribute any copies, modifications or derivatives of the Software, you must include a copy of or a link to these Terms and Conditions and not remove any copyright notices provided in or with the Software.
48+
49+
### Disclaimer
50+
51+
THE SOFTWARE IS PROVIDED "AS IS" AND WITHOUT WARRANTIES OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION WARRANTIES OF FITNESS FOR A PARTICULAR PURPOSE, MERCHANTABILITY, TITLE OR NON-INFRINGEMENT.
52+
IN NO EVENT WILL WE HAVE ANY LIABILITY TO YOU ARISING OUT OF OR RELATED TO THE SOFTWARE, INCLUDING INDIRECT, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES, EVEN IF WE HAVE BEEN INFORMED OF THEIR POSSIBILITY IN ADVANCE.
53+
54+
### Trademarks
55+
56+
Except for displaying the License Details and identifying us as the origin of the Software, you have no right under these Terms and Conditions to use our trademarks, trade names, service marks or product names.
57+
58+
## Grant of Future License
59+
60+
We hereby irrevocably grant you an additional license to use the Software under the Apache License, Version 2.0 that is effective on the second anniversary of the date we make the Software available. On or after that date, you may use the Software under the Apache License, Version 2.0, in which case the following will apply:
61+
62+
Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License.
63+
You may obtain a copy of the License at
64+
65+
http://www.apache.org/licenses/LICENSE-2.0
66+
67+
Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License.

modules/module-mssql/README.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
# PowerSync MSSQL Module
2+
3+
MSSQL replication module for PowerSync
Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
-- Create database (idempotent)
2+
IF DB_ID('$(DATABASE)') IS NULL
3+
BEGIN
4+
CREATE DATABASE [$(DATABASE)];
5+
END
6+
GO
7+
8+
-- Enable CDC at the database level (idempotent)
9+
USE [$(DATABASE)];
10+
IF (SELECT is_cdc_enabled FROM sys.databases WHERE name = '$(DATABASE)') = 0
11+
BEGIN
12+
EXEC sys.sp_cdc_enable_db;
13+
END
14+
GO
15+
16+
-- Create PowerSync checkpoints table
17+
-- Powersync requires this table to ensure regular checkpoints appear in CDC
18+
IF OBJECT_ID('dbo._powersync_checkpoints', 'U') IS NULL
19+
BEGIN
20+
CREATE TABLE dbo._powersync_checkpoints (
21+
id INT IDENTITY PRIMARY KEY,
22+
last_updated DATETIME NOT NULL DEFAULT (GETDATE())
23+
);
24+
END
25+
26+
GRANT INSERT, UPDATE ON dbo._powersync_checkpoints TO [$(DB_USER)];
27+
GO
28+
29+
-- Enable CDC for the powersync checkpoints table
30+
IF NOT EXISTS (SELECT 1 FROM cdc.change_tables WHERE source_object_id = OBJECT_ID(N'dbo._powersync_checkpoints'))
31+
BEGIN
32+
EXEC sys.sp_cdc_enable_table
33+
@source_schema = N'dbo',
34+
@source_name = N'_powersync_checkpoints',
35+
@role_name = N'cdc_reader',
36+
@supports_net_changes = 0;
37+
END
38+
GO
39+
40+
-- Wait until capture job exists - usually takes a few seconds after enabling CDC on a table for the first time
41+
DECLARE @tries int = 10;
42+
WHILE @tries > 0 AND NOT EXISTS (SELECT 1 FROM msdb.dbo.cdc_jobs WHERE job_type = N'capture')
43+
BEGIN
44+
WAITFOR DELAY '00:00:01';
45+
SET @tries -= 1;
46+
END;
47+
48+
-- Set the CDC capture job polling interval to 0 seconds (default is 5 seconds).
49+
-- At the cost of higher CPU usage, this minimizes latency for change capture.
50+
EXEC sys.sp_cdc_change_job @job_type = N'capture', @pollinginterval = 0;
51+
GO
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
ROOT_PASSWORD=321strong_ROOT_password
2+
DATABASE=powersync
3+
DB_USER=powersync_user
4+
DB_USER_PASSWORD=321strong_POWERSYNC_password

modules/module-mssql/dev/README.md

Lines changed: 82 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,82 @@
1+
# MSSQL Dev Database
2+
3+
This directory contains Docker Compose configuration for running a local MSSQL Server instance with CDC (Change Data Capture) enabled for development and testing. The image used is the 2022 Edition of SQL Server. 2025 can also be used, but has issues on Mac OS X 26 Tahoe due to this issue: https://github.com/microsoft/mssql-docker/issues/942
4+
5+
## Prerequisites
6+
7+
- Docker and Docker Compose installed
8+
- A `.env` file in this directory see the `.env.template` for required variables
9+
10+
## Environment Variables
11+
12+
```bash
13+
ROOT_PASSWORD=
14+
DATABASE=
15+
DB_USER=
16+
DB_USER_PASSWORD=
17+
```
18+
19+
**Note:** The `ROOT_PASSWORD` and `DB_USER_PASSWORD` must meet SQL Server password complexity requirements (at least 8 characters, including uppercase, lowercase, numbers, and special characters).
20+
21+
## Usage
22+
23+
### Starting the Database
24+
25+
From the `dev` directory, run:
26+
27+
```bash
28+
docker compose up -d
29+
```
30+
31+
This will:
32+
1. Start the MSSQL Server container (`mssql-dev`)
33+
2. Wait for the database to be healthy
34+
3. Automatically run the setup container (`mssql-dev-setup`) which executes `init.sql`
35+
36+
### Stopping the Database
37+
38+
```bash
39+
docker compose down
40+
```
41+
42+
To also remove the data volume:
43+
44+
```bash
45+
docker compose down -v
46+
```
47+
48+
### Viewing Logs
49+
50+
```bash
51+
docker compose logs -f
52+
```
53+
54+
## What `init.sql` Does
55+
56+
The initialization script (`init.sql`) performs the following setup steps:
57+
58+
1. **Database Creation**: Creates the application database (if it doesn't exist)
59+
2. **CDC Setup**: Enables Change Data Capture at the database level
60+
3. **User Creation**: Creates a SQL Server login and database user with appropriate permissions
61+
4. **Create PowerSync Checkpoints table**: Creates the required `_powersync_checkpoints` table.
62+
5. **Demo Tables**: Creates sample tables (`lists` and `todos`) for testing (optional examples)
63+
6. **CDC Table Enablement**: Enables CDC tracking on the demo tables
64+
7. **Permissions**: Grants `db_datareader` and `cdc_reader` roles to the application user
65+
8. **Sample Data**: Inserts initial test data into the `lists` table
66+
67+
All operations are idempotent, so you can safely re-run the setup without errors. The demo tables section (steps 5–7) serves as an example of how to enable CDC on your own tables.
68+
69+
## Connection Details
70+
71+
- **Host**: `localhost`
72+
- **Port**: `1433`
73+
- **SA Login**: `sa` / `{ROOT_PASSWORD}`
74+
- **App Login**: `{DB_USER}` / `{DB_USER_PASSWORD}`
75+
- **Database**: `{DATABASE}`
76+
77+
## Troubleshooting
78+
79+
- If the setup container fails, check logs: `docker compose logs mssql-dev-setup`
80+
- Ensure your `.env` file exists and contains all required variables
81+
- The database container may take 30–60 seconds to become healthy on the first startup
82+
- If you encounter connection issues, verify the container is running: `docker compose ps`
Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
name: mssql-dev
2+
services:
3+
mssql-dev:
4+
platform: linux/amd64
5+
image: mcr.microsoft.com/mssql/server:2022-latest # 2025 Can also be used, but not on Mac 26 Tahoe due to this issue: https://github.com/microsoft/mssql-docker/issues/942
6+
container_name: mssql-dev
7+
ports:
8+
- "1433:1433"
9+
environment:
10+
ACCEPT_EULA: "Y"
11+
MSSQL_SA_PASSWORD: "${ROOT_PASSWORD}"
12+
MSSQL_PID: "Developer"
13+
MSSQL_AGENT_ENABLED: "true" # required for CDC capture/cleanup jobs
14+
volumes:
15+
- data:/var/opt/mssql
16+
healthcheck:
17+
test: [ "CMD-SHELL", "/opt/mssql-tools18/bin/sqlcmd -C -S localhost -U sa -P \"$${MSSQL_SA_PASSWORD}\" -Q \"SELECT 1;\" || exit 1" ]
18+
interval: 5s
19+
timeout: 3s
20+
retries: 30
21+
22+
mssql-dev-setup:
23+
platform: linux/amd64
24+
image: mcr.microsoft.com/mssql/server:2022-latest
25+
container_name: mssql-dev-setup
26+
depends_on:
27+
mssql-dev:
28+
condition: service_healthy
29+
environment:
30+
MSSQL_SA_PASSWORD: "${ROOT_PASSWORD}"
31+
DATABASE: "${DATABASE}"
32+
DB_USER: "${DB_USER}"
33+
DB_USER_PASSWORD: "${DB_USER_PASSWORD}"
34+
volumes:
35+
- ./init.sql:/scripts/init.sql:ro
36+
entrypoint: ["/bin/bash", "-lc", "/opt/mssql-tools18/bin/sqlcmd -C -S mssql-dev,1433 -U sa -P \"$${MSSQL_SA_PASSWORD}\" -i /scripts/init.sql && echo '✅ MSSQL init done'"]
37+
38+
volumes:
39+
data:

0 commit comments

Comments
 (0)