Skip to content

Commit b89d5f3

Browse files
authored
feat: [UIE-9384] - Add Connection Pool types/endpoints/queries (#13148)
## Description 📝 Add Connection Pool types, endpoints, queries, validation for the upcoming PG Bouncer work ## How to test 🧪 ### Verification steps (How to verify changes) - [ ] Nothing to test in the UI yet, so we just need to make sure the types match the API spec and that the logic looks good
1 parent 23aa45d commit b89d5f3

File tree

9 files changed

+223
-0
lines changed

9 files changed

+223
-0
lines changed
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
"@linode/api-v4": Upcoming Features
3+
---
4+
5+
Added Database Connection Pool types and endpoints ([#13148](https://github.com/linode/manager/pull/13148))

packages/api-v4/src/databases/databases.ts

Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
import {
2+
createDatabaseConnectionPoolSchema,
23
createDatabaseSchema,
4+
updateDatabaseConnectionPoolSchema,
35
updateDatabaseSchema,
46
} from '@linode/validation/lib/databases.schema';
57

@@ -14,6 +16,7 @@ import Request, {
1416

1517
import type { Filter, ResourcePage as Page, Params } from '../types';
1618
import type {
19+
ConnectionPool,
1720
CreateDatabasePayload,
1821
Database,
1922
DatabaseBackup,
@@ -364,3 +367,73 @@ export const getDatabaseEngineConfig = (engine: Engine) =>
364367
setURL(`${API_ROOT}/databases/${encodeURIComponent(engine)}/config`),
365368
setMethod('GET'),
366369
);
370+
371+
/**
372+
* Get a paginated list of connection pools for a database
373+
*/
374+
export const getDatabaseConnectionPools = (databaseID: number) =>
375+
Request<Page<ConnectionPool>>(
376+
setURL(
377+
`${API_ROOT}/databases/postgresql/instances/${encodeURIComponent(databaseID)}/connection-pools`,
378+
),
379+
setMethod('GET'),
380+
);
381+
382+
/**
383+
* Get a connection pool for a database
384+
*/
385+
export const getDatabaseConnectionPool = (
386+
databaseID: number,
387+
poolName: string,
388+
) =>
389+
Request<ConnectionPool>(
390+
setURL(
391+
`${API_ROOT}/databases/postgresql/instances/${encodeURIComponent(databaseID)}/connection-pools/${encodeURIComponent(poolName)}`,
392+
),
393+
setMethod('GET'),
394+
);
395+
396+
/**
397+
* Create a new connection pool for a database. Connection pools can only be created on active clusters
398+
*/
399+
export const createDatabaseConnectionPool = (
400+
databaseID: number,
401+
data: ConnectionPool,
402+
) =>
403+
Request<ConnectionPool>(
404+
setURL(
405+
`${API_ROOT}/databases/postgresql/instances/${encodeURIComponent(databaseID)}/connection-pools`,
406+
),
407+
setMethod('POST'),
408+
setData(data, createDatabaseConnectionPoolSchema),
409+
);
410+
411+
/**
412+
* Update an existing connection pool. This may cause sudden closure of an in-use connection pool
413+
*/
414+
export const updateDatabaseConnectionPool = (
415+
databaseID: number,
416+
poolName: string,
417+
data: Omit<ConnectionPool, 'label'>,
418+
) =>
419+
Request<ConnectionPool>(
420+
setURL(
421+
`${API_ROOT}/databases/postgresql/instances/${encodeURIComponent(databaseID)}/connection-pools/${encodeURIComponent(poolName)}`,
422+
),
423+
setMethod('PUT'),
424+
setData(data, updateDatabaseConnectionPoolSchema),
425+
);
426+
427+
/**
428+
* Delete an existing connection pool. This may cause sudden closure of an in-use connection pool
429+
*/
430+
export const deleteDatabaseConnectionPool = (
431+
databaseID: number,
432+
poolName: string,
433+
) =>
434+
Request<{}>(
435+
setURL(
436+
`${API_ROOT}/databases/postgresql/instances/${encodeURIComponent(databaseID)}/connection-pools/${encodeURIComponent(poolName)}`,
437+
),
438+
setMethod('DELETE'),
439+
);

packages/api-v4/src/databases/types.ts

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -105,6 +105,7 @@ type MemberType = 'failover' | 'primary';
105105
export interface DatabaseInstance {
106106
allow_list: string[];
107107
cluster_size: ClusterSize;
108+
connection_pool_port: null | number;
108109
connection_strings: ConnectionStrings[];
109110
created: string;
110111
/** @Deprecated used by rdbms-legacy only, rdbms-default always encrypts */
@@ -249,3 +250,13 @@ export interface UpdateDatabasePayload {
249250
updates?: UpdatesSchedule;
250251
version?: string;
251252
}
253+
254+
export type PoolMode = 'session' | 'statement' | 'transaction';
255+
256+
export interface ConnectionPool {
257+
database: string;
258+
label: string;
259+
mode: PoolMode;
260+
size: number;
261+
username: null | string;
262+
}

packages/manager/src/factories/databases.ts

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import {
22
type ClusterSize,
3+
type ConnectionPool,
34
type Database,
45
type DatabaseBackup,
56
type DatabaseEngine,
@@ -160,6 +161,7 @@ export const databaseInstanceFactory =
160161
? ([1, 3][i % 2] as ClusterSize)
161162
: ([1, 2, 3][i % 3] as ClusterSize)
162163
),
164+
connection_pool_port: null,
163165
connection_strings: [],
164166
created: '2021-12-09T17:15:12',
165167
encrypted: false,
@@ -211,6 +213,7 @@ export const databaseInstanceFactory =
211213
export const databaseFactory = Factory.Sync.makeFactory<Database>({
212214
allow_list: [...IPv4List],
213215
cluster_size: Factory.each(() => pickRandom([1, 3])),
216+
connection_pool_port: null,
214217
connection_strings: [
215218
{
216219
driver: 'python',
@@ -285,6 +288,15 @@ export const databaseEngineFactory = Factory.Sync.makeFactory<DatabaseEngine>({
285288
version: Factory.each((i) => `${i}`),
286289
});
287290

291+
export const databaseConnectionPoolFactory =
292+
Factory.Sync.makeFactory<ConnectionPool>({
293+
database: 'defaultdb',
294+
mode: 'transaction',
295+
label: Factory.each((i) => `pool/${i}`),
296+
size: 10,
297+
username: null,
298+
});
299+
288300
export const mysqlConfigResponse = {
289301
binlog_retention_period: {
290302
description:
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
"@linode/queries": Upcoming Features
3+
---
4+
5+
Added Database Connection Pool queries ([#13148](https://github.com/linode/manager/pull/13148))

packages/queries/src/databases/databases.ts

Lines changed: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,16 @@
11
import {
22
createDatabase,
3+
createDatabaseConnectionPool,
34
deleteDatabase,
5+
deleteDatabaseConnectionPool,
46
legacyRestoreWithBackup,
57
patchDatabase,
68
resetDatabaseCredentials,
79
restoreWithBackup,
810
resumeDatabase,
911
suspendDatabase,
1012
updateDatabase,
13+
updateDatabaseConnectionPool,
1114
} from '@linode/api-v4/lib/databases';
1215
import { profileQueries, queryPresets } from '@linode/queries';
1316
import {
@@ -22,6 +25,7 @@ import { databaseQueries } from './keys';
2225

2326
import type {
2427
APIError,
28+
ConnectionPool,
2529
CreateDatabasePayload,
2630
Database,
2731
DatabaseBackup,
@@ -194,6 +198,80 @@ export const useDatabaseBackupsQuery = (
194198
enabled,
195199
});
196200

201+
export const useDatabaseConnectionPool = (
202+
databaseId: number,
203+
poolName: string,
204+
enabled: boolean = false,
205+
) =>
206+
useQuery<ConnectionPool, APIError[]>({
207+
...databaseQueries
208+
.database('postgresql', databaseId)
209+
._ctx.connectionPools._ctx.pool(poolName),
210+
enabled,
211+
});
212+
213+
export const useDatabaseConnectionPools = (
214+
databaseId: number,
215+
enabled: boolean = false,
216+
) =>
217+
useQuery<ResourcePage<ConnectionPool>, APIError[]>({
218+
...databaseQueries.database('postgresql', databaseId)._ctx.connectionPools
219+
._ctx.pools,
220+
enabled,
221+
});
222+
223+
export const useCreateDatabaseConnectionPoolMutation = (databaseId: number) => {
224+
const queryClient = useQueryClient();
225+
return useMutation<ConnectionPool, APIError[], ConnectionPool>({
226+
mutationFn: (data) => createDatabaseConnectionPool(databaseId, data),
227+
onSuccess() {
228+
queryClient.invalidateQueries(
229+
databaseQueries.database('postgresql', databaseId)._ctx.connectionPools,
230+
);
231+
},
232+
});
233+
};
234+
235+
export const useUpdateDatabaseConnectionPoolMutation = (
236+
databaseId: number,
237+
poolName: string,
238+
) => {
239+
const queryClient = useQueryClient();
240+
return useMutation<ConnectionPool, APIError[], Omit<ConnectionPool, 'label'>>(
241+
{
242+
mutationFn: (data) =>
243+
updateDatabaseConnectionPool(databaseId, poolName, data),
244+
onSuccess(connectionPool) {
245+
queryClient.setQueryData<ConnectionPool>(
246+
databaseQueries
247+
.database('postgresql', databaseId)
248+
._ctx.connectionPools._ctx.pool(connectionPool.label).queryKey,
249+
connectionPool,
250+
);
251+
},
252+
},
253+
);
254+
};
255+
256+
export const useDeleteDatabaseConnectionPoolMutation = (
257+
databaseId: number,
258+
poolName: string,
259+
) => {
260+
const queryClient = useQueryClient();
261+
return useMutation<{}, APIError[]>({
262+
mutationFn: () => deleteDatabaseConnectionPool(databaseId, poolName),
263+
onSuccess() {
264+
queryClient.invalidateQueries(
265+
databaseQueries.database('postgresql', databaseId)._ctx.connectionPools,
266+
);
267+
queryClient.removeQueries({
268+
queryKey: databaseQueries.database('postgresql', databaseId)._ctx
269+
.connectionPools.queryKey,
270+
});
271+
},
272+
});
273+
};
274+
197275
export const useDatabaseEnginesQuery = (enabled: boolean = false) =>
198276
useQuery<DatabaseEngine[], APIError[]>({
199277
...databaseQueries.engines,

packages/queries/src/databases/keys.ts

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
import {
22
getDatabaseBackups,
3+
getDatabaseConnectionPool,
4+
getDatabaseConnectionPools,
35
getDatabaseCredentials,
46
getDatabaseEngineConfig,
57
getDatabases,
@@ -30,6 +32,19 @@ export const databaseQueries = createQueryKeys('databases', {
3032
queryFn: () => getDatabaseCredentials(engine, id),
3133
queryKey: null,
3234
},
35+
connectionPools: {
36+
contextQueries: {
37+
pool: (poolName: string) => ({
38+
queryFn: () => getDatabaseConnectionPool(id, poolName),
39+
queryKey: [poolName],
40+
}),
41+
pools: {
42+
queryFn: () => getDatabaseConnectionPools(id),
43+
queryKey: null,
44+
},
45+
},
46+
queryKey: null,
47+
},
3348
},
3449
queryFn: () => getEngineDatabase(engine, id),
3550
queryKey: [engine, id],
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
"@linode/validation": Upcoming Features
3+
---
4+
5+
Added Database Connection Pool schemas ([#13148](https://github.com/linode/manager/pull/13148))

packages/validation/src/databases.schema.ts

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -217,3 +217,22 @@ export const createDynamicAdvancedConfigSchema = (allConfigurations: any[]) => {
217217
),
218218
});
219219
};
220+
221+
export const createDatabaseConnectionPoolSchema = object({
222+
database: string().required('Database is required'),
223+
mode: string()
224+
.oneOf(['transaction', 'session', 'statement'], 'Pool mode is required')
225+
.required('Pool mode is required'),
226+
label: string()
227+
.required('Name is required')
228+
.max(63, 'Name must not exceed 63 characters'),
229+
size: number().required('Size is required'),
230+
username: string().nullable().required('Username is required'),
231+
});
232+
233+
export const updateDatabaseConnectionPoolSchema = object({
234+
database: string().optional(),
235+
mode: string().oneOf(['transaction', 'session', 'statement']).optional(),
236+
size: number().optional(),
237+
username: string().nullable().optional(),
238+
});

0 commit comments

Comments
 (0)