diff --git a/common/config/rush/pnpm-lock.yaml b/common/config/rush/pnpm-lock.yaml index e7c515284cb..1490088f3b3 100644 --- a/common/config/rush/pnpm-lock.yaml +++ b/common/config/rush/pnpm-lock.yaml @@ -35,11 +35,14 @@ importers: specifier: ^8.4.1 version: 8.4.1 '@hcengineering/measurements': - specifier: ^0.7.0 - version: 0.7.0 + specifier: ^0.7.6 + version: 0.7.6 '@hcengineering/measurements-otlp': - specifier: ^0.7.0 - version: 0.7.0(encoding@0.1.13) + specifier: ^0.7.6 + version: 0.7.6(encoding@0.1.13) + '@hcengineering/postgres-base': + specifier: ^0.7.6 + version: 0.7.6 '@hocuspocus/provider': specifier: ^2.15.2 version: 2.15.2(bufferutil@4.0.8)(utf-8-validate@6.0.4)(y-protocols@1.0.6(yjs@13.6.27))(yjs@13.6.27) @@ -919,9 +922,6 @@ importers: '@rush-temp/postgres': specifier: file:./projects/postgres.tgz version: file:projects/postgres.tgz(@babel/core@7.23.9)(@jest/types@29.6.3)(babel-jest@29.7.0(@babel/core@7.23.9))(esbuild@0.25.9)(ts-node@10.9.2(@swc/core@1.13.5)(@types/node@22.15.29)(typescript@5.8.3)) - '@rush-temp/postgres-base': - specifier: file:./projects/postgres-base.tgz - version: file:projects/postgres-base.tgz(@babel/core@7.23.9)(@jest/types@29.6.3)(babel-jest@29.7.0(@babel/core@7.23.9))(esbuild@0.25.9)(ts-node@10.9.2(@swc/core@1.13.5)(@types/node@22.15.29)(typescript@5.8.3)) '@rush-temp/preference': specifier: file:./projects/preference.tgz version: file:projects/preference.tgz(@babel/core@7.23.9)(@jest/types@29.6.3)(@types/node@22.15.29)(babel-jest@29.7.0(@babel/core@7.23.9))(esbuild@0.25.9)(ts-node@10.9.2(@swc/core@1.13.5)(@types/node@22.15.29)(typescript@5.8.3)) @@ -3015,11 +3015,14 @@ packages: engines: {node: '>=6'} hasBin: true - '@hcengineering/measurements-otlp@0.7.0': - resolution: {integrity: sha512-RTvUzeIKvCENCG1k8LOfBrC32GiF6fgPnqLRY5U7rXLZFISF+fLWXk1Kp7NtkbQXBUVj7C6jWfWb8psJKyIuKg==} + '@hcengineering/measurements-otlp@0.7.6': + resolution: {integrity: sha512-Mf578I2f2xAOVHgROdLtjv9TGY6NH1xvYCqzOGXMFpP8EblOE/FJT94GNYF/jh5V5xb/6sqRe0W+NDw+XYUc4A==} - '@hcengineering/measurements@0.7.0': - resolution: {integrity: sha512-RxXi7LsInAZNkDPxoS/CcGoEM8FuzYvd5xDyaKu0gQblNXrmMpePzpf4Mk9riJkeocQvzeZnduGSUPdbC6FVCw==} + '@hcengineering/measurements@0.7.6': + resolution: {integrity: sha512-uB4GmDvf5qgkcKc6hZO4iQXbtl7I6JKJrQ4qzyM86cQ6Z6eJqrGL5rBROi2V8U/SbM7wOyMO41ciDRBTZ68EIQ==} + + '@hcengineering/postgres-base@0.7.6': + resolution: {integrity: sha512-6Vc1uTkPYj3iZV2AwFlG6k0FyQ5kwMujV/INn7wM6EnjdcAZ90F313+69YnkEpbUT5xBc8PetDXyaRx+uTye1w==} '@hocuspocus/common@2.15.2': resolution: {integrity: sha512-wU1wxXNnQQMXyeL3mdSDYiQsm/r/QyJVjjQhF7sUBrLnjdsN7bA1cvfcSvJBr1ymrMSeYRmUL3UlQmEHEOaP7w==} @@ -4383,7 +4386,7 @@ packages: version: 0.0.0 '@rush-temp/analytics-service@file:projects/analytics-service.tgz': - resolution: {integrity: sha512-nV/D7Umpfl8EbdeTJPWFf6TsSGJQBVJm0nzIkugVbscJxRPFXbvVQXvxIwIrciJHUSfJvyn+8PUGb4Io+urU+w==, tarball: file:projects/analytics-service.tgz} + resolution: {integrity: sha512-UtRndXv25cD90t9awupy3D7rbE/HRzDqwOl8Kz4uWWUJA7OSGQgnaMufRVT1sNEwRMbK99FCu4LLReSUpgvz+g==, tarball: file:projects/analytics-service.tgz} version: 0.0.0 '@rush-temp/analytics@file:projects/analytics.tgz': @@ -4599,7 +4602,7 @@ packages: version: 0.0.0 '@rush-temp/core@file:projects/core.tgz': - resolution: {integrity: sha512-UiUM+J5QIY+nftppcxUvIbrGVnOOrj3uGdiuCky1bYV9rWAvCBEotJ3oEyYhUUsQiGQlZIi82pyfXC1be0Pqrw==, tarball: file:projects/core.tgz} + resolution: {integrity: sha512-l0qBuyNZPVCtmAy0F9XDwy71liMh1viprhGl2iWSv0a57eASARgMCJQJFlX52mNQfwuoMUOp5Zj8UIb60qaRig==, tarball: file:projects/core.tgz} version: 0.0.0 '@rush-temp/datalake@file:projects/datalake.tgz': @@ -5438,12 +5441,8 @@ packages: resolution: {integrity: sha512-PVCRFEuVLiXuqe9zfBTyrya9AHdBUHtmyJSHHGUEU50RnucQRjS/Qk9VttNjjIoH3I29ag/Afxh3IkhakWSpog==, tarball: file:projects/pod-workspace.tgz} version: 0.0.0 - '@rush-temp/postgres-base@file:projects/postgres-base.tgz': - resolution: {integrity: sha512-kYL6r5Fy6JA9Sr6GQbFtZ+fRqTRfOlea6uw0bsXyCpmYfpgE+fYX973W7vcQijPzNgLdjseAqgVoy/IIN0mrVA==, tarball: file:projects/postgres-base.tgz} - version: 0.0.0 - '@rush-temp/postgres@file:projects/postgres.tgz': - resolution: {integrity: sha512-Tr2Xo8ZrvqajOnk3M1XItkMjsO6a3bMyGoFvKn5ViTEe3wxpMBGHOf3xw4TO2ojNy3NVeV+QFdSOMwxtfADK+A==, tarball: file:projects/postgres.tgz} + resolution: {integrity: sha512-mEl9Xpp2yPUK6pBTmG4tI9fMR7tZUVSYA+Bzp0FDJ9s+M3wiSiNx6WaVeHSl/rv6u2y/PacGzMRkMSesp72iJw==, tarball: file:projects/postgres.tgz} version: 0.0.0 '@rush-temp/preference-assets@file:projects/preference-assets.tgz': @@ -15543,9 +15542,9 @@ snapshots: protobufjs: 7.5.3 yargs: 17.7.2 - '@hcengineering/measurements-otlp@0.7.0(encoding@0.1.13)': + '@hcengineering/measurements-otlp@0.7.6(encoding@0.1.13)': dependencies: - '@hcengineering/measurements': 0.7.0 + '@hcengineering/measurements': 0.7.6 '@opentelemetry/api': 1.9.0 '@opentelemetry/api-logs': 0.203.0 '@opentelemetry/auto-instrumentations-node': 0.62.0(@opentelemetry/api@1.9.0)(@opentelemetry/core@2.0.1(@opentelemetry/api@1.9.0))(encoding@0.1.13) @@ -15564,7 +15563,11 @@ snapshots: - encoding - supports-color - '@hcengineering/measurements@0.7.0': {} + '@hcengineering/measurements@0.7.6': {} + + '@hcengineering/postgres-base@0.7.6': + dependencies: + postgres: 3.4.7 '@hocuspocus/common@2.15.2': dependencies: @@ -17822,7 +17825,7 @@ snapshots: '@rush-temp/analytics-service@file:projects/analytics-service.tgz(@babel/core@7.23.9)(@jest/types@29.6.3)(@types/node@22.15.29)(babel-jest@29.7.0(@babel/core@7.23.9))(encoding@0.1.13)(esbuild@0.25.9)(ts-node@10.9.2(@swc/core@1.13.5)(@types/node@22.15.29)(typescript@5.8.3))': dependencies: - '@hcengineering/measurements-otlp': 0.7.0(encoding@0.1.13) + '@hcengineering/measurements-otlp': 0.7.6(encoding@0.1.13) '@types/jest': 29.5.12 '@typescript-eslint/eslint-plugin': 6.21.0(@typescript-eslint/parser@6.21.0(eslint@8.56.0)(typescript@5.8.3))(eslint@8.56.0)(typescript@5.8.3) '@typescript-eslint/parser': 6.21.0(eslint@8.56.0)(typescript@5.8.3) @@ -19454,7 +19457,7 @@ snapshots: '@rush-temp/core@file:projects/core.tgz(@babel/core@7.23.9)(@jest/types@29.6.3)(babel-jest@29.7.0(@babel/core@7.23.9))(esbuild@0.25.9)(ts-node@10.9.2(@swc/core@1.13.5)(@types/node@22.15.29)(typescript@5.8.3))': dependencies: - '@hcengineering/measurements': 0.7.0 + '@hcengineering/measurements': 0.7.6 '@types/jest': 29.5.12 '@types/node': 22.15.29 '@typescript-eslint/eslint-plugin': 6.21.0(@typescript-eslint/parser@6.21.0(eslint@8.56.0)(typescript@5.8.3))(eslint@8.56.0)(typescript@5.8.3) @@ -25757,34 +25760,9 @@ snapshots: - socks - supports-color - '@rush-temp/postgres-base@file:projects/postgres-base.tgz(@babel/core@7.23.9)(@jest/types@29.6.3)(babel-jest@29.7.0(@babel/core@7.23.9))(esbuild@0.25.9)(ts-node@10.9.2(@swc/core@1.13.5)(@types/node@22.15.29)(typescript@5.8.3))': - dependencies: - '@types/jest': 29.5.12 - '@types/node': 22.15.29 - '@typescript-eslint/eslint-plugin': 6.21.0(@typescript-eslint/parser@6.21.0(eslint@8.56.0)(typescript@5.8.3))(eslint@8.56.0)(typescript@5.8.3) - '@typescript-eslint/parser': 6.21.0(eslint@8.56.0)(typescript@5.8.3) - eslint: 8.56.0 - eslint-config-standard-with-typescript: 40.0.0(@typescript-eslint/eslint-plugin@6.21.0(@typescript-eslint/parser@6.21.0(eslint@8.56.0)(typescript@5.8.3))(eslint@8.56.0)(typescript@5.8.3))(eslint-plugin-import@2.29.1(eslint@8.56.0))(eslint-plugin-n@15.7.0(eslint@8.56.0))(eslint-plugin-promise@6.1.1(eslint@8.56.0))(eslint@8.56.0)(typescript@5.8.3) - eslint-plugin-import: 2.29.1(eslint@8.56.0) - eslint-plugin-n: 15.7.0(eslint@8.56.0) - eslint-plugin-promise: 6.1.1(eslint@8.56.0) - jest: 29.7.0(@types/node@22.15.29)(ts-node@10.9.2(@swc/core@1.13.5)(@types/node@22.15.29)(typescript@5.8.3)) - postgres: 3.4.7 - prettier: 3.2.5 - ts-jest: 29.1.2(@babel/core@7.23.9)(@jest/types@29.6.3)(babel-jest@29.7.0(@babel/core@7.23.9))(esbuild@0.25.9)(jest@29.7.0(@types/node@22.15.29)(ts-node@10.9.2(@swc/core@1.13.5)(@types/node@22.15.29)(typescript@5.8.3)))(typescript@5.8.3) - typescript: 5.8.3 - transitivePeerDependencies: - - '@babel/core' - - '@jest/types' - - babel-jest - - babel-plugin-macros - - esbuild - - node-notifier - - supports-color - - ts-node - '@rush-temp/postgres@file:projects/postgres.tgz(@babel/core@7.23.9)(@jest/types@29.6.3)(babel-jest@29.7.0(@babel/core@7.23.9))(esbuild@0.25.9)(ts-node@10.9.2(@swc/core@1.13.5)(@types/node@22.15.29)(typescript@5.8.3))': dependencies: + '@hcengineering/postgres-base': 0.7.6 '@types/jest': 29.5.12 '@types/node': 22.15.29 '@typescript-eslint/eslint-plugin': 6.21.0(@typescript-eslint/parser@6.21.0(eslint@8.56.0)(typescript@5.8.3))(eslint@8.56.0)(typescript@5.8.3) diff --git a/packages/analytics-service/package.json b/packages/analytics-service/package.json index 4365990e98a..34210374e6b 100644 --- a/packages/analytics-service/package.json +++ b/packages/analytics-service/package.json @@ -41,7 +41,7 @@ "@hcengineering/platform": "^0.6.11", "@hcengineering/core": "^0.6.32", "@hcengineering/analytics": "^0.6.0", - "@hcengineering/measurements-otlp": "^0.7.0", + "@hcengineering/measurements-otlp": "^0.7.6", "winston": "^3.11.0", "winston-daily-rotate-file": "^5.0.0" }, diff --git a/packages/core/package.json b/packages/core/package.json index 4e952538228..777ebe43e47 100644 --- a/packages/core/package.json +++ b/packages/core/package.json @@ -41,7 +41,7 @@ "dependencies": { "@hcengineering/platform": "^0.6.11", "@hcengineering/analytics": "^0.6.0", - "@hcengineering/measurements": "^0.7.0", + "@hcengineering/measurements": "^0.7.6", "fast-equals": "^5.2.2" }, "repository": "https://github.com/hcengineering/platform", diff --git a/rush.json b/rush.json index 3eb8561d4aa..93d57c1deae 100644 --- a/rush.json +++ b/rush.json @@ -835,11 +835,6 @@ "projectFolder": "server/postgres", "shouldPublish": false }, - { - "packageName": "@hcengineering/postgres-base", - "projectFolder": "server/postgres-base", - "shouldPublish": false - }, { "packageName": "@hcengineering/elastic", "projectFolder": "server/elastic", diff --git a/server/postgres-base/.eslintrc.js b/server/postgres-base/.eslintrc.js deleted file mode 100644 index ce90fb9646f..00000000000 --- a/server/postgres-base/.eslintrc.js +++ /dev/null @@ -1,7 +0,0 @@ -module.exports = { - extends: ['./node_modules/@hcengineering/platform-rig/profiles/node/eslint.config.json'], - parserOptions: { - tsconfigRootDir: __dirname, - project: './tsconfig.json' - } -} diff --git a/server/postgres-base/.npmignore b/server/postgres-base/.npmignore deleted file mode 100644 index e3ec093c383..00000000000 --- a/server/postgres-base/.npmignore +++ /dev/null @@ -1,4 +0,0 @@ -* -!/lib/** -!CHANGELOG.md -/lib/**/__tests__/ diff --git a/server/postgres-base/config/rig.json b/server/postgres-base/config/rig.json deleted file mode 100644 index 78cc5a17334..00000000000 --- a/server/postgres-base/config/rig.json +++ /dev/null @@ -1,5 +0,0 @@ -{ - "$schema": "https://developer.microsoft.com/json-schemas/rig-package/rig.schema.json", - "rigPackageName": "@hcengineering/platform-rig", - "rigProfile": "node" -} diff --git a/server/postgres-base/jest.config.js b/server/postgres-base/jest.config.js deleted file mode 100644 index 2cfd408b679..00000000000 --- a/server/postgres-base/jest.config.js +++ /dev/null @@ -1,7 +0,0 @@ -module.exports = { - preset: 'ts-jest', - testEnvironment: 'node', - testMatch: ['**/?(*.)+(spec|test).[jt]s?(x)'], - roots: ["./src"], - coverageReporters: ["text-summary", "html"] -} diff --git a/server/postgres-base/package.json b/server/postgres-base/package.json deleted file mode 100644 index c150c3d35b2..00000000000 --- a/server/postgres-base/package.json +++ /dev/null @@ -1,39 +0,0 @@ -{ - "name": "@hcengineering/postgres-base", - "version": "0.6.0", - "main": "lib/index.js", - "svelte": "src/index.ts", - "types": "types/index.d.ts", - "author": "Copyright © Hardcore Engineering Inc.", - "template": "@hcengineering/node-package", - "license": "EPL-2.0", - "scripts": { - "build": "compile", - "build:watch": "compile", - "test": "jest --passWithNoTests --silent --forceExit", - "format": "format src", - "_phase:build": "compile transpile src", - "_phase:test": "jest --passWithNoTests --silent --forceExit", - "_phase:format": "format src", - "_phase:validate": "compile validate" - }, - "devDependencies": { - "@hcengineering/platform-rig": "^0.6.0", - "@typescript-eslint/eslint-plugin": "^6.11.0", - "eslint-plugin-import": "^2.26.0", - "eslint-plugin-promise": "^6.1.1", - "eslint-plugin-n": "^15.4.0", - "eslint": "^8.54.0", - "@typescript-eslint/parser": "^6.11.0", - "eslint-config-standard-with-typescript": "^40.0.0", - "prettier": "^3.1.0", - "typescript": "^5.8.3", - "jest": "^29.7.0", - "ts-jest": "^29.1.1", - "@types/jest": "^29.5.5", - "@types/node": "^22.15.29" - }, - "dependencies": { - "postgres": "^3.4.7" - } -} diff --git a/server/postgres-base/src/index.ts b/server/postgres-base/src/index.ts deleted file mode 100644 index 4f820c16be6..00000000000 --- a/server/postgres-base/src/index.ts +++ /dev/null @@ -1,439 +0,0 @@ -// -// Copyright © 2025 Hardcore Engineering Inc. -// -// Licensed under the Eclipse Public License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. You may -// obtain a copy of the License at https://www.eclipse.org/legal/epl-2.0 -// -// 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. -// - -import postgres, { type Options, type ParameterOrJSON } from 'postgres' - -const clientRefs = new Map() - -let clId = 0 - -export type DBResult = any[] & { count: number } -export interface DBClient { - execute: (query: string, parameters?: ParameterOrJSON[] | undefined) => Promise - - release: () => void - - reserve: () => Promise - - raw: () => postgres.Sql -} - -export function createDBClient (client: postgres.Sql, release: () => void = () => {}): DBClient { - return { - execute: (query, parameters) => - client.unsafe(query, doFetchTypes ? parameters : convertArrayParams(parameters), getPrepare()), - release, - reserve: async () => { - const reserved = await client.reserve() - return createDBClient(reserved, () => { - reserved.release() - }) - }, - raw: () => client - } -} - -export function convertArrayParams (params?: unknown[]): any[] | undefined { - if (params === undefined) return undefined - - return params.map((param) => { - if (!Array.isArray(param)) return param - - if (param.length === 0) return '{}' - - const sanitized = param.map((item) => { - if (item === null || item === undefined) return 'NULL' - - if (typeof item === 'number' || typeof item === 'boolean') { - return String(item) - } - - if (typeof item === 'string') { - const escaped = item.replace(/\\/g, '\\\\').replace(/"/g, '\\"') - return `"${escaped}"` - } - - const json = JSON.stringify(item) - const escapedJson = json.replace(/\\/g, '\\\\').replace(/"/g, '\\"') - return `"${escapedJson}"` - }) - - return `{${sanitized.join(',')}}` - }) -} - -export async function retryTxn ( - pool: postgres.Sql, - operation: (client: postgres.TransactionSql) => Promise -): Promise { - await pool.begin(async (client) => { - const result = await operation(client) - return result - }) -} - -/** - * @public - */ -export async function shutdownPostgres (): Promise { - for (const c of connections.values()) { - c.close(true) - } - connections.clear() -} - -export interface PostgresClientReference { - getClient: () => Promise - - mgr: ConnectionMgr - close: () => void - url: () => string -} - -class PostgresClientReferenceImpl { - count: number - client: postgres.Sql - - mgr: ConnectionMgr - - constructor ( - readonly connectionString: string, - client: postgres.Sql, - readonly onclose: () => void - ) { - this.count = 0 - this.client = client - this.mgr = new ConnectionMgr(createDBClient(this.client)) - } - - url (): string { - return this.connectionString - } - - getClient (): postgres.Sql { - return this.client - } - - close (force: boolean = false): void { - this.count-- - if (this.count === 0 || force) { - if (force) { - this.count = 0 - } - void (async () => { - this.mgr.close() - this.onclose() - const cl = this.client - await cl.end({ timeout: 1 }) - })() - } - } - - addRef (): void { - this.count++ - } -} -export class ClientRef implements PostgresClientReference { - id = ++clId - constructor ( - readonly client: PostgresClientReferenceImpl, - readonly mgr: ConnectionMgr - ) { - clientRefs.set(this.id, this) - } - - url (): string { - return this.client.url() - } - - closed = false - async getClient (): Promise { - if (!this.closed) { - return this.client.getClient() - } else { - throw Error('DB client is already closed') - } - } - - close (): void { - // Do not allow double close of mongo connection client - if (!this.closed) { - clientRefs.delete(this.id) - this.closed = true - this.client.close() - } - } -} - -export let dbExtraOptions: Partial> = {} -export function setDBExtraOptions (options: Partial>): void { - dbExtraOptions = options -} - -export function getPrepare (): { prepare: boolean } { - return { prepare: dbExtraOptions.prepare ?? false } -} - -export const doFetchTypes = true - -const connections = new Map() - -/** - * Initialize a connection to DB - * @public - */ -export function getDBClient ( - connectionString: string, - database?: string, - serviceName: string = 'transactor' -): PostgresClientReference { - const extraOptions = JSON.parse(process.env.POSTGRES_OPTIONS ?? '{}') - const key = `${connectionString}${extraOptions}` - - let existing = connections.get(key) - - if (existing === undefined) { - const sql = postgres(connectionString, { - connection: { - application_name: serviceName - }, - database, - max: 10, - min: 2, - connect_timeout: 30, - idle_timeout: 0, - transform: { - undefined: null - }, - debug: false, - notice: false, - onnotice (notice) {}, - onparameter (key, value) {}, - ...dbExtraOptions, - ...extraOptions, - fetch_types: doFetchTypes - }) - - existing = new PostgresClientReferenceImpl(connectionString, sql, () => { - connections.delete(key) - }) - connections.set(key, existing) - } - // Add reference and return once closable - existing.addRef() - return new ClientRef(existing, existing.mgr) -} - -class ConnectionInfo { - // It should preserve at least one available connection in pool, other connection should be closed - available: DBClient[] = [] - - released: boolean = false - - constructor ( - readonly connectionId: string, - protected readonly client: DBClient, - readonly managed: boolean, - readonly mgrId: string - ) {} - - async withReserve (action: (reservedClient: DBClient) => Promise, forced: boolean = false): Promise { - let reserved: DBClient | undefined - - // Check if we have at least one available connection and reserve one more if required. - if (this.available.length === 0) { - if (this.managed || forced) { - reserved = await this.client.reserve() - } - } else { - reserved = this.available.shift() as DBClient - } - - try { - // Use reserved or pool - return await action(reserved ?? this.client) - } catch (err: any) { - console.error(err) - throw err - } finally { - if (this.released) { - try { - reserved?.release() - } catch (err: any) { - console.error('failed to release', err) - } - } else if (reserved !== undefined) { - if (this.available.length > 0) { - reserved?.release() - } else { - this.available.push(reserved) - } - } - } - } - - release (): void { - for (const c of [...this.available]) { - c.release() - } - this.available = [] - } -} - -export class ConnectionMgr { - private readonly connections = new Map() - constructor (protected readonly client: DBClient) {} - - async write (id: string | undefined, mgrId: string, fn: (client: DBClient) => Promise): Promise { - const backoffInterval = 25 // millis - const maxTries = 5 - let tries = 0 - - const realId = id ?? `${++clId}` - - const connection = this.getConnection(realId, mgrId, false) - - try { - while (true) { - const retry: boolean | Error = await connection.withReserve(async (client) => { - tries++ - try { - await client.execute('BEGIN;') - await fn(client) - await client.execute('COMMIT;') - return true - } catch (err: any) { - await client.execute('ROLLBACK;') - console.error({ message: 'failed to process tx', error: err.message, cause: err }) - - if (!this.isRetryableError(err) || tries === maxTries) { - return err - } else { - console.log('Transaction failed. Retrying.') - console.log(err.message) - return false - } - } - }, true) - if (retry === true) { - break - } - if (retry instanceof Error) { - // Pass it to exit - throw retry - } - // Retry for a timeout - await new Promise((resolve) => setTimeout(resolve, backoffInterval)) - } - } finally { - if (!connection.managed) { - // We need to relase in case it temporaty connection was used - connection.release() - } - } - } - - async retry (id: string | undefined, mgrId: string, fn: (client: DBClient) => Promise): Promise { - const backoffInterval = 25 // millis - const maxTries = 5 - let tries = 0 - - const realId = id ?? `${++clId}` - // Will reuse reserved if had and use new one if not - const connection = this.getConnection(realId, mgrId, false) - - try { - while (true) { - const retry: false | { result: any } | Error = await connection.withReserve(async (client) => { - tries++ - try { - return { result: await fn(client) } - } catch (err: any) { - console.error({ message: 'failed to process sql', error: err.message, cause: err }) - if (!this.isRetryableError(err) || tries === maxTries) { - return err - } else { - console.log('Read Transaction failed. Retrying.') - console.log(err.message) - return false - } - } - }) - if (retry instanceof Error) { - // Pass it to exit - throw retry - } - if (retry === false) { - // Retry for a timeout - await new Promise((resolve) => setTimeout(resolve, backoffInterval)) - continue - } - return retry.result - } - } finally { - if (!connection.managed) { - // We need to relase in case it temporaty connection was used - connection.release() - } - } - } - - release (id: string): void { - const conn = this.connections.get(id) - if (conn !== undefined) { - conn.released = true - this.connections.delete(id) // We need to delete first - conn.release() - } - } - - close (mgrId?: string): void { - const cnts = this.connections - for (const [k, conn] of Array.from(cnts.entries())) { - if (mgrId !== undefined && conn.mgrId !== mgrId) { - continue - } - cnts.delete(k) - try { - conn.release() - } catch (err: any) { - console.error('failed to release connection') - } - } - } - - getConnection (id: string, mgrId: string, managed: boolean = true): ConnectionInfo { - let conn = this.connections.get(id) - if (conn === undefined) { - conn = new ConnectionInfo(id, this.client, managed, mgrId) - } - if (managed) { - this.connections.set(id, conn) - } - return conn - } - - private isRetryableError (err: any): boolean { - const msg: string = err?.message ?? '' - - return ( - err.code === '40001' || // Retry transaction - err.code === '55P03' || // Lock not available - err.code === 'CONNECTION_CLOSED' || // This error is thrown if the connection was closed without an error. - err.code === 'CONNECTION_DESTROYED' || // This error is thrown for any queries that were pending when the timeout to sql.end({ timeout: X }) was reached. If the DB client is being closed completely retry will result in CONNECTION_ENDED which is not retried so should be fine. - msg.includes('RETRY_SERIALIZABLE') - ) - } -} diff --git a/server/postgres-base/tsconfig.json b/server/postgres-base/tsconfig.json deleted file mode 100644 index c6a877cf6c3..00000000000 --- a/server/postgres-base/tsconfig.json +++ /dev/null @@ -1,12 +0,0 @@ -{ - "extends": "./node_modules/@hcengineering/platform-rig/profiles/node/tsconfig.json", - - "compilerOptions": { - "rootDir": "./src", - "outDir": "./lib", - "declarationDir": "./types", - "tsBuildInfoFile": ".build/build.tsbuildinfo" - }, - "include": ["src/**/*"], - "exclude": ["node_modules", "lib", "dist", "types", "bundle"] -} \ No newline at end of file diff --git a/server/postgres/package.json b/server/postgres/package.json index d09b44fb3f2..53a42aec5ad 100644 --- a/server/postgres/package.json +++ b/server/postgres/package.json @@ -38,6 +38,6 @@ "@hcengineering/core": "^0.6.32", "@hcengineering/platform": "^0.6.11", "@hcengineering/server-core": "^0.6.1", - "@hcengineering/postgres-base": "^0.6.0" + "@hcengineering/postgres-base": "^0.7.6" } }