SQL Datasources: Update Max Connection and Max Idle Connection Defaults to 100 and add auto mode (#65834)

* Update connection configuration for SQL datasources

* Working auto state for connection numbers

* Add migration

* Use defaults from constants file

* Remove dead code

* Add tests and restructure useMigrateDatabaseField

* Update function names

* Update docs

* Make sure we don't continually issue updates

* Update docs

* Use onOptionsChnage in ConnectionLimits

* Update docs

* Clean up docs

* Update migration

* Fix default values in docs

* Fix spacing issue

* Fix test

* Update default values for SQL connections

* Include consts

* Allow override for default SQL datasource connection parameters

* Fix linter errors

* Remove extra @ts-ignore

* Centralize logic for default values

* Remove debugging

* Remove unecessary function

* Update configuration docs

* minor suggested change

* Fix comment misspelling

* Remove unecessary default setting code

* Update docs to indicate that code was included for backport version

* Remove dead code

---------

Co-authored-by: lwandz13 <larissa.wandzura@grafana.com>
This commit is contained in:
Kyle Cunningham 2023-04-17 15:44:05 +07:00 committed by GitHub
parent 2509dec0cb
commit 92d92187d9
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
20 changed files with 378 additions and 146 deletions

View File

@ -413,6 +413,21 @@ default_home_dashboard_path =
# Upper limit of data sources that Grafana will return. This limit is a temporary configuration and it will be deprecated when pagination will be introduced on the list data sources API.
datasource_limit = 5000
################################### SQL Data Sources #####################
[sql_datasources]
# Default maximum number of open connections maintained in the connection pool
# when connecting to SQL based data sources
max_open_conns_default = 100
# Default maximum number of idle connections maintained in the connection pool
# when connecting to SQL based data sources
max_idle_conns_default = 100
# Default maximum connection lifetime used when connecting
# to SQL based data sources.
max_conn_lifetime_default = 14400
#################################### Users ###############################
[users]
# disable user signup / registration

View File

@ -41,19 +41,20 @@ To configure basic settings for the data source, complete the following steps:
1. Set the data source's basic configuration options:
| Name | Description |
| ------------------ | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| **Name** | Sets the name you use to refer to the data source in panels and queries. |
| **Default** | Sets the data source that's pre-selected for new panels. |
| **Host** | Sets the IP address/hostname and optional port of your MS SQL instance. Default port is 0, the driver default. You can specify multiple connection properties, such as `ApplicationIntent`, by separating each property with a semicolon (`;`). |
| **Database** | Sets the name of your MS SQL database. |
| **Authentication** | Sets the authentication mode, either using SQL Server Authentication or Windows Authentication (single sign-on for Windows users). |
| **User** | Defines the database user's username. |
| **Password** | Defines the database user's password. |
| **Encrypt** | Determines whether to negotiate a secure SSL TCP/IP connection with the server, or to which extent. Default is `false`. |
| **Max open** | Sets the maximum number of open connections to the database. Default is `unlimited`. |
| **Max idle** | Sets the maximum number of connections in the idle connection pool. Default is `2`. |
| **Max lifetime** | Sets the maximum number of seconds that the data source can reuse a connection. Default is `14400` (4 hours). |
| Name | Description |
| ------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| **Name** | Sets the name you use to refer to the data source in panels and queries. |
| **Default** | Sets the data source that's pre-selected for new panels. |
| **Host** | Sets the IP address/hostname and optional port of your MS SQL instance. Default port is 0, the driver default. You can specify multiple connection properties, such as `ApplicationIntent`, by separating each property with a semicolon (`;`). |
| **Database** | Sets the name of your MS SQL database. |
| **Authentication** | Sets the authentication mode, either using SQL Server Authentication or Windows Authentication (single sign-on for Windows users). |
| **User** | Defines the database user's username. |
| **Password** | Defines the database user's password. |
| **Encrypt** | Determines whether to negotiate a secure SSL TCP/IP connection with the server, or to which extent. Default is `false`. |
| **Max open** | Sets the maximum number of open connections to the database. Default is `100`. |
| **Max idle** | Sets the maximum number of connections in the idle connection pool. Default is `100`. |
| **Auto (max idle)** | If set will set the maximum number of idle connections to the number of maximum open connections (Grafana v9.5.1+). Default is `true`. |
| **Max lifetime** | Sets the maximum number of seconds that the data source can reuse a connection. Default is `14400` (4 hours). |
You can also configure settings specific to the Microsoft SQL Server data source. These options are described in the sections below.
@ -122,8 +123,9 @@ datasources:
user: grafana
jsonData:
database: grafana
maxOpenConns: 0 # Grafana v5.4+
maxIdleConns: 2 # Grafana v5.4+
maxOpenConns: 100 # Grafana v5.4+
maxIdleConns: 100 # Grafana v5.4+
maxIdleConnsAuto: true # Grafana v9.5.1+
connMaxLifetime: 14400 # Grafana v5.4+
connectionTimeout: 0 # Grafana v9.3+
secureJsonData:

View File

@ -35,20 +35,19 @@ Administrators can also [configure the data source via YAML]({{< relref "#provis
1. Set the data source's basic configuration options.
### Data source options
| Name | Description |
| ------------------ | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `Name` | The data source name. This is how you refer to the data source in panels and queries. |
| `Default` | Default data source means that it will be pre-selected for new panels. |
| `Host` | The IP address/hostname and optional port of your MySQL instance. |
| `Database` | Name of your MySQL database. |
| `User` | Database user's login/username |
| `Password` | Database user's password |
| `Session Timezone` | Specify the time zone used in the database session, such as `Europe/Berlin` or `+02:00`. This is necessary, if the timezone of the database (or the host of the database) is set to something other than UTC. Set the value used in the session with `SET time_zone='...'`. If you leave this field empty, then the time zone is not updated. For more information, refer to the [MySQL documentation](https://dev.mysql.com/doc/refman/8.0/en/time-zone-support.html). |
| `Max open` | The maximum number of open connections to the database, default `unlimited` (Grafana v5.4+). |
| `Max idle` | The maximum number of connections in the idle connection pool, default `2` (Grafana v5.4+). |
| `Max lifetime` | The maximum amount of time in seconds a connection may be reused, default `14400`/4 hours. This should always be lower than configured [wait_timeout](https://dev.mysql.com/doc/refman/8.0/en/server-system-variables.html#sysvar_wait_timeout) in MySQL (Grafana v5.4+). |
| Name | Description |
| -------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| **Name** | The data source name. This is how you refer to the data source in panels and queries. |
| **Default** | Default data source means that it will be pre-selected for new panels. |
| **Host** | The IP address/hostname and optional port of your MySQL instance. |
| **Database** | Name of your MySQL database. |
| **User** | Database user's login/username |
| **Password** | Database user's password |
| **Session Timezone** | Specify the time zone used in the database session, such as `Europe/Berlin` or `+02:00`. This is necessary, if the timezone of the database (or the host of the database) is set to something other than UTC. Set the value used in the session with `SET time_zone='...'`. If you leave this field empty, then the time zone is not updated. For more information, refer to the [MySQL documentation](https://dev.mysql.com/doc/refman/8.0/en/time-zone-support.html). |
| **Max open** | The maximum number of open connections to the database, default `100` (Grafana v5.4+). |
| **Max idle** | The maximum number of connections in the idle connection pool, default `100` (Grafana v5.4+). |
| **Auto (max idle)** | If set will set the maximum number of idle connections to the number of maximum open connections (Grafana v9.5.1+). Default is `true`. |
| **Max lifetime** | The maximum amount of time in seconds a connection may be reused, default `14400`/4 hours. This should always be lower than configured [wait_timeout](https://dev.mysql.com/doc/refman/8.0/en/server-system-variables.html#sysvar_wait_timeout) in MySQL (Grafana v5.4+). |
### Min time interval
@ -107,8 +106,9 @@ datasources:
user: grafana
jsonData:
database: grafana
maxOpenConns: 0 # Grafana v5.4+
maxIdleConns: 2 # Grafana v5.4+
maxOpenConns: 100 # Grafana v5.4+
maxIdleConns: 100 # Grafana v5.4+
maxIdleConnsAuto: true # Grafana v9.5.1+
connMaxLifetime: 14400 # Grafana v5.4+
secureJsonData:
password: ${GRAFANA_MYSQL_PASSWORD}
@ -127,8 +127,9 @@ datasources:
jsonData:
tlsAuth: true
database: grafana
maxOpenConns: 0 # Grafana v5.4+
maxIdleConns: 2 # Grafana v5.4+
maxOpenConns: 100 # Grafana v5.4+
maxIdleConns: 100 # Grafana v5.4+
maxIdleConnsAuto: true # Grafana v9.5.1+
connMaxLifetime: 14400 # Grafana v5.4+
secureJsonData:
password: ${GRAFANA_MYSQL_PASSWORD}
@ -150,8 +151,9 @@ datasources:
tlsAuth: true
skipTLSVerify: true
database: grafana
maxOpenConns: 0 # Grafana v5.4+
maxIdleConns: 2 # Grafana v5.4+
maxOpenConns: 100 # Grafana v5.4+
maxIdleConns: 100 # Grafana v5.4+
maxIdleConnsAuto: true # Grafana v9.5.1+
connMaxLifetime: 14400 # Grafana v5.4+
secureJsonData:
password: ${GRAFANA_MYSQL_PASSWORD}

View File

@ -33,23 +33,23 @@ To configure basic settings for the data source, complete the following steps:
1. Set the data source's basic configuration options:
| Name | Description |
| --------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| **Name** | The data source name. This is how you refer to the data source in panels and queries. |
| **Default** | Default data source means that it will be pre-selected for new panels. |
| **Host** | The IP address/hostname and optional port of your PostgreSQL instance. _Do not_ include the database name. The connection string for connecting to Postgres will not be correct and it may cause errors. |
| **Database** | Name of your PostgreSQL database. |
| **User** | Database user's login/username |
| **Password** | Database user's password |
| **SSL Mode** | Determines whether or with what priority a secure SSL TCP/IP connection will be negotiated with the server. When SSL Mode is disabled, SSL Method and Auth Details would not be visible. |
| **SSL Auth Details Method** | Determines whether the SSL Auth details will be configured as a file path or file content. Grafana v7.5+ |
| **SSL Auth Details Value** | File path or file content of SSL root certificate, client certificate and client key |
| **Max open** | The maximum number of open connections to the database, default `unlimited` (Grafana v5.4+). |
| **Max idle** | The maximum number of connections in the idle connection pool, default `2` (Grafana v5.4+). |
| **Max lifetime** | The maximum amount of time in seconds a connection may be reused, default `14400`/4 hours (Grafana v5.4+). |
| **Version** | Determines which functions are available in the query builder (only available in Grafana 5.3+). |
| **TimescaleDB** | A time-series database built as a PostgreSQL extension. When enabled, Grafana uses `time_bucket` in the `$__timeGroup` macro to display TimescaleDB specific aggregate functions in the query builder (only available in Grafana 5.3+). For more information, see [TimescaleDB documentation](https://docs.timescale.com/timescaledb/latest/tutorials/grafana/grafana-timescalecloud/#connect-timescaledb-and-grafana). |
| Name | Description |
| --------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| **Name** | The data source name. This is how you refer to the data source in panels and queries. |
| **Default** | Default data source means that it will be pre-selected for new panels. |
| **Host** | The IP address/hostname and optional port of your PostgreSQL instance. _Do not_ include the database name. The connection string for connecting to Postgres will not be correct and it may cause errors. |
| **Database** | Name of your PostgreSQL database. |
| **User** | Database user's login/username |
| **Password** | Database user's password |
| **SSL Mode** | Determines whether or with what priority a secure SSL TCP/IP connection will be negotiated with the server. When SSL Mode is disabled, SSL Method and Auth Details would not be visible. |
| **SSL Auth Details Method** | Determines whether the SSL Auth details will be configured as a file path or file content. Grafana v7.5+ |
| **SSL Auth Details Value** | File path or file content of SSL root certificate, client certificate and client key |
| **Max open** | The maximum number of open connections to the database, default `100` (Grafana v5.4+). |
| **Max idle** | The maximum number of connections in the idle connection pool, default `100` (Grafana v5.4+). |
| **Auto (max idle)** | If set will set the maximum number of idle connections to the number of maximum open connections (Grafana v9.5.1+). Default is `true`. |
| **Max lifetime** | The maximum amount of time in seconds a connection may be reused, default `14400`/4 hours (Grafana v5.4+). |
| **Version** | Determines which functions are available in the query builder (only available in Grafana 5.3+). |
| **TimescaleDB** | A time-series database built as a PostgreSQL extension. When enabled, Grafana uses `time_bucket` in the `$__timeGroup` macro to display TimescaleDB specific aggregate functions in the query builder (only available in Grafana 5.3+). For more information, see [TimescaleDB documentation](https://docs.timescale.com/timescaledb/latest/tutorials/grafana/grafana-timescalecloud/#connect-timescaledb-and-grafana). |
### Min time interval
@ -141,8 +141,9 @@ datasources:
jsonData:
database: grafana
sslmode: 'disable' # disable/require/verify-ca/verify-full
maxOpenConns: 0 # Grafana v5.4+
maxIdleConns: 2 # Grafana v5.4+
maxOpenConns: 100 # Grafana v5.4+
maxIdleConns: 100 # Grafana v5.4+
maxIdleConnsAuto: true # Grafana v9.5.1+
connMaxLifetime: 14400 # Grafana v5.4+
postgresVersion: 903 # 903=9.3, 904=9.4, 905=9.5, 906=9.6, 1000=10
timescaledb: false

View File

@ -75,7 +75,7 @@ rendering_ignore_https_errors = true
enable = newNavigation
```
You can override them on Linux machines with:
You can override variables on Linux machines with:
```bash
export GF_DEFAULT_INSTANCE_NAME=my-instance
@ -730,6 +730,22 @@ Path to the default home dashboard. If this value is empty, then Grafana uses St
<hr />
## [sql_datasources]
### max_open_conns_default
For SQL data sources (MySql, Postgres, MSSQL) you can override the default maximum number of open connections (default: 100). The value configured in data source settings will be preferred over the default value.
### max_idle_conns_default
For SQL data sources (MySql, Postgres, MSSQL) you can override the default allowed number of idle connections (default: 100). The value configured in data source settings will be preferred over the default value.
### max_conn_lifetime_default
For SQL data sources (MySql, Postgres, MSSQL) you can override the default maximum connection lifetime specified in seconds (default: 14400). The value configured in data source settings will be preferred over the default value.
<hr/>
## [users]
### allow_sign_up

View File

@ -369,6 +369,11 @@ type Cfg struct {
// Data sources
DataSourceLimit int
// SQL Data sources
SqlDatasourceMaxOpenConnsDefault int
SqlDatasourceMaxIdleConnsDefault int
SqlDatasourceMaxConnLifetimeDefault int
// Snapshots
SnapshotEnabled bool
ExternalSnapshotUrl string
@ -1116,6 +1121,7 @@ func (cfg *Cfg) Load(args CommandLineArgs) error {
}
cfg.readDataSourcesSettings()
cfg.readSqlDataSourceSettings()
cfg.Storage = readStorageSettings(iniFile)
cfg.Search = readSearchSettings(iniFile)
@ -1848,6 +1854,13 @@ func (cfg *Cfg) readDataSourcesSettings() {
cfg.DataSourceLimit = datasources.Key("datasource_limit").MustInt(5000)
}
func (cfg *Cfg) readSqlDataSourceSettings() {
sqlDatasources := cfg.Raw.Section("sql_datasources")
cfg.SqlDatasourceMaxOpenConnsDefault = sqlDatasources.Key("max_open_conns_default").MustInt(100)
cfg.SqlDatasourceMaxIdleConnsDefault = sqlDatasources.Key("max_idle_conns_default").MustInt(100)
cfg.SqlDatasourceMaxConnLifetimeDefault = sqlDatasources.Key("max_conn_lifetime_default").MustInt(14400)
}
func GetAllowedOriginGlobs(originPatterns []string) ([]glob.Glob, error) {
allowedOrigins := originPatterns
originGlobs := make([]glob.Glob, 0, len(allowedOrigins))

View File

@ -56,9 +56,9 @@ func (s *Service) QueryData(ctx context.Context, req *backend.QueryDataRequest)
func newInstanceSettings(cfg *setting.Cfg) datasource.InstanceFactoryFunc {
return func(settings backend.DataSourceInstanceSettings) (instancemgmt.Instance, error) {
jsonData := sqleng.JsonData{
MaxOpenConns: 0,
MaxIdleConns: 2,
ConnMaxLifetime: 14400,
MaxOpenConns: cfg.SqlDatasourceMaxOpenConnsDefault,
MaxIdleConns: cfg.SqlDatasourceMaxIdleConnsDefault,
ConnMaxLifetime: cfg.SqlDatasourceMaxConnLifetimeDefault,
Encrypt: "false",
ConnectionTimeout: 0,
SecureDSProxy: false,

View File

@ -52,9 +52,9 @@ func ProvideService(cfg *setting.Cfg, httpClientProvider httpclient.Provider) *S
func newInstanceSettings(cfg *setting.Cfg, httpClientProvider httpclient.Provider) datasource.InstanceFactoryFunc {
return func(settings backend.DataSourceInstanceSettings) (instancemgmt.Instance, error) {
jsonData := sqleng.JsonData{
MaxOpenConns: 0,
MaxIdleConns: 2,
ConnMaxLifetime: 14400,
MaxOpenConns: cfg.SqlDatasourceMaxOpenConnsDefault,
MaxIdleConns: cfg.SqlDatasourceMaxIdleConnsDefault,
ConnMaxLifetime: cfg.SqlDatasourceMaxConnLifetimeDefault,
SecureDSProxy: false,
}

View File

@ -56,9 +56,9 @@ func (s *Service) newInstanceSettings(cfg *setting.Cfg) datasource.InstanceFacto
return func(settings backend.DataSourceInstanceSettings) (instancemgmt.Instance, error) {
logger.Debug("Creating Postgres query endpoint")
jsonData := sqleng.JsonData{
MaxOpenConns: 0,
MaxIdleConns: 2,
ConnMaxLifetime: 14400,
MaxOpenConns: cfg.SqlDatasourceMaxOpenConnsDefault,
MaxIdleConns: cfg.SqlDatasourceMaxIdleConnsDefault,
ConnMaxLifetime: cfg.SqlDatasourceMaxConnLifetimeDefault,
Timescaledb: false,
ConfigurationMethod: "file-path",
SecureDSProxy: false,

View File

@ -84,6 +84,13 @@ type DataSourceInfo struct {
DecryptedSecureJSONData map[string]string
}
// Defaults for the xorm connection pool
type DefaultConnectionInfo struct {
MaxOpenConns int
MaxIdleConns int
ConnMaxLifetime int
}
type DataPluginConfiguration struct {
DriverName string
DSInfo DataSourceInfo

View File

@ -11,6 +11,8 @@ interface Props {
min?: number;
max?: number;
step?: number;
width?: number;
fieldDisabled?: boolean;
}
interface State {
@ -107,6 +109,8 @@ export class NumberInput extends PureComponent<Props, State> {
onBlur={this.updateValue}
onKeyPress={this.onKeyPress}
placeholder={this.props.placeholder}
disabled={this.props.fieldDisabled}
width={this.props.width}
/>
);
}

View File

@ -1,27 +1,86 @@
import React from 'react';
import { FieldSet, InlineField } from '@grafana/ui';
import { DataSourceSettings } from '@grafana/data';
import { FieldSet, InlineField, InlineFieldRow, InlineSwitch } from '@grafana/ui';
import { NumberInput } from 'app/core/components/OptionsUI/NumberInput';
import { SQLConnectionLimits } from '../../types';
import { SQLConnectionDefaults } from '../../constants';
import { SQLConnectionLimits, SQLOptions } from '../../types';
interface Props<T> {
onPropertyChanged: (property: keyof T, value?: number) => void;
onOptionsChange: Function;
options: DataSourceSettings<SQLOptions>;
labelWidth: number;
jsonData: SQLConnectionLimits;
}
export const ConnectionLimits = <T extends SQLConnectionLimits>(props: Props<T>) => {
const { onPropertyChanged, labelWidth, jsonData } = props;
const { onOptionsChange, options, labelWidth } = props;
const jsonData = options.jsonData;
const autoIdle = jsonData.maxIdleConnsAuto !== undefined ? jsonData.maxIdleConnsAuto : false;
// Update JSON data with new values
const updateJsonData = (values: {}) => {
const newOpts = {
...options,
jsonData: {
...jsonData,
...values,
},
};
return onOptionsChange(newOpts);
};
// For the case of idle connections and connection lifetime
// use a shared function to update respective properties
const onJSONDataNumberChanged = (property: keyof SQLConnectionLimits) => {
return (number?: number) => {
if (onPropertyChanged) {
onPropertyChanged(property, number);
}
updateJsonData({ property: number });
};
};
// When the maximum number of connections is changed
// see if we have the automatic idle option enabled
const onMaxConnectionsChanged = (number?: number) => {
if (autoIdle && number) {
updateJsonData({
maxOpenConns: number,
maxIdleConns: number,
});
} else if (number !== undefined) {
updateJsonData({
maxOpenConns: number,
});
}
};
// Update auto idle setting when control is toggled
// and set minimum idle connections if automatic
// is selected
const onConnectionIdleAutoChanged = () => {
let idleConns = undefined;
let maxConns = undefined;
// If the maximum number of open connections is undefined
// and we're setting auto idle then set the default amount
// otherwise take the numeric amount and get the value from that
if (!autoIdle) {
if (jsonData.maxOpenConns !== undefined) {
maxConns = jsonData.maxOpenConns;
idleConns = jsonData.maxOpenConns;
} else {
maxConns = SQLConnectionDefaults.MAX_CONNS;
idleConns = SQLConnectionDefaults.MAX_CONNS;
}
}
updateJsonData({
maxIdleConnsAuto: !autoIdle,
maxIdleConns: idleConns,
maxOpenConns: maxConns,
});
};
return (
<FieldSet label="Connection limits">
<InlineField
@ -36,29 +95,42 @@ export const ConnectionLimits = <T extends SQLConnectionLimits>(props: Props<T>)
labelWidth={labelWidth}
label="Max open"
>
<NumberInput
placeholder="unlimited"
value={jsonData.maxOpenConns}
onChange={onJSONDataNumberChanged('maxOpenConns')}
></NumberInput>
</InlineField>
<InlineField
tooltip={
<span>
The maximum number of connections in the idle connection pool.If <i>Max open connections</i> is greater than
0 but less than the <i>Max idle connections</i>, then the <i>Max idle connections</i> will be reduced to
match the <i>Max open connections</i> limit. If set to 0, no idle connections are retained.
</span>
}
labelWidth={labelWidth}
label="Max idle"
>
<NumberInput
placeholder="2"
value={jsonData.maxIdleConns}
onChange={onJSONDataNumberChanged('maxIdleConns')}
></NumberInput>
<NumberInput placeholder="unlimited" value={jsonData.maxOpenConns} onChange={onMaxConnectionsChanged} />
</InlineField>
<InlineFieldRow>
<InlineField
tooltip={
<span>
The maximum number of connections in the idle connection pool.If <i>Max open connections</i> is greater
than 0 but less than the <i>Max idle connections</i>, then the <i>Max idle connections</i> will be reduced
to match the <i>Max open connections</i> limit. If set to 0, no idle connections are retained.
</span>
}
labelWidth={labelWidth}
label="Max idle"
>
<NumberInput
placeholder="2"
value={jsonData.maxIdleConns}
onChange={onJSONDataNumberChanged('maxIdleConns')}
width={8}
fieldDisabled={autoIdle}
/>
</InlineField>
<InlineField
label="Auto"
labelWidth={8}
tooltip={
<span>
If enabled, automatically set the number of <i>Maximum idle connections</i> to the same value as
<i> Max open connections</i>. If the number of maximum open connections is not set it will be set to the
default ({SQLConnectionDefaults.MAX_CONNS}).
</span>
}
>
<InlineSwitch value={autoIdle} onChange={onConnectionIdleAutoChanged} />
</InlineField>
</InlineFieldRow>
<InlineField
tooltip="The maximum amount of time in seconds a connection may be reused. If set to 0, connections are reused forever."
labelWidth={labelWidth}

View File

@ -1,25 +0,0 @@
import { useEffect } from 'react';
import { DataSourceJsonData, DataSourcePluginOptionsEditorProps } from '@grafana/data';
import { logDebug } from '@grafana/runtime';
import { SQLOptions } from '../../types';
/**
* Moves the database field from the options object to jsonData.database and empties the database field.
*/
export function useMigrateDatabaseField<T extends DataSourceJsonData = SQLOptions, S = {}>({
onOptionsChange,
options,
}: DataSourcePluginOptionsEditorProps<T, S>) {
useEffect(() => {
if (options.database) {
logDebug(`Migrating from options.database with value ${options.database} for ${options.name}`);
onOptionsChange({
...options,
database: '',
jsonData: { ...options.jsonData, database: options.database },
});
}
}, [onOptionsChange, options]);
}

View File

@ -0,0 +1,71 @@
import { renderHook } from '@testing-library/react-hooks';
import { DataSourceSettings } from '@grafana/data';
import { SQLConnectionDefaults } from '../../constants';
import { SQLOptions } from '../../types';
import { useMigrateDatabaseFields } from './useMigrateDatabaseFields';
describe('Database Field Migration', () => {
let defaultProps = {
options: {
database: 'testDatabase',
id: 1,
uid: 'unique-id',
orgId: 1,
name: 'Datasource Name',
type: 'postgres',
typeName: 'Postgres',
typeLogoUrl: 'http://example.com/logo.png',
access: 'access',
url: 'http://example.com',
user: 'user',
basicAuth: true,
basicAuthUser: 'user',
isDefault: false,
secureJsonFields: {},
readOnly: false,
withCredentials: false,
jsonData: {
tlsAuth: false,
tlsAuthWithCACert: false,
timezone: 'America/Chicago',
tlsSkipVerify: false,
user: 'user',
},
},
};
it('should migrate the old database field to be included in jsonData', () => {
const props = {
...defaultProps,
onOptionsChange: (options: DataSourceSettings) => {
const jsonData = options.jsonData as SQLOptions;
expect(options.database).toBe('');
expect(jsonData.database).toBe('testDatabase');
},
};
// @ts-ignore Ignore this line as it's expected that
// the database object will not be in necessary (most current) state
const { rerender, result } = renderHook(() => useMigrateDatabaseFields(props));
rerender();
});
it('adds default max connection, max idle connection, and auto idle values when not detected', () => {
const props = {
...defaultProps,
onOptionsChange: (options: DataSourceSettings) => {
const jsonData = options.jsonData as SQLOptions;
expect(jsonData.maxOpenConns).toBe(SQLConnectionDefaults.MAX_CONNS);
expect(jsonData.maxIdleConns).toBe(Math.ceil(SQLConnectionDefaults.MAX_CONNS));
expect(jsonData.maxIdleConnsAuto).toBe(true);
},
};
// @ts-ignore Ignore this line as it's expected that
// the database object will not be in the expected (most current) state
const { rerender, result } = renderHook(() => useMigrateDatabaseFields(props));
rerender();
});
});

View File

@ -0,0 +1,63 @@
import { useEffect } from 'react';
import { DataSourcePluginOptionsEditorProps } from '@grafana/data';
import { logDebug } from '@grafana/runtime';
import { SQLConnectionDefaults } from '../../constants';
import { SQLOptions } from '../../types';
/**
* 1. Moves the database field from the options object to jsonData.database and empties the database field.
* 2. If max open connections, max idle connections, and auto idle are all undefined set these to default values.
*/
export function useMigrateDatabaseFields<T extends SQLOptions, S = {}>({
onOptionsChange,
options,
}: DataSourcePluginOptionsEditorProps<T, S>) {
useEffect(() => {
const jsonData = options.jsonData;
let newOptions = { ...options };
let optionsUpdated = false;
// Migrate the database field from the column into the jsonData object
if (options.database) {
logDebug(`Migrating from options.database with value ${options.database} for ${options.name}`);
newOptions.database = '';
newOptions.jsonData = { ...jsonData, database: options.database };
optionsUpdated = true;
}
// Set default values for max open connections, max idle connection,
// and auto idle if they're all undefined
if (
jsonData.maxOpenConns === undefined &&
jsonData.maxIdleConns === undefined &&
jsonData.maxIdleConnsAuto === undefined
) {
// It's expected that the default will be greater than 4
const maxOpenConns = SQLConnectionDefaults.MAX_CONNS;
const maxIdleConns = maxOpenConns;
logDebug(
`Setting default max open connections to ${maxOpenConns} and setting max idle connection to ${maxIdleConns}`
);
// Spread from the jsonData in new options in case
// the database field was migrated as well
newOptions.jsonData = {
...newOptions.jsonData,
maxOpenConns: maxOpenConns,
maxIdleConns: maxIdleConns,
maxIdleConnsAuto: true,
};
// Make sure we issue an update if options changed
optionsUpdated = true;
}
// Only issue an update if we changed options
if (optionsUpdated) {
onOptionsChange(newOptions);
}
}, [onOptionsChange, options]);
}

View File

@ -15,3 +15,11 @@ export const MACRO_NAMES = [
'$__unixEpochGroup',
'$__unixEpochGroupAlias',
];
/**
* Constants for SQL connection
* parameters and automatic settings
*/
export const SQLConnectionDefaults = {
MAX_CONNS: 100,
};

View File

@ -30,6 +30,7 @@ export interface SqlQueryForInterpolation {
export interface SQLConnectionLimits {
maxOpenConns: number;
maxIdleConns: number;
maxIdleConnsAuto: boolean;
connMaxLifetime: number;
}

View File

@ -26,7 +26,7 @@ import {
import { NumberInput } from 'app/core/components/OptionsUI/NumberInput';
import { config } from 'app/core/config';
import { ConnectionLimits } from 'app/features/plugins/sql/components/configuration/ConnectionLimits';
import { useMigrateDatabaseField } from 'app/features/plugins/sql/components/configuration/useMigrateDatabaseField';
import { useMigrateDatabaseFields } from 'app/features/plugins/sql/components/configuration/useMigrateDatabaseFields';
import { MSSQLAuthenticationType, MSSQLEncryptOptions, MssqlOptions } from '../types';
@ -35,7 +35,7 @@ export const ConfigurationEditor = (props: DataSourcePluginOptionsEditorProps<Ms
const styles = useStyles2(getStyles);
const jsonData = options.jsonData;
useMigrateDatabaseField(props);
useMigrateDatabaseFields(props);
const onResetPassword = () => {
updateDatasourcePluginResetOption(props, 'password');
@ -232,13 +232,7 @@ export const ConfigurationEditor = (props: DataSourcePluginOptionsEditorProps<Ms
) : null}
</FieldSet>
<ConnectionLimits
labelWidth={shortWidth}
jsonData={jsonData}
onPropertyChanged={(property, value) => {
updateDatasourcePluginJsonDataOption(props, property, value);
}}
></ConnectionLimits>
<ConnectionLimits labelWidth={shortWidth} options={options} onOptionsChange={onOptionsChange} />
<FieldSet label="MS SQL details">
<InlineField

View File

@ -21,7 +21,7 @@ import {
import { config } from 'app/core/config';
import { ConnectionLimits } from 'app/features/plugins/sql/components/configuration/ConnectionLimits';
import { TLSSecretsConfig } from 'app/features/plugins/sql/components/configuration/TLSSecretsConfig';
import { useMigrateDatabaseField } from 'app/features/plugins/sql/components/configuration/useMigrateDatabaseField';
import { useMigrateDatabaseFields } from 'app/features/plugins/sql/components/configuration/useMigrateDatabaseFields';
import { MySQLOptions } from '../types';
@ -29,7 +29,7 @@ export const ConfigurationEditor = (props: DataSourcePluginOptionsEditorProps<My
const { options, onOptionsChange } = props;
const jsonData = options.jsonData;
useMigrateDatabaseField(props);
useMigrateDatabaseFields(props);
const onResetPassword = () => {
updateDatasourcePluginResetOption(props, 'password');
@ -166,13 +166,7 @@ export const ConfigurationEditor = (props: DataSourcePluginOptionsEditorProps<My
</FieldSet>
) : null}
<ConnectionLimits
labelWidth={WIDTH_SHORT}
jsonData={jsonData}
onPropertyChanged={(property, value) => {
updateDatasourcePluginJsonDataOption(props, property, value);
}}
></ConnectionLimits>
<ConnectionLimits labelWidth={WIDTH_SHORT} options={options} onOptionsChange={onOptionsChange} />
<FieldSet label="MySQL details">
<InlineField

View File

@ -22,7 +22,7 @@ import {
import { config } from 'app/core/config';
import { ConnectionLimits } from 'app/features/plugins/sql/components/configuration/ConnectionLimits';
import { TLSSecretsConfig } from 'app/features/plugins/sql/components/configuration/TLSSecretsConfig';
import { useMigrateDatabaseField } from 'app/features/plugins/sql/components/configuration/useMigrateDatabaseField';
import { useMigrateDatabaseFields } from 'app/features/plugins/sql/components/configuration/useMigrateDatabaseFields';
import { PostgresOptions, PostgresTLSMethods, PostgresTLSModes, SecureJsonData } from '../types';
@ -49,7 +49,7 @@ export const PostgresConfigEditor = (props: DataSourcePluginOptionsEditorProps<P
useAutoDetectFeatures({ props, setVersionOptions });
useMigrateDatabaseField(props);
useMigrateDatabaseFields(props);
const { options, onOptionsChange } = props;
const jsonData = options.jsonData;
@ -246,13 +246,7 @@ export const PostgresConfigEditor = (props: DataSourcePluginOptionsEditorProps<P
</FieldSet>
) : null}
<ConnectionLimits
labelWidth={labelWidthShort}
jsonData={jsonData}
onPropertyChanged={(property, value) => {
updateDatasourcePluginJsonDataOption(props, property, value);
}}
></ConnectionLimits>
<ConnectionLimits labelWidth={labelWidthShort} options={options} onOptionsChange={onOptionsChange} />
<FieldSet label="PostgreSQL details">
<InlineField