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 .changeset/green-books-shout.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'@powersync/service-core': patch
---

Fix storageStats error in metrics endpoint when collections don't exist.
2 changes: 0 additions & 2 deletions packages/service-core/src/storage/BucketStorage.ts
Original file line number Diff line number Diff line change
Expand Up @@ -253,8 +253,6 @@ export interface SyncRulesBucketStorage {
*/
clear(): Promise<void>;

setSnapshotDone(lsn: string): Promise<void>;

autoActivate(): Promise<void>;

/**
Expand Down
27 changes: 18 additions & 9 deletions packages/service-core/src/storage/MongoBucketStorage.ts
Original file line number Diff line number Diff line change
Expand Up @@ -294,6 +294,15 @@ export class MongoBucketStorage implements BucketStorageFactory {
}

async getStorageMetrics(): Promise<StorageMetrics> {
const ignoreNotExiting = (e: unknown) => {
if (e instanceof mongo.MongoServerError && e.codeName == 'NamespaceNotFound') {
// Collection doesn't exist - return 0
return [{ storageStats: { size: 0 } }];
} else {
return Promise.reject(e);
}
};

const active_sync_rules = await this.getActiveSyncRules();
if (active_sync_rules == null) {
return {
Expand All @@ -307,34 +316,34 @@ export class MongoBucketStorage implements BucketStorageFactory {
.aggregate([
{
$collStats: {
storageStats: {},
count: {}
storageStats: {}
}
}
])
.toArray();
.toArray()
.catch(ignoreNotExiting);

const parameters_aggregate = await this.db.bucket_parameters
.aggregate([
{
$collStats: {
storageStats: {},
count: {}
storageStats: {}
}
}
])
.toArray();
.toArray()
.catch(ignoreNotExiting);

const replication_aggregate = await this.db.current_data
.aggregate([
{
$collStats: {
storageStats: {},
count: {}
storageStats: {}
}
}
])
.toArray();
.toArray()
.catch(ignoreNotExiting);

return {
operations_size_bytes: operations_aggregate[0].storageStats.size,
Expand Down
15 changes: 0 additions & 15 deletions packages/service-core/src/storage/mongo/MongoSyncBucketStorage.ts
Original file line number Diff line number Diff line change
Expand Up @@ -505,21 +505,6 @@ export class MongoSyncBucketStorage implements SyncRulesBucketStorage {
);
}

async setSnapshotDone(lsn: string): Promise<void> {
await this.db.sync_rules.updateOne(
{
_id: this.group_id
},
{
$set: {
snapshot_done: true,
persisted_lsn: lsn,
last_checkpoint_ts: new Date()
}
}
);
}

async autoActivate(): Promise<void> {
await this.db.client.withSession(async (session) => {
await session.withTransaction(async () => {
Expand Down
12 changes: 12 additions & 0 deletions packages/service-core/src/storage/mongo/db.ts
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,9 @@ export class PowerSyncMongo {
this.locks = this.db.collection('locks');
}

/**
* Clear all collections.
*/
async clear() {
await this.current_data.deleteMany({});
await this.bucket_data.deleteMany({});
Expand All @@ -70,4 +73,13 @@ export class PowerSyncMongo {
await this.instance.deleteOne({});
await this.locks.deleteMany({});
}

/**
* Drop the entire database.
*
* Primarily for tests.
*/
async drop() {
await this.db.dropDatabase();
}
}
22 changes: 22 additions & 0 deletions packages/service-core/test/src/data_storage.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1294,4 +1294,26 @@ bucket_definitions:

expect(getBatchMeta(batch3)).toEqual(null);
});

test('empty storage metrics', async () => {
const f = await factory({ dropAll: true });

const metrics = await f.getStorageMetrics();
expect(metrics).toEqual({
operations_size_bytes: 0,
parameters_size_bytes: 0,
replication_size_bytes: 0
});

const r = await f.configureSyncRules('bucket_definitions: {}');
const storage = f.getInstance(r.persisted_sync_rules!.parsed());
await storage.autoActivate();

const metrics2 = await f.getStorageMetrics();
expect(metrics2).toEqual({
operations_size_bytes: 0,
parameters_size_bytes: 0,
replication_size_bytes: 0
});
});
}
7 changes: 0 additions & 7 deletions packages/service-core/test/src/sync.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@ import { JSONBig } from '@powersync/service-jsonbig';
import { RequestParameters } from '@powersync/service-sync-rules';
import * as timers from 'timers/promises';
import { describe, expect, test } from 'vitest';
import { ZERO_LSN } from '../../src/replication/WalStream.js';
import { streamResponse } from '../../src/sync/sync.js';
import { makeTestTable, MONGO_STORAGE_FACTORY, StorageFactory } from './util.js';

Expand Down Expand Up @@ -33,7 +32,6 @@ function defineTests(factory: StorageFactory) {
});

const storage = await f.getInstance(syncRules.parsed());
await storage.setSnapshotDone(ZERO_LSN);
await storage.autoActivate();

const result = await storage.startBatch({}, async (batch) => {
Expand Down Expand Up @@ -82,7 +80,6 @@ function defineTests(factory: StorageFactory) {
});

const storage = await f.getInstance(syncRules.parsed());
await storage.setSnapshotDone(ZERO_LSN);
await storage.autoActivate();

const result = await storage.startBatch({}, async (batch) => {
Expand Down Expand Up @@ -125,7 +122,6 @@ function defineTests(factory: StorageFactory) {
});

const storage = await f.getInstance(syncRules.parsed());
await storage.setSnapshotDone(ZERO_LSN);
await storage.autoActivate();

const stream = streamResponse({
Expand All @@ -152,7 +148,6 @@ function defineTests(factory: StorageFactory) {
});

const storage = await f.getInstance(syncRules.parsed());
await storage.setSnapshotDone(ZERO_LSN);
await storage.autoActivate();

const stream = streamResponse({
Expand Down Expand Up @@ -211,7 +206,6 @@ function defineTests(factory: StorageFactory) {
});

const storage = await f.getInstance(syncRules.parsed());
await storage.setSnapshotDone(ZERO_LSN);
await storage.autoActivate();

const exp = Date.now() / 1000 + 0.1;
Expand Down Expand Up @@ -249,7 +243,6 @@ function defineTests(factory: StorageFactory) {
});

const storage = await f.getInstance(syncRules.parsed());
await storage.setSnapshotDone(ZERO_LSN);
await storage.autoActivate();

await storage.startBatch({}, async (batch) => {
Expand Down
17 changes: 14 additions & 3 deletions packages/service-core/test/src/util.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,11 +22,22 @@ Metrics.getInstance().resetCounters();

export const TEST_URI = env.PG_TEST_URL;

export type StorageFactory = () => Promise<BucketStorageFactory>;
export interface StorageOptions {
/**
* By default, collections are only cleared/
* Setting this to true will drop the collections completely.
*/
dropAll?: boolean;
}
export type StorageFactory = (options?: StorageOptions) => Promise<BucketStorageFactory>;

export const MONGO_STORAGE_FACTORY: StorageFactory = async () => {
export const MONGO_STORAGE_FACTORY: StorageFactory = async (options?: StorageOptions) => {
const db = await connectMongo();
await db.clear();
if (options?.dropAll) {
await db.drop();
} else {
await db.clear();
}
return new MongoBucketStorage(db, { slot_name_prefix: 'test_' });
};

Expand Down
Loading