Skip to content

Expand details in database application name #293

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Jul 1, 2025
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
8 changes: 5 additions & 3 deletions .changeset/slow-hounds-walk.md
Original file line number Diff line number Diff line change
@@ -1,10 +1,12 @@
---
'@powersync/service-module-mongodb-storage': patch
'@powersync/service-module-postgres-storage': patch
'@powersync/service-module-mongodb': patch
'@powersync/service-module-postgres': patch
'@powersync/service-module-mysql': patch
'@powersync/lib-service-mongodb': patch
'@powersync/service-core': patch
'@powersync/service-image': patch
'@powersync/service-core': minor
'@powersync/service-image': minor
---

Add 'powersync' as the app name for MongoDB and MySQL connections.
Add 'powersync' or 'powersync-storage' as the app name for database connections.
10 changes: 8 additions & 2 deletions libs/lib-mongodb/src/db/mongo.ts
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,8 @@ export const MONGO_OPERATION_TIMEOUT_MS = 30_000;
export const MONGO_CLEAR_OPERATION_TIMEOUT_MS = 5_000;

export interface MongoConnectionOptions {
maxPoolSize: number;
maxPoolSize?: number;
powersyncVersion?: string;
}

/**
Expand All @@ -50,7 +51,12 @@ export function createMongoClient(config: BaseMongoConfigDecoded, options?: Mong
serverSelectionTimeoutMS: 30_000,

// Identify the client
appName: 'powersync-storage',
appName: options?.powersyncVersion ? `powersync-storage ${options.powersyncVersion}` : 'powersync-storage',
driverInfo: {
// This is merged with the node driver info.
name: 'powersync-storage',
version: options?.powersyncVersion
},

lookup: normalized.lookup,

Expand Down
6 changes: 5 additions & 1 deletion libs/lib-postgres/src/db/connection/ConnectionSlot.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ export type ConnectionLease = {
export type ConnectionSlotOptions = {
config: pgwire.NormalizedConnectionConfig;
notificationChannels?: string[];
applicationName: string;
};

export const MAX_CONNECTION_ATTEMPTS = 5;
Expand Down Expand Up @@ -46,7 +47,10 @@ export class ConnectionSlot extends framework.BaseObserver<ConnectionSlotListene
}

protected async connect() {
this.connectingPromise = pgwire.connectPgWire(this.options.config, { type: 'standard' });
this.connectingPromise = pgwire.connectPgWire(this.options.config, {
type: 'standard',
applicationName: this.options.applicationName
});
const connection = await this.connectingPromise;
this.connectingPromise = null;
await this.iterateAsyncListeners(async (l) => l.connectionCreated?.(connection));
Expand Down
11 changes: 9 additions & 2 deletions libs/lib-postgres/src/db/connection/DatabaseClient.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,8 @@ export type DatabaseClientOptions = {
* Notification channels to listen to.
*/
notificationChannels?: string[];

applicationName: string;
};

