console: add new connection params to connect database form

GitOrigin-RevId: 073d30e50a196e265cd8dfb7cfb3c9af00c3fc0e
This commit is contained in:
Abhijeet Khangarot 2021-05-25 18:32:15 +05:30 committed by hasura-bot
parent 3bcf758e14
commit d0a66e5172
13 changed files with 181 additions and 35 deletions

View File

@ -6,6 +6,8 @@
### Bug fixes and improvements
(Add entries below in the order of server, console, cli, docs, others)
- console: add `pool_timeout`, `connection_lifetime` and `isolation_level` connection params to connect database form
## v2.0.0-alpha.11
### Breaking Changes

View File

@ -1,15 +1,18 @@
import React, { InputHTMLAttributes } from 'react';
import styles from '../../Common/Common.scss';
import Tooltip from '../Tooltip/Tooltip';
interface LabeledInputProps extends InputHTMLAttributes<HTMLInputElement> {
label: string;
boldlabel?: boolean;
tooltipText?: string;
}
export const LabeledInput: React.FC<LabeledInputProps> = props => (
<>
<label className={props.boldlabel ? '' : styles.connect_db_input_label}>
{props?.boldlabel ? <b>{props.label}</b> : props.label}
{props.tooltipText && <Tooltip message={props.tooltipText} />}
</label>
<input
type="text"

View File

@ -86,6 +86,7 @@ const ConnectDatabase: React.FC<ConnectDatabaseProps> = props => {
),
connectionSettings: connectionInfo?.pool_settings ?? {},
preparedStatements: connectionInfo?.use_prepared_statements ?? false,
isolationLevel: connectionInfo?.isolation_level ?? 'read-committed',
},
});

View File

