From 489e38c705397e72e4a6c51f50ee22d33304ffd1 Mon Sep 17 00:00:00 2001 From: Matteo Gioioso Date: Fri, 11 Nov 2022 09:15:03 +0200 Subject: [PATCH 1/3] feat(plugins): add queries abstraction and default postgres plugin --- README.md | 116 ++++++++++++++++++++++++++++++++++-------------- index.d.ts | 11 +++++ src/index.js | 75 +++++-------------------------- src/postgres.js | 84 +++++++++++++++++++++++++++++++++++ 4 files changed, 190 insertions(+), 96 deletions(-) create mode 100644 src/postgres.js diff --git a/README.md b/README.md index 452fc03..3ef13fe 100644 --- a/README.md +++ b/README.md @@ -32,20 +32,6 @@ using trusted backoff algorithms. Feel free to request additional features and contribute =) -## Changelog - -- **Default connections filtering (>= v2)**: this feature leverage postgres `application_name` to differentiate - clients created by this library and others, this will avoid terminating connections belonging to long-running - process, batch jobs, ect... - By default, we set the same `application_name` parameter for all the serverless clients, if you wish you can change it - by just specifying it in the client config: - ```javascript - const client = new ServerlessClient({ - ... - application_name: 'my_client', - }); - ``` - ## Install ```bash @@ -103,27 +89,91 @@ const handler = async (event, context) => { ``` +## Connections filtering (>= v2) + +This feature leverage postgres `application_name` to differentiate +clients created by this library and others, this will avoid terminating connections belonging to long-running +processes, batch jobs, ect... +By default, we set the same `application_name` parameter for all the serverless clients, if you wish you can change it +by just specifying it in the client config: + +```javascript +const client = new ServerlessClient({ + application_name: 'my_client', +}); +``` + +## Plugins (>= v2) + +Serverless-postgres is extensible and could be used for any wire compatible postgres engines such as Redshift, Google +Cloud Spanner, CockroachDB, YugabyteDB, etc... +If needed you can write your own plugin implementing the following interface: + +```typescript +interface Plugin { + getIdleProcessesListByMinimumTimeout(self: ServerlessClient): string | string[] [] + + getIdleProcessesListOrderByDate(self: ServerlessClient): string | string[] [] + + processCount(self: ServerlessClient): string | string[] [] + + killProcesses(self: ServerlessClient, pids: string[]): string | string[] [] + + showMaxConnections(self: ServerlessClient): string | string[] [] +} + +``` + +Every function supply as argument the serverless client itself so you can access any configuration parameter such +as `database`, `user`, `applicationName`, `ect...`; +you will need to return an array of values which is the query and the array of parameters, ex: + +```javascript +killProcesses(serverlessPgSelf, pids) { + const query = ` + SELECT pg_terminate_backend(pid) + FROM pg_stat_activity + WHERE pid = ANY ($1) + AND state = 'idle' + AND application_name = $2;` + + const values = [pids, serverlessPgSelf._application_name] + + return [query, values] +} +``` + +You can then use your plugin like this: + +```javascript + const client = new ServerlessClient({ + plugin: new MyServerlessPGPlugin(someObject) +}); +``` + ## Configuration Options -| Property | Type | Description | Default | -| -------- | ---- | ----------- | ------- | -| config | `Object` | A `node-pg` configuration object as defined [here](https://node-postgres.com/api/client) | `{}` | -| maxConnsFreqMs | `Integer` | The number of milliseconds to cache lookups of max_connections. | `60000` | -| manualMaxConnections | `Boolean` | if this parameters is set to true it will query to get the maxConnections values, to maximize performance you should set the `maxConnections` yourself | `false` | -| maxConnections | `Integer` | Max connections of your PostgreSQL. I highly suggest to set this yourself | `100` | -| strategy | `String` | Name of your chosen strategy for cleaning up "zombie" connections, allowed values `minimum_idle_time` or `ranked` | `minimum_idle_time` | -| minConnectionIdleTimeSec | `Integer` | The minimum number of seconds that a connection must be idle before the module will recycle it. | `0.5` | -| maxIdleConnectionsToKill | `Integer` or `null` | The amount of max connection that will get killed. Default is `ALL` | `null` | -| connUtilization | `Number` | The percentage of total connections to use when connecting to your PostgreSQL server. A value of `0.80` would use 80% of your total available connections. | `0.8` | -| debug | `Boolean` | Enable/disable debugging logs. | `false` | -| capMs | `Integer` | Maximum number of milliseconds between connection retries. | `1000` | -| baseMs | `Integer` | Number of milliseconds added to random backoff values. | `2` | -| delayMs | `Integer` | Additional delay to add to the exponential backoff. | `1000` | -| maxRetries | `Integer` | Maximum number of times to retry a connection before throwing an error. | `3` | -| processCountCacheEnabled | `Boolean` | Enable caching for get process count. | `False` | -| processCountFreqMs | `Integer` | The number of milliseconds to cache lookups of process count. | `6000` | -| allowCredentialsDiffing | `Boolean` | If you are using dynamic credentials, such as IAM, you can set this parameter to `true` and the client will be refreshed | `false` | -| library | `Function` | Custom postgres library | `require('pg')` | +| Property | Type | Description | Default | Version | +|--------------------------|---------------------|------------------------------------------------------------------------------------------------------------------------------------------------------------|---------------------|---------| +| config | `Object` | A `node-pg` configuration object as defined [here](https://node-postgres.com/api/client) | `{}` | | +| maxConnsFreqMs | `Integer` | The number of milliseconds to cache lookups of max_connections. | `60000` | | +| manualMaxConnections | `Boolean` | if this parameters is set to true it will query to get the maxConnections values, to maximize performance you should set the `maxConnections` yourself | `false` | | +| maxConnections | `Integer` | Max connections of your PostgreSQL. I highly suggest to set this yourself | `100` | | +| strategy | `String` | Name of your chosen strategy for cleaning up "zombie" connections, allowed values `minimum_idle_time` or `ranked` | `minimum_idle_time` | | +| minConnectionIdleTimeSec | `Integer` | The minimum number of seconds that a connection must be idle before the module will recycle it. | `0.5` | | +| maxIdleConnectionsToKill | `Integer` or `null` | The amount of max connection that will get killed. Default is `ALL` | `null` | | +| connUtilization | `Number` | The percentage of total connections to use when connecting to your PostgreSQL server. A value of `0.80` would use 80% of your total available connections. | `0.8` | | +| debug | `Boolean` | Enable/disable debugging logs. | `false` | | +| capMs | `Integer` | Maximum number of milliseconds between connection retries. | `1000` | | +| baseMs | `Integer` | Number of milliseconds added to random backoff values. | `2` | | +| delayMs | `Integer` | Additional delay to add to the exponential backoff. | `1000` | | +| maxRetries | `Integer` | Maximum number of times to retry a connection before throwing an error. | `3` | | +| processCountCacheEnabled | `Boolean` | Enable caching for get process count. | `False` | | +| processCountFreqMs | `Integer` | The number of milliseconds to cache lookups of process count. | `6000` | | +| allowCredentialsDiffing | `Boolean` | If you are using dynamic credentials, such as IAM, you can set this parameter to `true` and the client will be refreshed | `false` | | +| library | `Function` | Custom postgres library | `require('pg')` | | +| application_name | `String` | This is postgres specific configuration; serverless-pg uses it to avoid closing other applications connections. | `serverless_client` | `>= v2` | +| plugin | `Object` | This is where you need to initialize your plugin class | `Postgres` | `>= v2` | ## Note diff --git a/index.d.ts b/index.d.ts index d77fbad..01a8688 100644 --- a/index.d.ts +++ b/index.d.ts @@ -40,8 +40,19 @@ declare interface Config { delayMs?: number; maxRetries?: number; library?: typeof import("pg"); + plugin?: Plugin; } +declare interface Plugin { + getIdleProcessesListByMinimumTimeout(self: ServerlessClient): PluginReturnValue + getIdleProcessesListOrderByDate(self: ServerlessClient): PluginReturnValue + processCount(self: ServerlessClient): PluginReturnValue + killProcesses(self: ServerlessClient, pids: string[]): PluginReturnValue + showMaxConnections(self: ServerlessClient): PluginReturnValue +} + +declare type PluginReturnValue = string | string[] [] + declare namespace ServerlessClient { export { TlsOptions, Config } } diff --git a/src/index.js b/src/index.js index 2717192..8df8a57 100644 --- a/src/index.js +++ b/src/index.js @@ -7,9 +7,16 @@ */ const {isValidStrategy, type, validateNum, isWithinRange} = require("./utils"); +const Postgres = require("./postgres"); function ServerlessClient(config) { this._client = null; + if (config.plugin) { + this._plugin = config.plugin + } else { + this._plugin = new Postgres() + } + this.setConfig(config) } @@ -24,7 +31,7 @@ ServerlessClient.prototype._sleep = delay => ServerlessClient.prototype._setMaxConnections = async (__self) => { // If cache is expired if (Date.now() - __self._maxConns.cache.updated > __self._maxConns.freqMs) { - const results = await __self._client.query(`SHOW max_connections`) + const results = await __self._client.query(...__self._plugin.showMaxConnections(this)) const maxConnections = results.rows[0].max_connections __self._logger("Getting max connections from database...", maxConnections) @@ -39,25 +46,8 @@ ServerlessClient.prototype._setMaxConnections = async (__self) => { // This strategy arbitrarily (maxIdleConnections) terminates connections starting from the oldest one in idle. // It is very aggressive and it can cause disruption if a connection was in idle for a short period of time ServerlessClient.prototype._getIdleProcessesListOrderByDate = async function () { - const query = ` - SELECT pid, backend_start, state - FROM pg_stat_activity - WHERE datname = $1 - AND state = 'idle' - AND usename = $2 - AND application_name = $4 - ORDER BY state_change - LIMIT $3;` - - const values = [ - this._client.database, - this._client.user, - this._strategy.maxIdleConnectionsToKill, - this._application_name - ] - try { - const result = await this._client.query(query, values); + const result = await this._client.query(...this._plugin.getIdleProcessesListOrderByDate(this)); return result.rows } catch (e) { @@ -71,31 +61,8 @@ ServerlessClient.prototype._getIdleProcessesListOrderByDate = async function () // than a minimum amount of seconds, it is very accurate as it only takes the process that have been in idle // for more than a threshold time (minConnectionTimeoutSec) ServerlessClient.prototype._getIdleProcessesListByMinimumTimeout = async function () { - const query = ` - WITH processes AS ( - SELECT EXTRACT(EPOCH FROM (Now() - state_change)) AS idle_time, - pid - FROM pg_stat_activity - WHERE usename = $1 - AND datname = $2 - AND state = 'idle' - AND application_name = $5 - ) - SELECT pid - FROM processes - WHERE idle_time > $3 - LIMIT $4;` - - const values = [ - this._client.user, - this._client.database, - this._strategy.minConnIdleTimeSec, - this._strategy.maxIdleConnectionsToKill, - this._application_name - ] - try { - const result = await this._client.query(query, values) + const result = await this._client.query(...this._plugin.getIdleProcessesListByMinimumTimeout(this)) return result.rows } catch (e) { @@ -116,17 +83,8 @@ ServerlessClient.prototype._getProcessesCount = async function () { } if (isCacheExpiredOrDisabled(this)) { - const query = ` - SELECT COUNT(pid) - FROM pg_stat_activity - WHERE datname = $1 - AND usename = $2 - AND application_name = $3;` - - const values = [this._client.database, this._client.user, this._application_name] - try { - const result = await this._client.query(query, values); + const result = await this._client.query(...this._plugin.processCount(this)); this._processCount.cache = { count: result.rows[0].count || 0, updated: Date.now() @@ -147,17 +105,8 @@ ServerlessClient.prototype._getProcessesCount = async function () { ServerlessClient.prototype._killProcesses = async function (processesList) { const pids = processesList.map(proc => proc.pid); - const query = ` - SELECT pg_terminate_backend(pid) - FROM pg_stat_activity - WHERE pid = ANY ($1) - AND state = 'idle' - AND application_name = $2;` - - const values = [pids, this._application_name] - try { - return await this._client.query(query, values) + return await this._client.query(...this._plugin.killProcesses(this, pids)) } catch (e) { this._logger("Swallowed internal error: ", e.message) // Swallow the error, if this produce an error there is no need to error the function diff --git a/src/postgres.js b/src/postgres.js new file mode 100644 index 0000000..a51b787 --- /dev/null +++ b/src/postgres.js @@ -0,0 +1,84 @@ +class Postgres { + constructor() { + } + + getIdleProcessesListByMinimumTimeout(serverlessPgSelf) { + const query = ` + WITH processes AS ( + SELECT EXTRACT(EPOCH FROM (Now() - state_change)) AS idle_time, + pid + FROM pg_stat_activity + WHERE usename = $1 + AND datname = $2 + AND state = 'idle' + AND application_name = $5 + ) + SELECT pid + FROM processes + WHERE idle_time > $3 + LIMIT $4;` + + const values = [ + serverlessPgSelf._client.user, + serverlessPgSelf._client.database, + serverlessPgSelf._strategy.minConnIdleTimeSec, + serverlessPgSelf._strategy.maxIdleConnectionsToKill, + serverlessPgSelf._application_name + ] + + return [query, values] + } + + getIdleProcessesListOrderByDate(serverlessPgSelf) { + const query = ` + SELECT pid, backend_start, state + FROM pg_stat_activity + WHERE datname = $1 + AND state = 'idle' + AND usename = $2 + AND application_name = $4 + ORDER BY state_change + LIMIT $3;` + + const values = [ + serverlessPgSelf._client.database, + serverlessPgSelf._client.user, + serverlessPgSelf._strategy.maxIdleConnectionsToKill, + serverlessPgSelf._application_name + ] + + return [query, values] + } + + processCount(serverlessPgSelf){ + const query = ` + SELECT COUNT(pid) + FROM pg_stat_activity + WHERE datname = $1 + AND usename = $2 + AND application_name = $3;` + + const values = [serverlessPgSelf._client.database, serverlessPgSelf._client.user, serverlessPgSelf._application_name] + + return [query, values] + } + + killProcesses(serverlessPgSelf, pids){ + const query = ` + SELECT pg_terminate_backend(pid) + FROM pg_stat_activity + WHERE pid = ANY ($1) + AND state = 'idle' + AND application_name = $2;` + + const values = [pids, serverlessPgSelf._application_name] + + return [query, values] + } + + showMaxConnections(serverlessPgSelf) { + return [`SHOW max_connections`] + } +} + +module.exports = Postgres \ No newline at end of file From 8b669e073bc2bbc221fcaae271521e755be51700 Mon Sep 17 00:00:00 2001 From: Matteo Gioioso Date: Wed, 16 Nov 2022 09:24:46 +0200 Subject: [PATCH 2/3] Change interface and implementation of plugins --- README.md | 30 +++++++----------- index.d.ts | 40 +++++++++++++++++++----- src/index.js | 13 +++++--- src/postgres.js | 83 ++++++++++++++++++++++++------------------------- 4 files changed, 93 insertions(+), 73 deletions(-) diff --git a/README.md b/README.md index 3ef13fe..5c37778 100644 --- a/README.md +++ b/README.md @@ -111,35 +111,29 @@ If needed you can write your own plugin implementing the following interface: ```typescript interface Plugin { - getIdleProcessesListByMinimumTimeout(self: ServerlessClient): string | string[] [] + getIdleProcessesListByMinimumTimeout(self: ServerlessClient): Promise>; - getIdleProcessesListOrderByDate(self: ServerlessClient): string | string[] [] + getIdleProcessesListOrderByDate(self: ServerlessClient): Promise>; - processCount(self: ServerlessClient): string | string[] [] + processCount(self: ServerlessClient): Promise>; - killProcesses(self: ServerlessClient, pids: string[]): string | string[] [] + killProcesses(self: ServerlessClient, pids: string[]): Promise>; - showMaxConnections(self: ServerlessClient): string | string[] [] + showMaxConnections(self: ServerlessClient): Promise>; } ``` Every function supply as argument the serverless client itself so you can access any configuration parameter such as `database`, `user`, `applicationName`, `ect...`; -you will need to return an array of values which is the query and the array of parameters, ex: +if your changes are minimal you can inherit the main Postgres plugin class: ```javascript -killProcesses(serverlessPgSelf, pids) { - const query = ` - SELECT pg_terminate_backend(pid) - FROM pg_stat_activity - WHERE pid = ANY ($1) - AND state = 'idle' - AND application_name = $2;` - - const values = [pids, serverlessPgSelf._application_name] - - return [query, values] +class MyPlugin extends Postgres { + constructor() { + super(); + } + // ... } ``` @@ -158,7 +152,7 @@ You can then use your plugin like this: | config | `Object` | A `node-pg` configuration object as defined [here](https://node-postgres.com/api/client) | `{}` | | | maxConnsFreqMs | `Integer` | The number of milliseconds to cache lookups of max_connections. | `60000` | | | manualMaxConnections | `Boolean` | if this parameters is set to true it will query to get the maxConnections values, to maximize performance you should set the `maxConnections` yourself | `false` | | -| maxConnections | `Integer` | Max connections of your PostgreSQL. I highly suggest to set this yourself | `100` | | +| maxConnections | `Integer` | Max connections of your PostgreSQL, it should be set equal to `max_connections` in your cluster. I highly suggest to set this yourself | `100` | | | strategy | `String` | Name of your chosen strategy for cleaning up "zombie" connections, allowed values `minimum_idle_time` or `ranked` | `minimum_idle_time` | | | minConnectionIdleTimeSec | `Integer` | The minimum number of seconds that a connection must be idle before the module will recycle it. | `0.5` | | | maxIdleConnectionsToKill | `Integer` or `null` | The amount of max connection that will get killed. Default is `ALL` | `null` | | diff --git a/index.d.ts b/index.d.ts index 01a8688..90d7d43 100644 --- a/index.d.ts +++ b/index.d.ts @@ -1,4 +1,4 @@ -import stream = require('stream'); +import stream = require("stream"); declare interface TlsOptions { rejectUnauthorized?: boolean; @@ -44,26 +44,50 @@ declare interface Config { } declare interface Plugin { - getIdleProcessesListByMinimumTimeout(self: ServerlessClient): PluginReturnValue - getIdleProcessesListOrderByDate(self: ServerlessClient): PluginReturnValue - processCount(self: ServerlessClient): PluginReturnValue - killProcesses(self: ServerlessClient, pids: string[]): PluginReturnValue - showMaxConnections(self: ServerlessClient): PluginReturnValue + getIdleProcessesListByMinimumTimeout(self: ServerlessClient): Promise>; + + getIdleProcessesListOrderByDate(self: ServerlessClient): Promise>; + + processCount(self: ServerlessClient): Promise>; + + killProcesses(self: ServerlessClient, pids: string[]): Promise>; + + showMaxConnections(self: ServerlessClient): Promise>; } -declare type PluginReturnValue = string | string[] [] +declare interface ProcessList { + pid: string; +} + +declare interface Count { + count: number; +} + +declare interface MaxConnections { + max_connections: number; +} + +declare interface NodePgClientResponse { + rows: T[]; +} declare namespace ServerlessClient { - export { TlsOptions, Config } + export { TlsOptions, Config }; } declare class ServerlessClient { constructor(config: Config) + clean(): Promise + setConfig(config: Config): void + connect(): Promise + query(...args): Promise + end(): Promise + on(...args): void } diff --git a/src/index.js b/src/index.js index 8df8a57..c3d9ff6 100644 --- a/src/index.js +++ b/src/index.js @@ -31,7 +31,7 @@ ServerlessClient.prototype._sleep = delay => ServerlessClient.prototype._setMaxConnections = async (__self) => { // If cache is expired if (Date.now() - __self._maxConns.cache.updated > __self._maxConns.freqMs) { - const results = await __self._client.query(...__self._plugin.showMaxConnections(this)) + const results = await __self._plugin.showMaxConnections(__self) const maxConnections = results.rows[0].max_connections __self._logger("Getting max connections from database...", maxConnections) @@ -47,7 +47,7 @@ ServerlessClient.prototype._setMaxConnections = async (__self) => { // It is very aggressive and it can cause disruption if a connection was in idle for a short period of time ServerlessClient.prototype._getIdleProcessesListOrderByDate = async function () { try { - const result = await this._client.query(...this._plugin.getIdleProcessesListOrderByDate(this)); + const result = await this._plugin.getIdleProcessesListOrderByDate(this); return result.rows } catch (e) { @@ -62,7 +62,7 @@ ServerlessClient.prototype._getIdleProcessesListOrderByDate = async function () // for more than a threshold time (minConnectionTimeoutSec) ServerlessClient.prototype._getIdleProcessesListByMinimumTimeout = async function () { try { - const result = await this._client.query(...this._plugin.getIdleProcessesListByMinimumTimeout(this)) + const result = await this._plugin.getIdleProcessesListByMinimumTimeout(this) return result.rows } catch (e) { @@ -84,7 +84,7 @@ ServerlessClient.prototype._getProcessesCount = async function () { if (isCacheExpiredOrDisabled(this)) { try { - const result = await this._client.query(...this._plugin.processCount(this)); + const result = await this._plugin.processCount(this); this._processCount.cache = { count: result.rows[0].count || 0, updated: Date.now() @@ -94,6 +94,7 @@ ServerlessClient.prototype._getProcessesCount = async function () { } catch (e) { this._logger("Swallowed internal error", e.message) // Swallow the error, if this produce an error there is no need to error the function + // TODO: maybe return the cached process count would be better return 0 } } @@ -106,7 +107,7 @@ ServerlessClient.prototype._killProcesses = async function (processesList) { const pids = processesList.map(proc => proc.pid); try { - return await this._client.query(...this._plugin.killProcesses(this, pids)) + return await this._plugin.killProcesses(this, pids) } catch (e) { this._logger("Swallowed internal error: ", e.message) // Swallow the error, if this produce an error there is no need to error the function @@ -144,6 +145,8 @@ ServerlessClient.prototype.clean = async function () { const processesList = await strategy(); if (processesList.length) { const killedProcesses = await this._killProcesses(processesList); + // This to minimize the chances of re-triggering the killProcesses if the lambda is called after few seconds + this._processCount.cache.count = this._processCount.cache.count - killedProcesses.rows.length this._logger("+++++ Killed processes: ", killedProcesses.rows.length, " +++++") return killedProcesses.rows } diff --git a/src/postgres.js b/src/postgres.js index a51b787..dfaf0d4 100644 --- a/src/postgres.js +++ b/src/postgres.js @@ -2,21 +2,19 @@ class Postgres { constructor() { } - getIdleProcessesListByMinimumTimeout(serverlessPgSelf) { + async getIdleProcessesListByMinimumTimeout(serverlessPgSelf) { const query = ` - WITH processes AS ( - SELECT EXTRACT(EPOCH FROM (Now() - state_change)) AS idle_time, - pid - FROM pg_stat_activity - WHERE usename = $1 - AND datname = $2 - AND state = 'idle' - AND application_name = $5 - ) - SELECT pid - FROM processes - WHERE idle_time > $3 - LIMIT $4;` + WITH processes AS (SELECT EXTRACT(EPOCH FROM (Now() - state_change)) AS idle_time, + pid + FROM pg_stat_activity + WHERE usename = $1 + AND datname = $2 + AND state = 'idle' + AND application_name = $5) + SELECT pid + FROM processes + WHERE idle_time > $3 + LIMIT $4;`; const values = [ serverlessPgSelf._client.user, @@ -24,61 +22,62 @@ class Postgres { serverlessPgSelf._strategy.minConnIdleTimeSec, serverlessPgSelf._strategy.maxIdleConnectionsToKill, serverlessPgSelf._application_name - ] + ]; - return [query, values] + return serverlessPgSelf._client.query(query, values); } - getIdleProcessesListOrderByDate(serverlessPgSelf) { + async getIdleProcessesListOrderByDate(serverlessPgSelf) { const query = ` - SELECT pid, backend_start, state - FROM pg_stat_activity - WHERE datname = $1 - AND state = 'idle' - AND usename = $2 - AND application_name = $4 - ORDER BY state_change - LIMIT $3;` + SELECT pid, backend_start, state + FROM pg_stat_activity + WHERE datname = $1 + AND state = 'idle' + AND usename = $2 + AND application_name = $4 + ORDER BY state_change + LIMIT $3;`; const values = [ serverlessPgSelf._client.database, serverlessPgSelf._client.user, serverlessPgSelf._strategy.maxIdleConnectionsToKill, serverlessPgSelf._application_name - ] + ]; - return [query, values] + return serverlessPgSelf._client.query(query, values); } - processCount(serverlessPgSelf){ + async processCount(serverlessPgSelf) { const query = ` SELECT COUNT(pid) FROM pg_stat_activity WHERE datname = $1 AND usename = $2 - AND application_name = $3;` + AND application_name = $3;`; - const values = [serverlessPgSelf._client.database, serverlessPgSelf._client.user, serverlessPgSelf._application_name] + const values = [serverlessPgSelf._client.database, serverlessPgSelf._client.user, serverlessPgSelf._application_name]; - return [query, values] + return serverlessPgSelf._client.query(query, values); } - killProcesses(serverlessPgSelf, pids){ + async killProcesses(serverlessPgSelf, pids) { const query = ` - SELECT pg_terminate_backend(pid) - FROM pg_stat_activity - WHERE pid = ANY ($1) - AND state = 'idle' - AND application_name = $2;` + SELECT pg_terminate_backend(pid) + FROM pg_stat_activity + WHERE pid = ANY ($1) + AND state = 'idle' + AND application_name = $2;`; - const values = [pids, serverlessPgSelf._application_name] + const values = [pids, serverlessPgSelf._application_name]; - return [query, values] + return serverlessPgSelf._client.query(query, values); } - showMaxConnections(serverlessPgSelf) { - return [`SHOW max_connections`] + async showMaxConnections(serverlessPgSelf) { + const query = `SHOW max_connections`; + return serverlessPgSelf._client.query(query); } } -module.exports = Postgres \ No newline at end of file +module.exports = Postgres; \ No newline at end of file From 249f3edb6f0e57a1a35dc6495f8565f5110e7043 Mon Sep 17 00:00:00 2001 From: Matteo Gioioso Date: Sun, 7 May 2023 10:51:03 +0300 Subject: [PATCH 3/3] fix(): rebase plugins branch --- src/index.js | 1 + src/postgres.js | 18 +++++++++--------- 2 files changed, 10 insertions(+), 9 deletions(-) diff --git a/src/index.js b/src/index.js index c3d9ff6..2d63adf 100644 --- a/src/index.js +++ b/src/index.js @@ -85,6 +85,7 @@ ServerlessClient.prototype._getProcessesCount = async function () { if (isCacheExpiredOrDisabled(this)) { try { const result = await this._plugin.processCount(this); + this._processCount.cache = { count: result.rows[0].count || 0, updated: Date.now() diff --git a/src/postgres.js b/src/postgres.js index dfaf0d4..a29b6a6 100644 --- a/src/postgres.js +++ b/src/postgres.js @@ -29,14 +29,14 @@ class Postgres { async getIdleProcessesListOrderByDate(serverlessPgSelf) { const query = ` - SELECT pid, backend_start, state - FROM pg_stat_activity - WHERE datname = $1 - AND state = 'idle' - AND usename = $2 - AND application_name = $4 - ORDER BY state_change - LIMIT $3;`; + SELECT pid, backend_start, state + FROM pg_stat_activity + WHERE datname = $1 + AND state = 'idle' + AND usename = $2 + AND application_name = $4 + ORDER BY state_change + LIMIT $3;` const values = [ serverlessPgSelf._client.database, @@ -48,7 +48,7 @@ class Postgres { return serverlessPgSelf._client.query(query, values); } - async processCount(serverlessPgSelf) { + async processCount(serverlessPgSelf){ const query = ` SELECT COUNT(pid) FROM pg_stat_activity