export type DatabaseClientListener = NotificationListener & {
Expand Down Expand Up @@ -43,12 +45,17 @@ export class DatabaseClient extends AbstractPostgresConnection<DatabaseClientLis
super();
this.closed = false;
this.pool = pgwire.connectPgWirePool(options.config, {
maxSize: options.config.max_pool_size
maxSize: options.config.max_pool_size,
applicationName: options.applicationName
});
this.connections = Array.from({ length: TRANSACTION_CONNECTION_COUNT }, (v, index) => {
// Only listen to notifications on a single (the first) connection
const notificationChannels = index == 0 ? options.notificationChannels : [];
const slot = new ConnectionSlot({ config: options.config, notificationChannels });
const slot = new ConnectionSlot({
config: options.config,
notificationChannels,
applicationName: options.applicationName
});
slot.registerListener({
connectionAvailable: () => this.processConnectionQueue(),
connectionError: (ex) => this.handleConnectionError(ex),
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import * as lib_mongo from '@powersync/lib-service-mongodb';
import { ErrorCode, logger, ServiceAssertionError, ServiceError } from '@powersync/lib-services-framework';
import { storage } from '@powersync/service-core';
import { POWERSYNC_VERSION, storage } from '@powersync/service-core';
import { MongoStorageConfig } from '../../types/types.js';
import { MongoBucketStorage } from '../MongoBucketStorage.js';
import { PowerSyncMongo } from './db.js';
Expand All @@ -23,6 +23,7 @@ export class MongoStorageProvider implements storage.BucketStorageProvider {

const decodedConfig = MongoStorageConfig.decode(storage as any);
const client = lib_mongo.db.createMongoClient(decodedConfig, {
powersyncVersion: POWERSYNC_VERSION,
maxPoolSize: resolvedConfig.storage.max_pool_size ?? 8
});

Expand Down
10 changes: 8 additions & 2 deletions modules/module-mongodb-storage/src/storage/implementation/db.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import * as lib_mongo from '@powersync/lib-service-mongodb';
import { mongo } from '@powersync/lib-service-mongodb';
import { storage } from '@powersync/service-core';
import { POWERSYNC_VERSION, storage } from '@powersync/service-core';

import { MongoStorageConfig } from '../../types/types.js';
import {
Expand Down Expand Up @@ -130,5 +130,11 @@ export class PowerSyncMongo {
}

export function createPowerSyncMongo(config: MongoStorageConfig, options?: lib_mongo.MongoConnectionOptions) {
return new PowerSyncMongo(lib_mongo.createMongoClient(config, options), { database: config.database });
return new PowerSyncMongo(
lib_mongo.createMongoClient(config, {
powersyncVersion: POWERSYNC_VERSION,
...options
}),
{ database: config.database }
);
}
Original file line number Diff line number Diff line change
Expand Up @@ -104,20 +104,13 @@ export function replicaIdToSubkey(table: bson.ObjectId, id: storage.ReplicaId):
}
}

/**
* Helper function for creating a MongoDB client from consumers of this package
*/
const createMongoClient = (url: string, options?: mongo.MongoClientOptions) => {
return new mongo.MongoClient(url, options);
};

/**
* Helper for unit tests
*/
export const connectMongoForTests = (url: string, isCI: boolean) => {
// Short timeout for tests, to fail fast when the server is not available.
// Slightly longer timeouts for CI, to avoid arbitrary test failures
const client = createMongoClient(url, {
const client = new mongo.MongoClient(url, {
connectTimeoutMS: isCI ? 15_000 : 5_000,
socketTimeoutMS: isCI ? 15_000 : 5_000,
serverSelectionTimeoutMS: isCI ? 15_000 : 2_500
Expand Down
9 changes: 7 additions & 2 deletions modules/module-mongodb/src/replication/MongoManager.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { mongo } from '@powersync/lib-service-mongodb';

import { NormalizedMongoConnectionConfig } from '../types/types.js';
import { BSON_DESERIALIZE_DATA_OPTIONS } from '@powersync/service-core';
import { BSON_DESERIALIZE_DATA_OPTIONS, POWERSYNC_VERSION } from '@powersync/service-core';

/**
* Manage a MongoDB source database connection.
Expand Down Expand Up @@ -30,7 +30,12 @@ export class MongoManager {
serverSelectionTimeoutMS: 30_000,

// Identify the client
appName: 'powersync',
appName: `powersync ${POWERSYNC_VERSION}`,
driverInfo: {
// This is merged with the node driver info.
name: 'powersync',
version: POWERSYNC_VERSION
},

// Avoid too many connections:
// 1. It can overwhelm the source database.
Expand Down
5 changes: 3 additions & 2 deletions modules/module-mysql/src/replication/BinLogReplicationJob.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { container, logger as defaultLogger } from '@powersync/lib-services-framework';
import { replication } from '@powersync/service-core';
import { POWERSYNC_VERSION, replication } from '@powersync/service-core';
import { BinlogConfigurationError, BinLogStream } from './BinLogStream.js';
import { MySQLConnectionManagerFactory } from './MySQLConnectionManagerFactory.js';

Expand Down Expand Up @@ -61,7 +61,8 @@ export class BinLogReplicationJob extends replication.AbstractReplicationJob {
// https://dev.mysql.com/doc/refman/8.0/en/performance-schema-connection-attribute-tables.html
// These do not appear to be supported by Zongji yet, so we only specify it here.
// Query using `select * from performance_schema.session_connect_attrs`.
program_name: 'powersync'
program_name: 'powersync',
program_version: POWERSYNC_VERSION

// _client_name and _client_version is specified by the driver
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import { fileURLToPath } from 'url';

import { normalizePostgresStorageConfig, PostgresStorageConfigDecoded } from '../types/types.js';

import { getStorageApplicationName } from '../utils/application-name.js';
import { STORAGE_SCHEMA_NAME } from '../utils/db.js';
import { PostgresMigrationStore } from './PostgresMigrationStore.js';

Expand All @@ -25,7 +26,8 @@ export class PostgresMigrationAgent extends migrations.AbstractPowerSyncMigratio

this.db = new lib_postgres.DatabaseClient({
config: normalizePostgresStorageConfig(config),
schema: STORAGE_SCHEMA_NAME
schema: STORAGE_SCHEMA_NAME,
applicationName: getStorageApplicationName()
});
this.store = new PostgresMigrationStore({
db: this.db
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,15 @@ import { configFile } from '@powersync/service-types';
import { isPostgresStorageConfig, normalizePostgresStorageConfig, PostgresStorageConfig } from '../types/types.js';
import { STORAGE_SCHEMA_NAME } from '../utils/db.js';
import { ServiceAssertionError } from '@powersync/lib-services-framework';
import { getStorageApplicationName } from '../utils/application-name.js';

export const openMigrationDB = (config: configFile.BaseStorageConfig) => {
if (!isPostgresStorageConfig(config)) {
throw new ServiceAssertionError(`Input storage configuration is not for Postgres`);
}
return new lib_postgres.DatabaseClient({
config: normalizePostgresStorageConfig(PostgresStorageConfig.decode(config)),
schema: STORAGE_SCHEMA_NAME
schema: STORAGE_SCHEMA_NAME,
applicationName: getStorageApplicationName()
});
};
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import { NOTIFICATION_CHANNEL, STORAGE_SCHEMA_NAME } from '../utils/db.js';
import { notifySyncRulesUpdate } from './batch/PostgresBucketBatch.js';
import { PostgresSyncRulesStorage } from './PostgresSyncRulesStorage.js';
import { PostgresPersistedSyncRulesContent } from './sync-rules/PostgresPersistedSyncRulesContent.js';
import { getStorageApplicationName } from '../utils/application-name.js';

export type PostgresBucketStorageOptions = {
config: NormalizedPostgresStorageConfig;
Expand All @@ -32,7 +33,8 @@ export class PostgresBucketStorageFactory
this.db = new lib_postgres.DatabaseClient({
config: options.config,
schema: STORAGE_SCHEMA_NAME,
notificationChannels: [NOTIFICATION_CHANNEL]
notificationChannels: [NOTIFICATION_CHANNEL],
applicationName: getStorageApplicationName()
});
this.slot_name_prefix = options.slot_name_prefix;

Expand Down
8 changes: 8 additions & 0 deletions modules/module-postgres-storage/src/utils/application-name.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
import { POWERSYNC_VERSION } from '@powersync/service-core';

/**
* Name for postgres application_name, for bucket storage connections.
*/
export function getStorageApplicationName() {
return `powersync-storage/${POWERSYNC_VERSION}`;
}
4 changes: 3 additions & 1 deletion modules/module-postgres/src/api/PostgresRouteAPIAdapter.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import * as replication_utils from '../replication/replication-utils.js';
import { getDebugTableInfo } from '../replication/replication-utils.js';
import { KEEPALIVE_STATEMENT, PUBLICATION_NAME } from '../replication/WalStream.js';
import * as types from '../types/types.js';
import { getApplicationName } from '../utils/application-name.js';

export class PostgresRouteAPIAdapter implements api.RouteAPI {
connectionTag: string;
Expand All @@ -16,7 +17,8 @@ export class PostgresRouteAPIAdapter implements api.RouteAPI {

static withConfig(config: types.ResolvedConnectionConfig) {
const pool = pgwire.connectPgWirePool(config, {
idleTimeout: 30_000
idleTimeout: 30_000,
applicationName: getApplicationName()
});
return new PostgresRouteAPIAdapter(pool, config.tag, config);
}
Expand Down
4 changes: 3 additions & 1 deletion modules/module-postgres/src/auth/SupabaseKeyCollector.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import * as jose from 'jose';

import * as types from '../types/types.js';
import { AuthorizationError, ErrorCode } from '@powersync/lib-services-framework';
import { getApplicationName } from '../utils/application-name.js';

/**
* Fetches key from the Supabase database.
Expand All @@ -28,7 +29,8 @@ export class SupabaseKeyCollector implements auth.KeyCollector {
// limit to a single connection, and close the connection shortly
// after using it.
idleTimeout: 5_000,
maxSize: 1
maxSize: 1,
applicationName: getApplicationName()
});
}

Expand Down
7 changes: 5 additions & 2 deletions modules/module-postgres/src/module/PostgresModule.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ import * as types from '../types/types.js';
import { PostgresConnectionConfig } from '../types/types.js';
import { baseUri, NormalizedBasePostgresConnectionConfig } from '@powersync/lib-service-postgres';
import { ReplicationMetric } from '@powersync/service-types';
import { getApplicationName } from '../utils/application-name.js';

export class PostgresModule extends replication.ReplicationModule<types.PostgresConnectionConfig> {
constructor() {
Expand Down Expand Up @@ -88,7 +89,8 @@ export class PostgresModule extends replication.ReplicationModule<types.Postgres
const normalisedConfig = this.resolveConfig(this.decodedConfig!);
const connectionManager = new PgManager(normalisedConfig, {
idleTimeout: 30_000,
maxSize: 1
maxSize: 1,
applicationName: getApplicationName()
});

try {
Expand Down Expand Up @@ -144,7 +146,8 @@ export class PostgresModule extends replication.ReplicationModule<types.Postgres
// FIXME: This is not a complete implementation yet.
const connectionManager = new PgManager(normalizedConfig, {
idleTimeout: 30_000,
maxSize: 1
maxSize: 1,
applicationName: getApplicationName()
});
const connection = await connectionManager.snapshotConnection();
try {
Expand Down
5 changes: 3 additions & 2 deletions modules/module-postgres/src/replication/PgManager.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import * as pgwire from '@powersync/service-jpgwire';
import semver from 'semver';
import { NormalizedPostgresConnectionConfig } from '../types/types.js';
import { getApplicationName } from '../utils/application-name.js';

/**
* Shorter timeout for snapshot connections than for replication connections.
Expand Down Expand Up @@ -31,7 +32,7 @@ export class PgManager {
* Create a new replication connection.
*/
async replicationConnection(): Promise<pgwire.PgConnection> {
const p = pgwire.connectPgWire(this.options, { type: 'replication' });
const p = pgwire.connectPgWire(this.options, { type: 'replication', applicationName: getApplicationName() });
this.connectionPromises.push(p);
return await p;
}
Expand All @@ -51,7 +52,7 @@ export class PgManager {
* This connection must not be shared between multiple async contexts.
*/
async snapshotConnection(): Promise<pgwire.PgConnection> {
const p = pgwire.connectPgWire(this.options, { type: 'standard' });
const p = pgwire.connectPgWire(this.options, { type: 'standard', applicationName: getApplicationName() });
this.connectionPromises.push(p);
const connection = await p;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import { MissingReplicationSlotError, sendKeepAlive, WalStream } from './WalStre

import { replication } from '@powersync/service-core';
import { ConnectionManagerFactory } from './ConnectionManagerFactory.js';
import { getApplicationName } from '../utils/application-name.js';

export interface WalStreamReplicationJobOptions extends replication.AbstractReplicationJobOptions {
connectionFactory: ConnectionManagerFactory;
Expand All @@ -21,7 +22,8 @@ export class WalStreamReplicationJob extends replication.AbstractReplicationJob
this.connectionManager = this.connectionFactory.create({
// Pool connections are only used intermittently.
idleTimeout: 30_000,
maxSize: 2
maxSize: 2,
applicationName: getApplicationName()
});
}

Expand Down Expand Up @@ -87,7 +89,8 @@ export class WalStreamReplicationJob extends replication.AbstractReplicationJob
const connectionManager = this.connectionFactory.create({
// Pool connections are only used intermittently.
idleTimeout: 30_000,
maxSize: 2
maxSize: 2,
applicationName: getApplicationName()
});
try {
await this.rateLimiter?.waitUntilAllowed({ signal: this.abortController.signal });
Expand Down
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
import { replication, storage } from '@powersync/service-core';
import { PostgresModule } from '../module/PostgresModule.js';
import { getApplicationName } from '../utils/application-name.js';
import { ConnectionManagerFactory } from './ConnectionManagerFactory.js';
import { cleanUpReplicationSlot } from './replication-utils.js';
import { WalStreamReplicationJob } from './WalStreamReplicationJob.js';
import { PostgresModule } from '../module/PostgresModule.js';

export interface WalStreamReplicatorOptions extends replication.AbstractReplicatorOptions {
connectionFactory: ConnectionManagerFactory;
Expand All @@ -29,6 +30,7 @@ export class WalStreamReplicator extends replication.AbstractReplicator<WalStrea

async cleanUp(syncRulesStorage: storage.SyncRulesBucketStorage): Promise<void> {
const connectionManager = this.connectionFactory.create({
applicationName: getApplicationName(),
idleTimeout: 30_000,
maxSize: 1
});
Expand Down
8 changes: 8 additions & 0 deletions modules/module-postgres/src/utils/application-name.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
import { POWERSYNC_VERSION } from '@powersync/service-core';

/**
* application_name for PostgreSQL connections to the source database
*/
export function getApplicationName() {
return `powersync/${POWERSYNC_VERSION}`;
}
Loading