Skip to content

Commit 4cd4bd9

Browse files
authored
fix: pool settings improvements (#347)
1 parent 658d583 commit 4cd4bd9

File tree

9 files changed

+652
-1484
lines changed

9 files changed

+652
-1484
lines changed

package-lock.json

Lines changed: 611 additions & 1434 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -25,10 +25,10 @@
2525
"node": ">= 14.0.0"
2626
},
2727
"dependencies": {
28-
"@aws-sdk/client-s3": "^3.350.0",
29-
"@aws-sdk/lib-storage": "^3.182.0",
30-
"@aws-sdk/node-http-handler": "^3.178.0",
31-
"@aws-sdk/s3-request-presigner": "^3.182.0",
28+
"@aws-sdk/client-s3": "3.350.0",
29+
"@aws-sdk/lib-storage": "3.350.0",
30+
"@aws-sdk/node-http-handler": "3.350.0",
31+
"@aws-sdk/s3-request-presigner": "3.350.0",
3232
"@fastify/multipart": "^7.6.0",
3333
"@fastify/rate-limit": "^7.6.0",
3434
"@fastify/swagger": "^8.3.1",

src/config.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -119,7 +119,7 @@ export function getConfig(): StorageConfigType {
119119
10
120120
),
121121
databaseConnectionTimeout: parseInt(
122-
getOptionalConfigFromEnv('DATABASE_CONNECTION_TIMEOUT') || '30000',
122+
getOptionalConfigFromEnv('DATABASE_CONNECTION_TIMEOUT') || '3000',
123123
10
124124
),
125125
region: getConfigFromEnv('REGION'),

src/database/connection.ts

Lines changed: 19 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,6 @@ import pg from 'pg'
22
import { Knex, knex } from 'knex'
33
import { JwtPayload } from 'jsonwebtoken'
44
import { getConfig } from '../config'
5-
import { logger } from '../monitoring'
65
import { DbActiveConnection, DbActivePool } from '../monitoring/metrics'
76

87
// https://github.com/knex/knex/issues/387#issuecomment-51554522
@@ -30,12 +29,13 @@ export interface User {
3029
}
3130

3231
export const connections = new Map<string, Knex>()
32+
const searchPath = ['storage', 'public', 'extensions']
3333

3434
export class TenantConnection {
3535
public readonly role: string
3636

3737
protected constructor(
38-
public readonly pool: Knex,
38+
protected readonly pool: Knex,
3939
protected readonly options: TenantConnectionOptions
4040
) {
4141
this.role = options.user.payload.role || 'anon'
@@ -56,20 +56,19 @@ export class TenantConnection {
5656
let knexPool = connections.get(connectionString)
5757

5858
if (knexPool) {
59-
return new this(await knexPool, options)
59+
return new this(knexPool, options)
6060
}
6161

6262
const isExternalPool = Boolean(options.isExternalPool)
6363

6464
knexPool = knex({
6565
client: 'pg',
66-
searchPath: ['public', 'storage', 'extensions'],
66+
searchPath: isExternalPool ? undefined : searchPath,
6767
pool: {
6868
min: 0,
6969
max: isExternalPool ? 1 : options.maxConnections || databaseMaxConnections,
70-
propagateCreateError: false,
7170
acquireTimeoutMillis: databaseConnectionTimeout,
72-
idleTimeoutMillis: isExternalPool ? 100 : undefined,
71+
idleTimeoutMillis: isExternalPool ? 100 : databaseFreePoolAfterInactivity,
7372
reapIntervalMillis: isExternalPool ? 110 : undefined,
7473
},
7574
connection: connectionString,
@@ -97,38 +96,12 @@ export class TenantConnection {
9796
})
9897

9998
if (!isExternalPool) {
100-
let freePoolIntervalFn: NodeJS.Timeout | undefined
101-
10299
knexPool.client.pool.on('poolDestroySuccess', () => {
103-
if (freePoolIntervalFn) {
104-
clearTimeout(freePoolIntervalFn)
105-
}
106-
107100
if (connections.get(connectionString) === knexPool) {
108101
connections.delete(connectionString)
109102
}
110103
})
111104

112-
knexPool.client.pool.on('stopReaping', () => {
113-
if (freePoolIntervalFn) {
114-
clearTimeout(freePoolIntervalFn)
115-
}
116-
117-
freePoolIntervalFn = setTimeout(async () => {
118-
connections.delete(connectionString)
119-
knexPool?.destroy().catch((e) => {
120-
logger.error(e, 'Error destroying pool')
121-
})
122-
clearTimeout(freePoolIntervalFn)
123-
}, databaseFreePoolAfterInactivity)
124-
})
125-
126-
knexPool.client.pool.on('startReaping', () => {
127-
if (freePoolIntervalFn) {
128-
clearTimeout(freePoolIntervalFn)
129-
freePoolIntervalFn = undefined
130-
}
131-
})
132105
connections.set(connectionString, knexPool)
133106
}
134107

@@ -141,10 +114,20 @@ export class TenantConnection {
141114
}
142115
}
143116

144-
transaction(isolation?: Knex.IsolationLevels, instance?: Knex) {
145-
return (instance || this.pool).transactionProvider({
146-
isolationLevel: isolation,
147-
})
117+
async transaction(instance?: Knex) {
118+
const pool = instance || this.pool
119+
const tnx = await pool.transaction()
120+
121+
if (!instance && this.options.isExternalPool) {
122+
await tnx.raw(`SELECT set_config('search_path', ?, true)`, [searchPath.join(', ')])
123+
}
124+
return tnx
125+
}
126+
127+
transactionProvider(instance?: Knex): Knex.TransactionProvider {
128+
return async () => {
129+
return this.transaction(instance)
130+
}
148131
}
149132

150133
asSuperUser() {

src/storage/database/knex.ts

Lines changed: 2 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -42,10 +42,7 @@ export class StorageKnexDB implements Database {
4242

4343
while (retryLeft > 0) {
4444
try {
45-
const tnx = await this.connection.transaction(
46-
transactionOptions?.isolation as Knex.IsolationLevels,
47-
this.options.tnx
48-
)()
45+
const tnx = await this.connection.transactionProvider(this.options.tnx)()
4946

5047
try {
5148
await this.connection.setScope(tnx)
@@ -475,7 +472,7 @@ export class StorageKnexDB implements Database {
475472
const needsNewTransaction = !tnx || differentScopes
476473

477474
if (!tnx || needsNewTransaction) {
478-
tnx = await this.connection.transaction(isolation, this.options.tnx)()
475+
tnx = await this.connection.transactionProvider(this.options.tnx)()
479476
tnx.on('query-error', (error: DatabaseError) => {
480477
throw DBError.fromDBError(error)
481478
})

src/test/object.test.ts

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,11 +12,13 @@ import { StorageBackendError } from '../storage'
1212
import { useMockObject, useMockQueue } from './common'
1313
import { getPostgresConnection } from '../database'
1414
import { getServiceKeyUser } from '../database/tenant'
15+
import { Knex } from 'knex'
1516

1617
dotenv.config({ path: '.env.test' })
1718

1819
const { anonKey, jwtSecret, serviceKey, tenantId } = getConfig()
1920

21+
let tnx: Knex.Transaction | undefined
2022
async function getSuperuserPostgrestClient() {
2123
const superUser = await getServiceKeyUser(tenantId)
2224

@@ -26,14 +28,20 @@ async function getSuperuserPostgrestClient() {
2628
tenantId,
2729
host: 'localhost',
2830
})
29-
const tnx = await conn.pool
31+
tnx = await conn.transaction()
3032

3133
return tnx
3234
}
3335

3436
useMockObject()
3537
useMockQueue()
3638

39+
afterEach(async () => {
40+
if (tnx) {
41+
await tnx.commit()
42+
}
43+
})
44+
3745
/*
3846
* GET /object/:id
3947
*/

src/test/rls.test.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -132,7 +132,7 @@ describe('RLS policies', () => {
132132

133133
afterAll(async () => {
134134
await db.destroy()
135-
await (storage.db as StorageKnexDB).connection.pool.destroy()
135+
await (storage.db as StorageKnexDB).connection.dispose()
136136
})
137137

138138
testSpec.tests.forEach((_test, index) => {

src/test/webhooks.test.ts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -294,7 +294,7 @@ describe('Webhooks', () => {
294294

295295
async function createObject(pg: TenantConnection, bucketId: string) {
296296
const objectName = Date.now()
297-
const tnx = pg.pool
297+
const tnx = await pg.transaction()
298298

299299
const [data] = await tnx
300300
.from<Obj>('objects')
@@ -316,5 +316,7 @@ async function createObject(pg: TenantConnection, bucketId: string) {
316316
])
317317
.returning('*')
318318

319+
await tnx.commit()
320+
319321
return data as Obj
320322
}

tsconfig.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,8 @@
1212
"target": "ES2020",
1313
"outDir": "dist",
1414
"sourceMap": true,
15-
"strict": true
15+
"strict": true,
16+
"skipLibCheck": true
1617
},
1718
"include": ["./src/**/*"],
1819
"exclude": ["node_modules", "dist"],

0 commit comments

Comments
 (0)