diff --git a/conf/defaults.ini b/conf/defaults.ini index 1a727c18fb6..c298b25f728 100644 --- a/conf/defaults.ini +++ b/conf/defaults.ini @@ -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 diff --git a/docs/sources/datasources/mssql/_index.md b/docs/sources/datasources/mssql/_index.md index 35af22224d0..c93c255cafc 100644 --- a/docs/sources/datasources/mssql/_index.md +++ b/docs/sources/datasources/mssql/_index.md @@ -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: diff --git a/docs/sources/datasources/mysql/_index.md b/docs/sources/datasources/mysql/_index.md index d86dcf60976..33ad8a30578 100644 --- a/docs/sources/datasources/mysql/_index.md +++ b/docs/sources/datasources/mysql/_index.md @@ -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} diff --git a/docs/sources/datasources/postgres/_index.md b/docs/sources/datasources/postgres/_index.md index f2a90da7fde..82db22b8908 100644 --- a/docs/sources/datasources/postgres/_index.md +++ b/docs/sources/datasources/postgres/_index.md @@ -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 diff --git a/docs/sources/setup-grafana/configure-grafana/_index.md b/docs/sources/setup-grafana/configure-grafana/_index.md index 3f61ed8b758..1d1e3f96fe1 100644 --- a/docs/sources/setup-grafana/configure-grafana/_index.md +++ b/docs/sources/setup-grafana/configure-grafana/_index.md @@ -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
+## [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. + +
+ ## [users] ### allow_sign_up diff --git a/pkg/setting/setting.go b/pkg/setting/setting.go index bbf734ecce8..06732a6d056 100644 --- a/pkg/setting/setting.go +++ b/pkg/setting/setting.go @@ -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)) diff --git a/pkg/tsdb/mssql/mssql.go b/pkg/tsdb/mssql/mssql.go index 086bd33c47c..616bb37da56 100644 --- a/pkg/tsdb/mssql/mssql.go +++ b/pkg/tsdb/mssql/mssql.go @@ -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, diff --git a/pkg/tsdb/mysql/mysql.go b/pkg/tsdb/mysql/mysql.go index 502a7163f54..eca156dd57b 100644 --- a/pkg/tsdb/mysql/mysql.go +++ b/pkg/tsdb/mysql/mysql.go @@ -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, } diff --git a/pkg/tsdb/postgres/postgres.go b/pkg/tsdb/postgres/postgres.go index 6a931b6cbb3..f3684591ff2 100644 --- a/pkg/tsdb/postgres/postgres.go +++ b/pkg/tsdb/postgres/postgres.go @@ -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, diff --git a/pkg/tsdb/sqleng/sql_engine.go b/pkg/tsdb/sqleng/sql_engine.go index 8e53d77dd8e..aeb93d411d1 100644 --- a/pkg/tsdb/sqleng/sql_engine.go +++ b/pkg/tsdb/sqleng/sql_engine.go @@ -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 diff --git a/public/app/core/components/OptionsUI/NumberInput.tsx b/public/app/core/components/OptionsUI/NumberInput.tsx index c507b421f48..159a0d550d3 100644 --- a/public/app/core/components/OptionsUI/NumberInput.tsx +++ b/public/app/core/components/OptionsUI/NumberInput.tsx @@ -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 { onBlur={this.updateValue} onKeyPress={this.onKeyPress} placeholder={this.props.placeholder} + disabled={this.props.fieldDisabled} + width={this.props.width} /> ); } diff --git a/public/app/features/plugins/sql/components/configuration/ConnectionLimits.tsx b/public/app/features/plugins/sql/components/configuration/ConnectionLimits.tsx index 602365c9954..ed7d4cbd0d8 100644 --- a/public/app/features/plugins/sql/components/configuration/ConnectionLimits.tsx +++ b/public/app/features/plugins/sql/components/configuration/ConnectionLimits.tsx @@ -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 { - onPropertyChanged: (property: keyof T, value?: number) => void; + onOptionsChange: Function; + options: DataSourceSettings; labelWidth: number; - jsonData: SQLConnectionLimits; } export const ConnectionLimits = (props: Props) => { - 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 (
(props: Props) labelWidth={labelWidth} label="Max open" > - - - - The maximum number of connections in the idle connection pool.If Max open connections is greater than - 0 but less than the Max idle connections, then the Max idle connections will be reduced to - match the Max open connections limit. If set to 0, no idle connections are retained. - - } - labelWidth={labelWidth} - label="Max idle" - > - + + + + The maximum number of connections in the idle connection pool.If Max open connections is greater + than 0 but less than the Max idle connections, then the Max idle connections will be reduced + to match the Max open connections limit. If set to 0, no idle connections are retained. + + } + labelWidth={labelWidth} + label="Max idle" + > + + + + If enabled, automatically set the number of Maximum idle connections to the same value as + Max open connections. If the number of maximum open connections is not set it will be set to the + default ({SQLConnectionDefaults.MAX_CONNS}). + + } + > + + + ({ - onOptionsChange, - options, -}: DataSourcePluginOptionsEditorProps) { - 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]); -} diff --git a/public/app/features/plugins/sql/components/configuration/useMigrateDatabaseFields.test.ts b/public/app/features/plugins/sql/components/configuration/useMigrateDatabaseFields.test.ts new file mode 100644 index 00000000000..a687b85c304 --- /dev/null +++ b/public/app/features/plugins/sql/components/configuration/useMigrateDatabaseFields.test.ts @@ -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(); + }); +}); diff --git a/public/app/features/plugins/sql/components/configuration/useMigrateDatabaseFields.ts b/public/app/features/plugins/sql/components/configuration/useMigrateDatabaseFields.ts new file mode 100644 index 00000000000..cf587f1b938 --- /dev/null +++ b/public/app/features/plugins/sql/components/configuration/useMigrateDatabaseFields.ts @@ -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({ + onOptionsChange, + options, +}: DataSourcePluginOptionsEditorProps) { + 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]); +} diff --git a/public/app/features/plugins/sql/constants.ts b/public/app/features/plugins/sql/constants.ts index 0d3466aecc6..af6a4758786 100644 --- a/public/app/features/plugins/sql/constants.ts +++ b/public/app/features/plugins/sql/constants.ts @@ -15,3 +15,11 @@ export const MACRO_NAMES = [ '$__unixEpochGroup', '$__unixEpochGroupAlias', ]; + +/** + * Constants for SQL connection + * parameters and automatic settings + */ +export const SQLConnectionDefaults = { + MAX_CONNS: 100, +}; diff --git a/public/app/features/plugins/sql/types.ts b/public/app/features/plugins/sql/types.ts index 325f7b6ea24..c930d49261e 100644 --- a/public/app/features/plugins/sql/types.ts +++ b/public/app/features/plugins/sql/types.ts @@ -30,6 +30,7 @@ export interface SqlQueryForInterpolation { export interface SQLConnectionLimits { maxOpenConns: number; maxIdleConns: number; + maxIdleConnsAuto: boolean; connMaxLifetime: number; } diff --git a/public/app/plugins/datasource/mssql/configuration/ConfigurationEditor.tsx b/public/app/plugins/datasource/mssql/configuration/ConfigurationEditor.tsx index e095e3173ad..635d3f9fbfe 100644 --- a/public/app/plugins/datasource/mssql/configuration/ConfigurationEditor.tsx +++ b/public/app/plugins/datasource/mssql/configuration/ConfigurationEditor.tsx @@ -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 { updateDatasourcePluginResetOption(props, 'password'); @@ -232,13 +232,7 @@ export const ConfigurationEditor = (props: DataSourcePluginOptionsEditorProps - { - updateDatasourcePluginJsonDataOption(props, property, value); - }} - > +
{ updateDatasourcePluginResetOption(props, 'password'); @@ -166,13 +166,7 @@ export const ConfigurationEditor = (props: DataSourcePluginOptionsEditorProps ) : null} - { - updateDatasourcePluginJsonDataOption(props, property, value); - }} - > +
) : null} - { - updateDatasourcePluginJsonDataOption(props, property, value); - }} - > +