mirror of
https://github.com/hcengineering/platform.git
synced 2026-09-12 04:37:44 +02:00
remove mutex (#7056)
Signed-off-by: Denis Bykhov <bykhov.denis@gmail.com>
This commit is contained in:
@@ -79,7 +79,6 @@ import {
|
||||
isDataField,
|
||||
isOwner,
|
||||
type JoinProps,
|
||||
Mutex,
|
||||
parseDoc,
|
||||
parseDocWithProjection,
|
||||
parseUpdate,
|
||||
@@ -90,16 +89,36 @@ import {
|
||||
abstract class PostgresAdapterBase implements DbAdapter {
|
||||
protected readonly _helper: DBCollectionHelper
|
||||
protected readonly tableFields = new Map<string, string[]>()
|
||||
protected readonly mutex = new Mutex()
|
||||
protected readonly connections = new Map<string, postgres.ReservedSql>()
|
||||
protected readonly connections = new Map<string, postgres.ReservedSql | Promise<postgres.ReservedSql>>()
|
||||
|
||||
protected readonly retryTxn = async (
|
||||
connection: postgres.ReservedSql,
|
||||
client: postgres.ReservedSql,
|
||||
fn: (client: postgres.ReservedSql) => Promise<any>
|
||||
): Promise<void> => {
|
||||
await this.mutex.runExclusive(async () => {
|
||||
await this.processOps(connection, fn)
|
||||
})
|
||||
const backoffInterval = 100 // millis
|
||||
const maxTries = 5
|
||||
let tries = 0
|
||||
|
||||
while (true) {
|
||||
await client.unsafe('BEGIN;')
|
||||
tries++
|
||||
|
||||
try {
|
||||
const result = await fn(client)
|
||||
await client.unsafe('COMMIT;')
|
||||
return result
|
||||
} catch (err: any) {
|
||||
await client.unsafe('ROLLBACK;')
|
||||
|
||||
if (err.code !== '40001' || tries === maxTries) {
|
||||
throw err
|
||||
} else {
|
||||
console.log('Transaction failed. Retrying.')
|
||||
console.log(err.message)
|
||||
await new Promise((resolve) => setTimeout(resolve, tries * backoffInterval))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
constructor (
|
||||
@@ -133,7 +152,11 @@ abstract class PostgresAdapterBase implements DbAdapter {
|
||||
if (ctx.id === undefined) return
|
||||
const conn = this.connections.get(ctx.id)
|
||||
if (conn !== undefined) {
|
||||
conn.release()
|
||||
if (conn instanceof Promise) {
|
||||
;(await conn).release()
|
||||
} else {
|
||||
conn.release()
|
||||
}
|
||||
this.connections.delete(ctx.id)
|
||||
}
|
||||
}
|
||||
@@ -141,40 +164,10 @@ abstract class PostgresAdapterBase implements DbAdapter {
|
||||
protected async getConnection (ctx: MeasureContext): Promise<postgres.ReservedSql | undefined> {
|
||||
if (ctx.id === undefined) return
|
||||
const conn = this.connections.get(ctx.id)
|
||||
if (conn !== undefined) return conn
|
||||
const client = await this.client.reserve()
|
||||
if (conn !== undefined) return await conn
|
||||
const client = this.client.reserve()
|
||||
this.connections.set(ctx.id, client)
|
||||
return client
|
||||
}
|
||||
|
||||
private async processOps (
|
||||
client: postgres.ReservedSql,
|
||||
operation: (client: postgres.ReservedSql) => Promise<any>
|
||||
): Promise<void> {
|
||||
const backoffInterval = 100 // millis
|
||||
const maxTries = 5
|
||||
let tries = 0
|
||||
|
||||
while (true) {
|
||||
await client.unsafe('BEGIN;')
|
||||
tries++
|
||||
|
||||
try {
|
||||
const result = await operation(client)
|
||||
await client.unsafe('COMMIT;')
|
||||
return result
|
||||
} catch (err: any) {
|
||||
await client.unsafe('ROLLBACK;')
|
||||
|
||||
if (err.code !== '40001' || tries === maxTries) {
|
||||
throw err
|
||||
} else {
|
||||
console.log('Transaction failed. Retrying.')
|
||||
console.log(err.message)
|
||||
await new Promise((resolve) => setTimeout(resolve, tries * backoffInterval))
|
||||
}
|
||||
}
|
||||
}
|
||||
return await client
|
||||
}
|
||||
|
||||
async traverse<T extends Doc>(
|
||||
@@ -239,9 +232,13 @@ abstract class PostgresAdapterBase implements DbAdapter {
|
||||
abstract init (): Promise<void>
|
||||
|
||||
async close (): Promise<void> {
|
||||
this.connections.forEach((c) => {
|
||||
c.release()
|
||||
})
|
||||
for (const c of this.connections.values()) {
|
||||
if (c instanceof Promise) {
|
||||
;(await c).release()
|
||||
} else {
|
||||
c.release()
|
||||
}
|
||||
}
|
||||
this.refClient.close()
|
||||
}
|
||||
|
||||
@@ -1495,7 +1492,7 @@ class PostgresAdapter extends PostgresAdapterBase {
|
||||
return { object }
|
||||
}
|
||||
} catch (err) {
|
||||
console.error(err)
|
||||
console.error(err, { tx, params, updates })
|
||||
}
|
||||
})
|
||||
return {}
|
||||
|
||||
@@ -241,6 +241,10 @@ export function inferType (val: any): string {
|
||||
if (typeof val === 'boolean') {
|
||||
return '::boolean'
|
||||
}
|
||||
if (Array.isArray(val)) {
|
||||
const type = inferType(val[0])
|
||||
return type + '[]'
|
||||
}
|
||||
return ''
|
||||
}
|
||||
|
||||
@@ -409,38 +413,3 @@ export interface JoinProps {
|
||||
toClass: Ref<Class<Doc>>
|
||||
classes?: Ref<Class<Doc>>[] // filter by classes
|
||||
}
|
||||
|
||||
export class Mutex {
|
||||
private locked: boolean = false
|
||||
private readonly waitingQueue: Array<(value: boolean) => void> = []
|
||||
|
||||
private async acquire (): Promise<void> {
|
||||
while (this.locked) {
|
||||
await new Promise<boolean>((resolve) => {
|
||||
this.waitingQueue.push(resolve)
|
||||
})
|
||||
}
|
||||
this.locked = true
|
||||
}
|
||||
|
||||
private release (): void {
|
||||
if (!this.locked) {
|
||||
throw new Error('Mutex is not locked')
|
||||
}
|
||||
|
||||
this.locked = false
|
||||
const nextResolver = this.waitingQueue.shift()
|
||||
if (nextResolver !== undefined) {
|
||||
nextResolver(true)
|
||||
}
|
||||
}
|
||||
|
||||
async runExclusive<T>(fn: () => Promise<T> | T): Promise<T> {
|
||||
await this.acquire()
|
||||
try {
|
||||
return await fn()
|
||||
} finally {
|
||||
this.release()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user