remove mutex (#7056)

Signed-off-by: Denis Bykhov <bykhov.denis@gmail.com>
This commit is contained in:
Denis Bykhov
2024-10-30 20:51:18 +07:00
committed by GitHub
parent 1b7d559180
commit e4bf53ad1b
8 changed files with 75 additions and 88 deletions
+42 -45
View File
@@ -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 {}
+4 -35
View File
@@ -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()
}
}
}