@ -6,6 +6,7 @@ import Tooltip from '../../../Common/Tooltip/Tooltip';
import { getSupportedDrivers } from '../../../../dataSources';
import styles from './DataSources.scss';
import { IsolationLevelOptions } from '../../../../metadata/types';
export interface ConnectionSettingsFormProps {
// Connect DB State Props
@ -50,6 +51,7 @@ const ConnectionSettingsForm: React.FC<ConnectionSettingsFormProps> = ({
<div className={styles.connection_settings_input_layout}>
<LabeledInput
label="Max Connections"
tooltipText="Maximum number of connections to be kept in the pool"
type="number"
className={`form-control ${styles.connnection_settings_form_input}`}
placeholder="50"
@ -71,6 +73,7 @@ const ConnectionSettingsForm: React.FC<ConnectionSettingsFormProps> = ({
<div className={styles.connection_settings_input_layout}>
<LabeledInput
label="Idle Timeout"
tooltipText="The idle timeout (in seconds) per connection"
type="number"
className={`form-control ${styles.connnection_settings_form_input}`}
placeholder="180"
@ -95,6 +98,7 @@ const ConnectionSettingsForm: React.FC<ConnectionSettingsFormProps> = ({
<div className={styles.connection_settings_input_layout}>
<LabeledInput
label="Retries"
tooltipText="Number of retries to perform"
type="number"
className={`form-control ${styles.connnection_settings_form_input}`}
placeholder="1"
@ -113,6 +117,82 @@ const ConnectionSettingsForm: React.FC<ConnectionSettingsFormProps> = ({
/>
</div>
) : null}
{getSupportedDrivers('connectDbForm.pool_timeout').includes(
connectionDBState.dbType
) ? (
<div className={styles.connection_settings_input_layout}>
<LabeledInput
label="Pool Timeout"
tooltipText="Maximum time (in seconds) to wait while acquiring a Postgres connection from the pool"
type="number"
className={`form-control ${styles.connnection_settings_form_input}`}
placeholder="360"
value={
connectionDBState.connectionSettings?.pool_timeout ??
undefined
}
onChange={e =>
connectionDBStateDispatch({
type: 'UPDATE_POOL_TIMEOUT',
data: e.target.value,
})
}
min="0"
boldlabel
data-test="pool-timeout"
/>
</div>
) : null}
{getSupportedDrivers(
'connectDbForm.connection_lifetime'
).includes(connectionDBState.dbType) ? (
<div className={styles.connection_settings_input_layout}>
<LabeledInput
label="Connection Lifetime"
tooltipText="Time (in seconds) from connection creation after which the connection should be destroyed and a new one created. A value of 0 indicates we should never destroy an active connection. If 0 is passed, memory from large query results may not be reclaimed."
type="number"
className={`form-control ${styles.connnection_settings_form_input}`}
placeholder="600"
value={
connectionDBState.connectionSettings
?.connection_lifetime ?? undefined
}
onChange={e =>
connectionDBStateDispatch({
type: 'UPDATE_CONNECTION_LIFETIME',
data: e.target.value,
})
}
min="0"
boldlabel
data-test="connection-lifetime"
/>
</div>
) : null}
{getSupportedDrivers('connectDbForm.isolation_level').includes(
connectionDBState.dbType
) && (
<div className={styles.connection_settings_input_layout}>
<label>
<b>Isolation Level</b>
<Tooltip message="The transaction isolation level in which the queries made to the source will be run" />
</label>
<select
className={`form-control ${styles.connnection_settings_form_input}`}
onChange={e =>
connectionDBStateDispatch({
type: 'UPDATE_ISOLATION_LEVEL',
data: e.target.value as IsolationLevelOptions,
})
}
value={connectionDBState.isolationLevel}
>
<option value="read-committed">read-committed</option>
<option value="repeatable-read">repeatable-read</option>
<option value="serializable">serializable</option>
</select>
</div>
)}
{getSupportedDrivers(
'connectDbForm.prepared_statements'
).includes(connectionDBState.dbType) ? (

View File

@ -2,7 +2,11 @@ import { Driver, getSupportedDrivers } from '../../../../dataSources';
import { makeConnectionStringFromConnectionParams } from './ManageDBUtils';
import { addDataSource } from '../../../../metadata/actions';
import { Dispatch } from '../../../../types';
import { SourceConnectionInfo } from '../../../../metadata/types';
import {
ConnectionPoolSettings,
IsolationLevelOptions,
SourceConnectionInfo,
} from '../../../../metadata/types';
export const connectionTypes = {
DATABASE_URL: 'DATABASE_URL',
@ -10,12 +14,6 @@ export const connectionTypes = {
ENV_VAR: 'ENVIRONMENT_VARIABLES',
};
type ConnectionSettings = {
max_connections?: number;
idle_timeout?: number;
retries?: number;
};
type ConnectionParams = {
host: string;
port: string;
@ -37,7 +35,8 @@ export type ConnectDBState = {
envVarState: {
envVar: string;
};
connectionSettings: ConnectionSettings;
connectionSettings: ConnectionPoolSettings;
isolationLevel?: IsolationLevelOptions;
preparedStatements?: boolean;
};
@ -62,6 +61,7 @@ export const defaultState: ConnectDBState = {
},
connectionSettings: {},
preparedStatements: false,
isolationLevel: 'read-committed',
};
type DefaultStateProps = {
@ -97,7 +97,7 @@ export const connectDataSource = (
cb: () => void,
replicas?: Omit<
SourceConnectionInfo,
'connection_string' | 'use_prepared_statements'
'connection_string' | 'use_prepared_statements' | 'isolation_level'
>[],
isEditState = false
) => {
@ -139,6 +139,7 @@ export const connectDataSource = (
datasets: currentState.databaseURLState.datasets,
},
preparedStatements: currentState.preparedStatements,
isolationLevel: currentState.isolationLevel,
},
},
cb,
@ -154,8 +155,9 @@ export type ConnectDBActions =
name: string;
driver: Driver;
databaseUrl: string;
connectionSettings: ConnectionSettings;
connectionSettings: ConnectionPoolSettings;
preparedStatements: boolean;
isolationLevel: IsolationLevelOptions;
};
}
| { type: 'UPDATE_PARAM_STATE'; data: ConnectionParams }
@ -173,9 +175,12 @@ export type ConnectDBActions =
| { type: 'UPDATE_MAX_CONNECTIONS'; data: string }
| { type: 'UPDATE_RETRIES'; data: string }
| { type: 'UPDATE_IDLE_TIMEOUT'; data: string }
| { type: 'UPDATE_POOL_TIMEOUT'; data: string }
| { type: 'UPDATE_CONNECTION_LIFETIME'; data: string }
| { type: 'UPDATE_DB_DRIVER'; data: Driver }
| { type: 'UPDATE_CONNECTION_SETTINGS'; data: ConnectionSettings }
| { type: 'UPDATE_CONNECTION_SETTINGS'; data: ConnectionPoolSettings }
| { type: 'UPDATE_PREPARED_STATEMENTS'; data: boolean }
| { type: 'UPDATE_ISOLATION_LEVEL'; data: IsolationLevelOptions }
| { type: 'RESET_INPUT_STATE' };
export const connectDBReducer = (
@ -194,6 +199,7 @@ export const connectDBReducer = (
},
connectionSettings: action.data.connectionSettings,
preparedStatements: action.data.preparedStatements,
isolationLevel: action.data.isolationLevel,
};
case 'UPDATE_PARAM_STATE':
return {
@ -293,11 +299,32 @@ export const connectDBReducer = (
idle_timeout: setNumberFromString(action.data),
},
};
case 'UPDATE_POOL_TIMEOUT':
return {
...state,
connectionSettings: {
...state.connectionSettings,
pool_timeout: setNumberFromString(action.data),
},
};
case 'UPDATE_CONNECTION_LIFETIME':
return {
...state,
connectionSettings: {
...state.connectionSettings,
connection_lifetime: setNumberFromString(action.data),
},
};
case 'UPDATE_CONNECTION_SETTINGS':
return {
...state,
connectionSettings: action.data,
};
case 'UPDATE_ISOLATION_LEVEL':
return {
...state,
isolationLevel: action.data,
};
case 'UPDATE_PREPARED_STATEMENTS':
return {
...state,

View File

@ -69,11 +69,25 @@ const CollapsibleToggle: React.FC<CollapsibleToggleProps> = ({
</div>
) : null}
{dataSource.connection_pool_settings?.retries ? (
<div>
<div className={styles.add_mar_bottom_mid}>
<span className={styles.dataSource_label}>Retries</span>
{dataSource.connection_pool_settings?.retries}
</div>
) : null}
{dataSource.connection_pool_settings?.pool_timeout ? (
<div className={styles.add_mar_bottom_mid}>
<span className={styles.dataSource_label}>Pool Timeout</span>
{dataSource.connection_pool_settings?.pool_timeout}
</div>
) : null}
{dataSource.connection_pool_settings?.connection_lifetime ? (
<div>
<span className={styles.dataSource_label}>
Connection Lifetime
</span>
{dataSource.connection_pool_settings?.connection_lifetime}
</div>
) : null}
</>
) : (
''

View File

@ -158,8 +158,11 @@ export const supportedFeatures: SupportedFeaturesType = {
environmentVariable: true,
read_replicas: false,
prepared_statements: false,
isolation_level: false,
connectionSettings: false,
retries: false,
pool_timeout: false,
connection_lifetime: false,
},
};

View File

@ -164,8 +164,11 @@ export const supportedFeatures: SupportedFeaturesType = {
environmentVariable: true,
read_replicas: false,
prepared_statements: false,
isolation_level: false,
connectionSettings: true,
retries: false,
pool_timeout: false,
connection_lifetime: false,
},
};

View File

@ -587,8 +587,11 @@ export const supportedFeatures: SupportedFeaturesType = {
environmentVariable: true,
read_replicas: true,
prepared_statements: true,
isolation_level: true,
connectionSettings: true,
retries: true,
pool_timeout: true,
connection_lifetime: true,
},
};

View File

@ -295,8 +295,11 @@ export type SupportedFeaturesType = {
environmentVariable: boolean;
read_replicas: boolean;
prepared_statements: boolean;
isolation_level: boolean;
connectionSettings: boolean;
retries: boolean;
pool_timeout: boolean;
connection_lifetime: boolean;
};
};

View File

@ -1,7 +1,9 @@
import requestAction from '../utils/requestAction';
import Endpoints, { globalCookiePolicy } from '../Endpoints';
import {
ConnectionPoolSettings,
HasuraMetadataV3,
IsolationLevelOptions,
RestEndpointEntry,
SourceConnectionInfo,
} from './types';
@ -118,17 +120,14 @@ export interface AddDataSourceRequest {
payload: {
name: string;
dbUrl: string | { from_env: string };
connection_pool_settings: {
max_connections?: number;
idle_timeout?: number; // in seconds
retries?: number;
};
connection_pool_settings: ConnectionPoolSettings;
replace_configuration?: boolean;
bigQuery: {
projectId: string;
datasets: string;
};
preparedStatements?: boolean;
isolationLevel?: IsolationLevelOptions;
};
};
}
@ -244,7 +243,7 @@ export const addDataSource = (
successCb: () => void,
replicas?: Omit<
SourceConnectionInfo,
'connection_string' | 'use_prepared_statements'
'connection_string' | 'use_prepared_statements' | 'isolation_level'
>[],
skipNotification = false
): Thunk<Promise<void | ReduxState>, MetadataActions> => (

View File

@ -1,27 +1,28 @@
import { Driver } from '../dataSources';
import { SourceConnectionInfo } from './types';
import {
ConnectionPoolSettings,
IsolationLevelOptions,
SourceConnectionInfo,
} from './types';
export const addSource = (
driver: Driver,
payload: {
name: string;
dbUrl: string | { from_env: string };
connection_pool_settings?: {
max_connections?: number;
idle_timeout?: number;
retries?: number;
};
connection_pool_settings?: ConnectionPoolSettings;
replace_configuration?: boolean;
bigQuery: {
projectId: string;
datasets: string;
};
preparedStatements?: boolean;
isolationLevel?: IsolationLevelOptions;
},
// supported only for PG sources at the moment
replicas?: Omit<
SourceConnectionInfo,
'connection_string' | 'use_prepared_statements'
'connection_string' | 'use_prepared_statements' | 'isolation_level'
>[]
) => {
const replace_configuration = payload.replace_configuration ?? false;
@ -69,6 +70,7 @@ export const addSource = (
database_url: payload.dbUrl,
pool_settings: payload.connection_pool_settings,
use_prepared_statements: payload.preparedStatements,
isolation_level: payload.isolationLevel,
},
read_replicas: replicas?.length ? replicas : null,
},

View File

@ -5,11 +5,7 @@ export type DataSource = {
name: string;
url: string | { from_env: string };
driver: Driver;
connection_pool_settings?: {
max_connections?: number;
idle_timeout?: number;
retries?: number;
};
connection_pool_settings?: ConnectionPoolSettings;
read_replicas?: Omit<SourceConnectionInfo, 'connection_string'>[];
};
@ -882,17 +878,27 @@ export interface RestEndpointEntry {
* Docs for type: https://hasura.io/docs/latest/graphql/core/api-reference/syntax-defs.html#pgsourceconnectioninfo
*/
export interface ConnectionPoolSettings {
max_connections?: number;
idle_timeout?: number;
retries?: number;
pool_timeout?: number;
connection_lifetime?: number;
}
export type IsolationLevelOptions =
| 'read-committed'
| 'repeatable-read'
| 'serializable';
export interface SourceConnectionInfo {
// used for SQL Server
connection_string: string | { from_env: string };
// used for Postgres
database_url: string | { from_env: string };
pool_settings: {
max_connections: number;
idle_timeout: number;
retries: number;
};
pool_settings: ConnectionPoolSettings;
use_prepared_statements: boolean;
isolation_level: IsolationLevelOptions;
}
/**