diff --git a/src/components/configurationForm.vue b/src/components/configurationForm.vue index a42d8b0..f3810ea 100644 --- a/src/components/configurationForm.vue +++ b/src/components/configurationForm.vue @@ -53,23 +53,23 @@ const osTypes = ref([ const dbTypes = ref([ { - label: 'Web application', + label: 'Web-приложение', value: DB_TYPE_WEB, }, { - label: 'Online transaction processing system', + label: 'Система обработки онлайн-транзакций', value: DB_TYPE_OLTP, }, { - label: 'Data warehouse', + label: 'Хранилище данных', value: DB_TYPE_DW, }, { - label: 'Desktop application', + label: 'Десктопное приложение', value: DB_TYPE_DESKTOP, }, { - label: 'Mixed type of application', + label: 'Приложение смешанного типа', value: DB_TYPE_MIXED, }, ]) @@ -78,26 +78,26 @@ const ramUnits = ref([SIZE_UNIT_TB, SIZE_UNIT_GB, SIZE_UNIT_MB]) const dataStorages = ref([ { - label: 'NVMe storage', + label: 'Хранилище NVMe', value: HARD_DRIVE_NVME, }, { - label: 'SSD storage', + label: 'Хранилище SSD', value: HARD_DRIVE_SSD, }, { - label: 'Network (SAN) storage', + label: 'Сетевое хранилище (SAN)', value: HARD_DRIVE_SAN, }, { - label: 'HDD storage', + label: 'Хранилище HDD', value: HARD_DRIVE_HDD, }, ]) const dataSizes = ref([ { - label: 'Less than RAM', + label: 'Меньше RAM', value: DB_SIZE_LESS_RAM, }, { @@ -105,7 +105,7 @@ const dataSizes = ref([ value: DB_SIZE_MID_RAM, }, { - label: 'More than 3x RAM', + label: 'Более 3xRAM', value: DB_SIZE_GREATER_RAM, }, ]) @@ -129,81 +129,65 @@ const onSubmit = async () => { diff --git a/src/stores/useConfigurationStore.js b/src/stores/useConfigurationStore.js deleted file mode 100644 index 1a73645..0000000 --- a/src/stores/useConfigurationStore.js +++ /dev/null @@ -1,479 +0,0 @@ -import { defineStore } from 'pinia' -import { reactive, computed } from 'vue' -import { useSettingsStore } from './useSettingsStore' -import { - DEFAULT_DB_VERSION, - OS_LINUX, - OS_WINDOWS, - OS_MAC, - DB_TYPE_WEB, - DB_TYPE_OLTP, - DB_TYPE_DW, - DB_TYPE_DESKTOP, - DB_TYPE_MIXED, - SIZE_UNIT_GB, - HARD_DRIVE_SSD, - HARD_DRIVE_HDD, - HARD_DRIVE_SAN, - HARD_DRIVE_NVME, - DB_SIZE_LESS_RAM, - DB_SIZE_MID_RAM, - DB_SIZE_GREATER_RAM, -} from '/src/utils/constants/configuration.js' - -const SIZE_UNIT_MAP = { - KB: 1024, - MB: 1048576, - GB: 1073741824, - TB: 1099511627776, - PB: 1125899906842624, -} - -const DEFAULT_DB_SETTINGS = { - default: { - ['max_worker_processes']: 8, - ['max_parallel_workers_per_gather']: 2, - ['max_parallel_workers']: 8, - }, -} - -const initialState = { - dbVersion: DEFAULT_DB_VERSION, - osType: OS_LINUX, - dbType: DB_TYPE_WEB, - totalMemory: null, - totalMemoryUnit: SIZE_UNIT_GB, - cpuNum: null, - connectionNum: null, - hdType: HARD_DRIVE_SSD, - dbSize: DB_SIZE_MID_RAM, -} - -const selectCheckpointCompletionTarget = 0.9 - -export const useConfigurationStore = defineStore('configuration', () => { - const state = reactive({ ...initialState }) - - const getConfiguration = computed(() => ({ ...state })) - const getDBVersion = computed(() => getConfiguration.value.dbVersion) - const getOSType = computed(() => getConfiguration.value.osType) - const getDBType = computed(() => getConfiguration.value.dbType) - const getTotalMemory = computed(() => getConfiguration.value.totalMemory) - const getTotalMemoryUnit = computed( - () => getConfiguration.value.totalMemoryUnit, - ) - const getCPUNum = computed(() => getConfiguration.value.cpuNum || null) - const getConnectionNum = computed( - () => getConfiguration.value.connectionNum || null, - ) - const getHDType = computed(() => getConfiguration.value.hdType) - const getDBSize = computed(() => getConfiguration.value.dbSize) - const getTotalMemoryInBytes = computed( - () => getTotalMemory.value * SIZE_UNIT_MAP[getTotalMemoryUnit.value], - ) - const getTotalMemoryInKb = computed( - () => getTotalMemoryInBytes.value / SIZE_UNIT_MAP['KB'], - ) - const getDBDefaultValues = computed( - () => DEFAULT_DB_SETTINGS[getDBVersion] || DEFAULT_DB_VERSION.default, - ) - const getIsConfigured = computed(() => !!getTotalMemory.value) - const getMaxConnections = computed( - () => - getConnectionNum.value ?? - { - [DB_TYPE_WEB]: 200, - [DB_TYPE_OLTP]: 300, - [DB_TYPE_DW]: 40, - [DB_TYPE_DESKTOP]: 20, - [DB_TYPE_MIXED]: 100, - }[getDBType.value], - ) - const getSharedBuffers = computed(() => { - const totalMemoryKb = getTotalMemoryInKb.value - - const val = { - [DB_TYPE_WEB]: Math.floor(totalMemoryKb / 4), - [DB_TYPE_OLTP]: Math.floor(totalMemoryKb / 4), - [DB_TYPE_DW]: Math.floor(totalMemoryKb / 4), - [DB_TYPE_DESKTOP]: Math.floor(totalMemoryKb / 16), - [DB_TYPE_MIXED]: Math.floor(totalMemoryKb / 4), - }[getDBType.value] - - return getDBVersion.value < 10 && OS_WINDOWS === getOSType.value - ? Math.min(val, (512 * SIZE_UNIT_MAP['MB']) / SIZE_UNIT_MAP['KB']) - : val - }) - const getHugePages = computed(() => - getOSType.value !== OS_MAC && - getSharedBuffers.value >= (2 * SIZE_UNIT_MAP['GB']) / SIZE_UNIT_MAP['KB'] - ? 'try' - : 'off', - ) - const getEffectiveCacheSize = computed(() => { - const totalMemoryKb = getTotalMemoryInKb.value - - return { - [DB_TYPE_WEB]: Math.floor((totalMemoryKb * 3) / 4), - [DB_TYPE_OLTP]: Math.floor((totalMemoryKb * 3) / 4), - [DB_TYPE_DW]: Math.floor((totalMemoryKb * 3) / 4), - [DB_TYPE_DESKTOP]: Math.floor(totalMemoryKb / 4), - [DB_TYPE_MIXED]: Math.floor((totalMemoryKb * 3) / 4), - }[getDBType.value] - }) - const getMaintenanceWorkMem = computed(() => { - const totalMemoryKb = getTotalMemoryInKb.value - // 1. Вычисляем базовое значение по типу БД - const baseMemValue = { - [DB_TYPE_WEB]: Math.floor(totalMemoryKb / 16), - [DB_TYPE_OLTP]: Math.floor(totalMemoryKb / 16), - [DB_TYPE_DW]: Math.floor(totalMemoryKb / 8), - [DB_TYPE_DESKTOP]: Math.floor(totalMemoryKb / 16), - [DB_TYPE_MIXED]: Math.floor(totalMemoryKb / 16), - }[getDBType.value] - - // 2. Определяем, действует ли строгое ограничение для Windows - const isLegacyWindows = - OS_WINDOWS === getOSType.value && getDBVersion.value <= 17 - - // 3. Задаем лимит памяти (2GB для старых Windows, иначе 8GB) - const maxLimitGb = isLegacyWindows ? 2 : 8 - const memoryLimit = (maxLimitGb * SIZE_UNIT_MAP['GB']) / SIZE_UNIT_MAP['KB'] - - // 4. Если превышен лимит на Windows, вычитаем 1MB, иначе просто возвращаем минимум - return baseMemValue >= memoryLimit && isLegacyWindows - ? memoryLimit - (1 * SIZE_UNIT_MAP['MB']) / SIZE_UNIT_MAP['KB'] - : Math.min(baseMemValue, memoryLimit) - }) - const getCheckpointSegment = computed(() => [ - { - key: 'min_wal_size', - value: { - [DB_TYPE_WEB]: (1024 * SIZE_UNIT_MAP['MB']) / SIZE_UNIT_MAP['KB'], - [DB_TYPE_OLTP]: (2048 * SIZE_UNIT_MAP['MB']) / SIZE_UNIT_MAP['KB'], - [DB_TYPE_DW]: (4096 * SIZE_UNIT_MAP['MB']) / SIZE_UNIT_MAP['KB'], - [DB_TYPE_DESKTOP]: (100 * SIZE_UNIT_MAP['MB']) / SIZE_UNIT_MAP['KB'], - [DB_TYPE_MIXED]: (1024 * SIZE_UNIT_MAP['MB']) / SIZE_UNIT_MAP['KB'], - }[getDBType.value], - }, - { - key: 'max_wal_size', - value: { - [DB_TYPE_WEB]: (4096 * SIZE_UNIT_MAP['MB']) / SIZE_UNIT_MAP['KB'], - [DB_TYPE_OLTP]: (8192 * SIZE_UNIT_MAP['MB']) / SIZE_UNIT_MAP['KB'], - [DB_TYPE_DW]: (16384 * SIZE_UNIT_MAP['MB']) / SIZE_UNIT_MAP['KB'], - [DB_TYPE_DESKTOP]: (2048 * SIZE_UNIT_MAP['MB']) / SIZE_UNIT_MAP['KB'], - [DB_TYPE_MIXED]: (4096 * SIZE_UNIT_MAP['MB']) / SIZE_UNIT_MAP['KB'], - }[getDBType.value], - }, - ]) - const getWalBuffers = computed(() => { - const KB_IN_MB = SIZE_UNIT_MAP['MB'] / SIZE_UNIT_MAP['KB'] - const baseWal = Math.floor((3 * sharedBuffersValue) / 100) - - return Math.max( - 32, - baseWal > 14 * KB_IN_MB - ? 16 * KB_IN_MB - : Math.min(baseWal, 16 * KB_IN_MB), - ) - }) - const getDefaultStatisticsTarget = computed( - () => - ({ - [DB_TYPE_WEB]: 100, - [DB_TYPE_OLTP]: 100, - [DB_TYPE_DW]: 500, - [DB_TYPE_DESKTOP]: 100, - [DB_TYPE_MIXED]: 100, - })[getDBType.value], - ) - const getRandomPageCost = computed(() => { - // Если база помещается в RAM, поиск на диске не важен - if (getDBSize.value === DB_SIZE_LESS_RAM) return 1.1 - - // HDD или аналитические БД (DW), не помещающиеся в RAM, требуют дефолтной стоимости (4.0) - if (getHDType.value === HARD_DRIVE_HDD || getDBType.value === DB_TYPE_DW) - return 4 - - return 1.1 - }) - const getEffectiveIOConcurrency = computed(() => - getOSType.value === OS_LINUX - ? { - [HARD_DRIVE_HDD]: 2, - [HARD_DRIVE_SSD]: 200, - [HARD_DRIVE_SAN]: 300, - [HARD_DRIVE_NVME]: 1000, - }[getHDType.value] - : null, - ) - const getParallelSettings = computed(() => { - const cpuNum = getCPUNum.value - const dbVersion = getDBVersion.value - const dbType = getDBType.value - - if (!cpuNum || cpuNum < 4) return [] - - const halfCpu = Math.ceil(cpuNum / 2) - const cappedWorkers = Math.min(4, halfCpu) // Ограничение в 4 воркера - - const config = [ - { key: 'max_worker_processes', value: cpuNum }, - { - key: 'max_parallel_workers_per_gather', - value: dbType !== DB_TYPE_DW ? cappedWorkers : halfCpu, - }, - ...(dbVersion >= 10 - ? [{ key: 'max_parallel_workers', value: cpuNum }] - : []), - ...(dbVersion >= 11 - ? [{ key: 'max_parallel_maintenance_workers', value: cappedWorkers }] - : []), - ] - - return config - }) - const getWorkMem = computed(() => { - const dbDefaultValues = getDBDefaultValues.value - - const maxWorkerProcesses = getParallelSettings.value.find( - (param) => param['key'] === 'max_worker_processes', - ) - - const parallelForWorkMem = - maxWorkerProcesses?.value > 0 - ? maxWorkerProcesses.value - : dbDefaultValues['max_worker_processes'] > 0 - ? dbDefaultValues['max_worker_processes'] - : 1 - - // Базовая формула расчёта work_mem - const baseWorkMem = - (getTotalMemoryInKb.value - sharedBuffersValue) / - ((maxConnectionsValue + parallelForWorkMem) * 3) - - // Коэффициенты для разных типов БД - const dbTypeCoefficients = { - [DB_TYPE_WEB]: 1, - [DB_TYPE_OLTP]: 1, - [DB_TYPE_DW]: 0.5, - [DB_TYPE_DESKTOP]: 1 / 6, - [DB_TYPE_MIXED]: 0.5, - } - - // Базовая формула с коэффициентом типа БД - const baseWorkMemResult = Math.floor( - baseWorkMem * dbTypeCoefficients[dbType], - ) - - // Корректировка в зависимости от размера БД относительно RAM - const sizeAdjustedWorkMem = - dbSize === DB_SIZE_LESS_RAM - ? Math.floor(baseWorkMemResult * 1.3) - : dbSize === DB_SIZE_GREATER_RAM - ? Math.floor(baseWorkMemResult * 0.9) - : baseWorkMemResult - - // Применяем минимальное значение 4 MB - const minWorkMem = 4 * 1024 // 4 MB в KB - const workMemWithMin = Math.max(sizeAdjustedWorkMem, minWorkMem) - - // Применяем ограничение для Windows 64-bit до PostgreSQL 17 - const winMemoryLimit = 2 * 1024 * 1024 - 1024 // ~2GB - 1MB в KB - const workMemResult = - osType === OS_WINDOWS && dbVersion <= 17 - ? Math.min(workMemWithMin, winMemoryLimit) - : workMemWithMin - - return workMemResult - }) - const getWalLevel = computed(() => - getDBType.value === DB_TYPE_DESKTOP - ? [ - { - key: 'wal_level', - value: 'minimal', - }, - // max_wal_senders must be 0 when wal_level=minimal - { - key: 'max_wal_senders', - value: '0', - }, - ] - : [], - ) - const getJit = computed(() => - getDBVersion.value >= 12 && - [DB_TYPE_WEB, DB_TYPE_OLTP, DB_TYPE_MIXED].includes(getDBType.value) - ? 'off' - : null, - ) - const getWalCompression = computed(() => { - const ver = getDBVersion.value - - return ver >= 15 ? 'lz4' : ver >= 10 ? 'on' : null - }) - const getAutovacuumMaxWorkers = computed(() => { - const cpuNum = getCPUNum.value - - if (!cpuNum) return null - if (cpuNum >= 32) return 5 - if (cpuNum >= 16) return 4 - return null - }) - const getAutovacuumWorkMem = computed(() => { - const threshold = (2 * SIZE_UNIT_MAP['GB']) / SIZE_UNIT_MAP['KB'] - - // Windows 64-bit has a strict 2GB limit up to PostgreSQL 17 - const winMemoryLimit = - (2 * SIZE_UNIT_MAP['GB']) / SIZE_UNIT_MAP['KB'] - - (1 * SIZE_UNIT_MAP['MB']) / SIZE_UNIT_MAP['KB'] - - const isWindowsWithLowLimit = - getOSType.value === OS_WINDOWS && getDBVersion.value <= 17 - - if (getMaintenanceWorkMem.value < threshold) { - return null - } - - if (isWindowsWithLowLimit) { - return Math.min(threshold, winMemoryLimit) - } - - return threshold - }) - const getIOMethod = computed(() => - getDBVersion.value < 18 - ? null - : getOSType.value === OS_LINUX - ? 'io_uring' - : 'worker', - ) - const getIOWorkers = computed(() => { - if (dbVersion < 18 || !cpuNum || ioMethod === 'io_uring') return null - - return Math.max(4, Math.min(32, Math.floor(cpuNum / 4))) || null - }) - const getWarningInfoMessages = computed(() => { - const warnings = [] - const totalMemory = getTotalMemoryInBytes.value - - // Memory warnings - if (totalMemory < 256 * SIZE_UNIT_MAP['MB']) { - warnings.push('this tool not being optimal', 'for low memory systems') - } else if (totalMemory > 100 * SIZE_UNIT_MAP['GB']) { - warnings.push( - 'this tool not being optimal', - 'for very high memory systems', - ) - } - - // Advanced features compilation warnings - if (getWalCompression.value === 'lz4') { - if (warnings.length > 0) warnings.push('') - - warnings.push( - 'wal_compression = lz4 requires PostgreSQL', - 'to be compiled with --with-lz4', - ) - } - - if (getIOMethod.value === 'io_uring') { - if (warnings.length > 0) warnings.push('') - - warnings.push( - 'io_method = io_uring requires PostgreSQL', - 'to be compiled with --with-liburing', - ) - } - - // I/O Cost Warning for Analytical DBs - if ( - getDBType.value === DB_TYPE_DW && - getHDType.value !== HARD_DRIVE_HDD && - getDBSize.value !== DB_SIZE_LESS_RAM - ) { - const driveName = { - [HARD_DRIVE_SSD]: 'SSDs', - [HARD_DRIVE_NVME]: 'NVMe drives', - [HARD_DRIVE_SAN]: 'SAN storage', - }[hdType] - - if (warnings.length > 0) warnings.push('') - - warnings.push( - `Cost parameters for Data Warehouses on ${driveName} are left at defaults`, - 'to avoid catastrophic index scan selections', - 'Monitor query planner behavior and adjust random_page_cost if necessary', - ) - } - - return warnings.length > 0 ? ['WARNING', ...warnings] : [] - }) - - function setState(payload) { - const settingsStore = useSettingsStore() - - state.value = { - dbVersion: parseFloat(payload.dbVersion), - osType: payload.osType, - dbType: payload.dbType, - totalMemory: parseInt(payload.totalMemory, 10), - totalMemoryUnit: payload.totalMemoryUnit, - cpuNum: payload.cpuNum ? parseInt(payload.cpuNum, 10) : null, - connectionNum: payload.connectionNum - ? parseInt(payload.connectionNum, 10) - : null, - hdType: payload.hdType, - dbSize: payload.dbSize, - } - - settingsStore.onSubmitConfiguration() - } - - const $reset = () => ({ ...initialState }) - - return { - state, - - // Getters - getDBVersion, - getOSType, - getDBType, - getTotalMemory, - getTotalMemoryUnit, - getCPUNum, - getConnectionNum, - getHDType, - getDBSize, - getTotalMemoryInBytes, - getTotalMemoryInKb, - getDBDefaultValues, - getIsConfigured, - getMaxConnections, - getSharedBuffers, - getHugePages, - getEffectiveCacheSize, - getMaintenanceWorkMem, - getCheckpointSegment, - getWalBuffers, - getDefaultStatisticsTarget, - getRandomPageCost, - getEffectiveIOConcurrency, - getParallelSettings, - getWorkMem, - getWalLevel, - getJit, - getWalCompression, - getAutovacuumMaxWorkers, - getAutovacuumWorkMem, - getIOMethod, - getIOWorkers, - getWarningInfoMessages, - - // Actions - setState, - $reset, - } -}) diff --git a/src/stores/useSettingsStore.js b/src/stores/useSettingsStore.js deleted file mode 100644 index 4abfa2d..0000000 --- a/src/stores/useSettingsStore.js +++ /dev/null @@ -1,42 +0,0 @@ -import { defineStore } from 'pinia' -import { reactive, computed } from 'vue' - -import { TAB_CONFIG, APP_THEMES_LIGHT } from '/src/utils/constants/settings.js' - -const initialState = { - tabState: TAB_CONFIG, - theme: APP_THEMES_LIGHT, -} - -export const useSettingsStore = defineStore('settings', () => { - const state = reactive({ ...initialState }) - - const getSettings = computed(() => state) - const getThemeSettings = computed(() => state.theme) - const getTabSettings = computed(() => state.tabState) - - function toggleTheme() { - state.theme = - APP_THEMES_LIGHT === state.theme ? APP_THEMES_DARK : APP_THEMES_LIGHT - } - function onSubmitConfiguration() { - state.tabState = TAB_CONFIG - } - function $reset() { - state = { ...initialState } - } - return { - // STATE - state, - - // GETTERS - getSettings, - getThemeSettings, - getTabSettings, - toggleTheme, - - // ACTIONS - onSubmitConfiguration, - $reset, - } -})