Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .prettierignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
**/.git
**/node_modules
dist
lib
pnpm-lock.yaml
5 changes: 3 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ _[PowerSync](https://www.powersync.com) is a Postgres-SQLite sync engine, which
The service can be started using the public Docker image. See the image [notes](./service/README.md)

# Monorepo Structure:

## Packages

- [packages/service-core](./packages/service-core/README.md)
Expand Down Expand Up @@ -52,13 +53,13 @@ Contains the PowerSync service code. This project is used to build the `journeya

- [docs](./docs/README.md)

Technical documentation regarding the implementation of PowerSync.
Technical documentation regarding the implementation of PowerSync.

## Test Client

- [test-client](./test-client/README.md)

Contains a minimal client demonstrating direct usage of the HTTP stream sync API. This can be used to test sync rules in contexts such as automated testing.
Contains a minimal client demonstrating direct usage of the HTTP stream sync API. This can be used to test sync rules in contexts such as automated testing.

# Developing

Expand Down
2 changes: 1 addition & 1 deletion libs/lib-services/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,6 @@
"devDependencies": {
"@types/lodash": "^4.17.5",
"@types/uuid": "^9.0.4",
"vitest": "^0.34.6"
"vitest": "^2.1.1"
}
}
6 changes: 3 additions & 3 deletions modules/module-postgres/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@
"build": "tsc -b",
"build:tests": "tsc -b test/tsconfig.json",
"clean": "rm -rf ./lib && tsc -b --clean",
"test": "vitest --no-threads"
"test": "vitest"
},
"exports": {
".": {
Expand Down Expand Up @@ -42,8 +42,8 @@
},
"devDependencies": {
"@types/uuid": "^9.0.4",
"typescript": "^5.2.2",
"vitest": "^0.34.6",
"typescript": "^5.6.2",
"vitest": "^2.1.1",
"vite-tsconfig-paths": "^4.3.2"
}
}
5 changes: 4 additions & 1 deletion modules/module-postgres/src/replication/PgManager.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,10 @@ export class PgManager {

private connectionPromises: Promise<pgwire.PgConnection>[] = [];

constructor(public options: NormalizedPostgresConnectionConfig, public poolOptions: pgwire.PgPoolOptions) {
constructor(
public options: NormalizedPostgresConnectionConfig,
public poolOptions: pgwire.PgPoolOptions
) {
// The pool is lazy - no connections are opened until a query is performed.
this.pool = pgwire.connectPgWirePool(this.options, poolOptions);
}
Expand Down
2 changes: 1 addition & 1 deletion modules/module-postgres/src/replication/PgRelation.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ export function getReplicaIdColumns(relation: PgoutputRelation): storage.ColumnD
} else {
return relation.columns
.filter((c) => (c.flags & 0b1) != 0)
.map((c) => ({ name: c.name, typeId: c.typeOid } satisfies storage.ColumnDescriptor));
.map((c) => ({ name: c.name, typeId: c.typeOid }) satisfies storage.ColumnDescriptor);
}
}
export function getRelId(source: PgoutputRelation): number {
Expand Down
44 changes: 2 additions & 42 deletions modules/module-postgres/src/utils/populate_test_data.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
import * as crypto from 'crypto';
import { Worker, isMainThread, parentPort, workerData } from 'node:worker_threads';
import { Worker } from 'node:worker_threads';

import * as pgwire from '@powersync/service-jpgwire';

Expand All @@ -12,49 +11,10 @@ export interface PopulateDataOptions {
size: number;
}

if (isMainThread || parentPort == null) {
// Not a worker - ignore
} else {
try {
const options = workerData as PopulateDataOptions;

const result = await populateDataInner(options);
parentPort.postMessage(result);
process.exit(0);
} catch (e) {
// This is a bug, not a connection issue
console.error(e);
// Only closes the Worker thread
process.exit(2);
}
}

async function populateDataInner(options: PopulateDataOptions) {
// Dedicated connection so we can release the memory easily
const initialDb = await pgwire.connectPgWire(options.connection, { type: 'standard' });
const largeDescription = crypto.randomBytes(options.size / 2).toString('hex');
let operation_count = 0;
for (let i = 0; i < options.num_transactions; i++) {
const prefix = `test${i}K`;

await initialDb.query({
statement: `INSERT INTO test_data(id, description, other) SELECT $1 || i, $2, 'foo' FROM generate_series(1, $3) i`,
params: [
{ type: 'varchar', value: prefix },
{ type: 'varchar', value: largeDescription },
{ type: 'int4', value: options.per_transaction }
]
});
operation_count += options.per_transaction;
}
await initialDb.end();
return operation_count;
}

export async function populateData(options: PopulateDataOptions) {
const WORKER_TIMEOUT = 30_000;

const worker = new Worker(new URL('./populate_test_data.js', import.meta.url), {
const worker = new Worker(new URL('./populate_test_data_worker.js', import.meta.url), {
workerData: options
});
const timeout = setTimeout(() => {
Expand Down
50 changes: 50 additions & 0 deletions modules/module-postgres/src/utils/populate_test_data_worker.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
import * as crypto from 'crypto';
import { isMainThread, parentPort, workerData } from 'node:worker_threads';

import * as pgwire from '@powersync/service-jpgwire';
import type { PopulateDataOptions } from './populate_test_data.js';

// This util is actually for tests only, but we need it compiled to JS for the service to work, so it's placed in the service.

if (isMainThread || parentPort == null) {
// Must not be imported - only expected to run in a worker
throw new Error('Do not import this file');
} else {
try {
const options = workerData as PopulateDataOptions;
if (options == null) {
throw new Error('loaded worker without options');
}

const result = await populateDataInner(options);
parentPort.postMessage(result);
process.exit(0);
} catch (e) {
// This is a bug, not a connection issue
console.error(e);
// Only closes the Worker thread
process.exit(2);
}
}

async function populateDataInner(options: PopulateDataOptions) {
// Dedicated connection so we can release the memory easily
const initialDb = await pgwire.connectPgWire(options.connection, { type: 'standard' });
const largeDescription = crypto.randomBytes(options.size / 2).toString('hex');
let operation_count = 0;
for (let i = 0; i < options.num_transactions; i++) {
const prefix = `test${i}K`;

await initialDb.query({
statement: `INSERT INTO test_data(id, description, other) SELECT $1 || i, $2, 'foo' FROM generate_series(1, $3) i`,
params: [
{ type: 'varchar', value: prefix },
{ type: 'varchar', value: largeDescription },
{ type: 'int4', value: options.per_transaction }
]
});
operation_count += options.per_transaction;
}
await initialDb.end();
return operation_count;
}
5 changes: 4 additions & 1 deletion modules/module-postgres/test/src/wal_stream_utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,10 @@ export class WalStreamTestContext {
public storage?: SyncRulesBucketStorage;
private replicationConnection?: pgwire.PgConnection;

constructor(public factory: BucketStorageFactory, public connectionManager: PgManager) {}
constructor(
public factory: BucketStorageFactory,
public connectionManager: PgManager
) {}

async dispose() {
this.abortController.abort();
Expand Down
8 changes: 7 additions & 1 deletion modules/module-postgres/vitest.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,12 @@ import tsconfigPaths from 'vite-tsconfig-paths';
export default defineConfig({
plugins: [tsconfigPaths()],
test: {
setupFiles: './test/src/setup.ts'
setupFiles: './test/src/setup.ts',
poolOptions: {
threads: {
singleThread: true
}
},
pool: 'threads'
}
});
10 changes: 5 additions & 5 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -21,20 +21,20 @@
"test": "pnpm run -r test"
},
"devDependencies": {
"@changesets/cli": "^2.27.3",
"@types/node": "18.11.11",
"@changesets/cli": "^2.27.8",
"@types/node": "^22.5.5",
"async": "^3.2.4",
"bson": "^6.6.0",
"concurrently": "^8.2.2",
"inquirer": "^9.2.7",
"npm-check-updates": "^16.10.15",
"prettier": "^2.8.8",
"npm-check-updates": "^17.1.2",
"prettier": "^3.3.3",
"rsocket-core": "1.0.0-alpha.3",
"rsocket-websocket-client": "1.0.0-alpha.3",
"semver": "^7.5.4",
"tsc-watch": "^6.2.0",
"ts-node-dev": "^2.0.0",
"typescript": "~5.2.2",
"typescript": "^5.6.2",
"ws": "^8.2.3"
}
}
14 changes: 7 additions & 7 deletions packages/jpgwire/ca/README.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@

## AWS RDS

https://docs.aws.amazon.com/AmazonRDS/latest/UserGuide/UsingWithRDS.SSL.html#UsingWithRDS.SSL.CertificatesAllRegions
Expand All @@ -11,12 +10,13 @@ https://learn.microsoft.com/en-us/azure/postgresql/flexible-server/how-to-connec
https://learn.microsoft.com/en-us/azure/postgresql/single-server/concepts-certificate-rotation

Includes:
* BaltimoreCyberTrustRoot
* DigiCertGlobalRootG2 Root CA
* Microsoft RSA Root Certificate Authority 2017
* Microsoft ECC Root Certificate Authority 2017
* DigiCert Global Root G3
* DigiCert Global Root CA

- BaltimoreCyberTrustRoot
- DigiCertGlobalRootG2 Root CA
- Microsoft RSA Root Certificate Authority 2017
- Microsoft ECC Root Certificate Authority 2017
- DigiCert Global Root G3
- DigiCert Global Root CA

## Supabase

Expand Down
3 changes: 3 additions & 0 deletions packages/jsonbig/README.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
# powersync-jsonbig

JSON is used everywhere, including:

1. PostgreSQL (json/jsonb types)
2. Sync rules input (values are normalized to JSON text).
3. Sync rule transformations (extracting values, constructing objects in the future)
Expand All @@ -9,10 +10,12 @@ JSON is used everywhere, including:

Where we can, JSON data is kept as strings and not parsed.
This is so that:

1. We don't add parsing / serializing overhead.
2. We don't change the data.

Specifically:

1. The SQLite type system makes a distinction between INTEGER and REAL values. We try to preserve this.
2. Integers in SQLite can be up to 64-bit.

Expand Down
4 changes: 2 additions & 2 deletions packages/rsocket-router/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@
"@types/ws": "~8.2.0",
"bson": "^6.6.0",
"rsocket-websocket-client": "1.0.0-alpha.3",
"typescript": "~5.2.2",
"vitest": "^0.34.6"
"typescript": "^5.6.2",
"vitest": "^2.1.1"
}
}
6 changes: 3 additions & 3 deletions packages/service-core/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@
"scripts": {
"build": "tsc -b",
"build:tests": "tsc -b test/tsconfig.json",
"test": "vitest --no-threads",
"test": "vitest",
"clean": "rm -rf ./lib && tsc -b --clean"
},
"dependencies": {
Expand Down Expand Up @@ -50,8 +50,8 @@
"@types/uuid": "^9.0.4",
"fastify": "4.23.2",
"fastify-plugin": "^4.5.1",
"typescript": "^5.2.2",
"typescript": "^5.6.2",
"vite-tsconfig-paths": "^4.3.2",
"vitest": "^0.34.6"
"vitest": "^2.1.1"
}
}
5 changes: 4 additions & 1 deletion packages/service-core/src/auth/RemoteJWKSCollector.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,10 @@ export type RemoteJWKSCollectorOptions = {
export class RemoteJWKSCollector implements KeyCollector {
private url: URL;

constructor(url: string, protected options?: RemoteJWKSCollectorOptions) {
constructor(
url: string,
protected options?: RemoteJWKSCollectorOptions
) {
try {
this.url = new URL(url);
} catch (e) {
Expand Down
6 changes: 5 additions & 1 deletion packages/service-core/src/storage/mongo/MongoCompactor.ts
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,11 @@ export class MongoCompactor {
private maxOpId: bigint | undefined;
private buckets: string[] | undefined;

constructor(private db: PowerSyncMongo, private group_id: number, options?: MongoCompactOptions) {
constructor(
private db: PowerSyncMongo,
private group_id: number,
options?: MongoCompactOptions
) {
this.idLimitBytes = (options?.memoryLimitMB ?? DEFAULT_MEMORY_LIMIT_MB) * 1024 * 1024;
this.moveBatchLimit = options?.moveBatchLimit ?? DEFAULT_MOVE_BATCH_LIMIT;
this.moveBatchQueryLimit = options?.moveBatchQueryLimit ?? DEFAULT_MOVE_BATCH_QUERY_LIMIT;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,10 @@ export class MongoPersistedSyncRulesContent implements PersistedSyncRulesContent

public current_lock: MongoSyncRulesLock | null = null;

constructor(private db: PowerSyncMongo, doc: mongo.WithId<SyncRuleDocument>) {
constructor(
private db: PowerSyncMongo,
doc: mongo.WithId<SyncRuleDocument>
) {
this.id = doc._id;
this.sync_rules_content = doc.content;
this.last_checkpoint_lsn = doc.last_checkpoint_lsn;
Expand Down
8 changes: 6 additions & 2 deletions packages/service-core/src/storage/mongo/MongoSyncRulesLock.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ import { logger } from '@powersync/lib-services-framework';
* replicates those sync rules at a time.
*/
export class MongoSyncRulesLock implements ReplicationLock {
private readonly refreshInterval: NodeJS.Timer;
private readonly refreshInterval: NodeJS.Timeout;

static async createLock(db: PowerSyncMongo, sync_rules: PersistedSyncRulesContent): Promise<MongoSyncRulesLock> {
const lockId = crypto.randomBytes(8).toString('hex');
Expand All @@ -35,7 +35,11 @@ export class MongoSyncRulesLock implements ReplicationLock {
return new MongoSyncRulesLock(db, sync_rules.id, lockId);
}

constructor(private db: PowerSyncMongo, public sync_rules_id: number, private lock_id: string) {
constructor(
private db: PowerSyncMongo,
public sync_rules_id: number,
private lock_id: string
) {
this.refreshInterval = setInterval(async () => {
try {
await this.refresh();
Expand Down
5 changes: 4 additions & 1 deletion packages/service-core/src/storage/mongo/PersistedBatch.ts
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,10 @@ export class PersistedBatch {
*/
currentSize = 0;

constructor(private group_id: number, writtenSize: number) {
constructor(
private group_id: number,
writtenSize: number
) {
this.currentSize = writtenSize;
}

Expand Down
Loading
Loading