Этап 1. Организационный момент и проблемная мотивация
Хронометраж: 80 минут — 5 минут | 60 минут — 3 минуты
Преподаватель: «Здравствуйте, коллеги! Прошу занять свои рабочие места, включить персональные компьютеры и проверить запуск службы PostgreSQL. Сегодня мы переходим к важнейшему этапу промышленной разработки — созданию исполняемого модуля проекта и организации его сетевого взаимодействия с базой данных.
Представьте реальную ситуацию из производственной практики: крупный интернет-магазин запускает распродажу "Черная пятница". В первую же минуту на сайт заходит 2000 покупателей. Разработчики написали код по принципу "на каждый клик создаем новое подключение к БД". В итоге за пару секунд сервер СУБД попытался открыть две тысячи TCP-сокетов, израсходовал всю оперативную память на порождение дочерних процессов и намертво рухнул с системной ошибкой: FATAL: sorry, too many clients already. Бизнес потерял миллионы рублей за 10 минут простоя.
Почему это произошло? Потому что открытие прямого сетевого соединения с СУБД — это крайне тяжелая и дорогая операция. Сегодня на занятии мы научимся профессионально проектировать строки подключения, настраивать высокопроизводительные пулы соединений и корректно обрабатывать сетевые сбои!»
Этап 2. Теоретический базис: строки подключения, пул соединений и SSL
Хронометраж: 80 минут — 15 минут | 60 минут — 12 минут
Преподаватель: «Внимание на экран проектора. Давайте разберем, что происходит на физическом уровне, когда клиентская программа пытается отправить запрос в PostgreSQL. Посмотрим на слайд 5 презентации.
Сначала операционная система выполняет тройное рукопожатие TCP: SYN, SYN-ACK, ACK. На это уходит 10–30 миллисекунд. Затем, если мы заботимся о безопасности, инициируется шифрование канала TLS — обмен сертификатами и ключами Диффи-Хеллмана. Это еще 30–70 миллисекунд. Далее следует аутентификация в СУБД по алгоритму SCRAM-SHA-256. И только после выделения отдельного серверного процесса postgres backend база готова исполнить запрос. Суммарно на каждое новое подключение тратится до 200 миллисекунд!
Какое инженерное решение устраняет эту проблему? Вопрос аудитории: как не повторять этот долгий процесс на каждый чих пользователя?»
Студент (Иванов): «Использовать пул соединений! Мы заранее открываем несколько соединений, держим их готовыми в оперативной памяти, а когда приходит запрос — берем готовое соединение на пару миллисекунд и потом возвращаем обратно в пул, не закрывая сокет».
Преподаватель: «Абсолютно верно! Пул соединений — это программный менеджер кэшированных сокетов. Время получения соединения из пула составляет меньше половины миллисекунды. Но здесь есть ключевые параметры, которые вы обязаны уметь рассчитывать:
min_connections — базовое количество постоянно прогретых сокетов;
max_connections — максимальный лимит соединений, выше которого потоки будут вставать в очередь, предотвращая перегрузку СУБД;
idle_timeout — время, по истечении которого лишние простаивающие соединения будут закрыты.
И помните золотое правило методологии 12-Factor App: никаких логинов и паролей внутри файлов исходного кода! Все параметры мы выносим во внешние конфигурационные файлы:
config.json,
config.yaml или в переменные окружения
.env».
Этап 3. Сквозная живая демонстрация (Live Coding)
Хронометраж: 80 минут — 10 минут | 60 минут — 7 минут
Преподаватель: «Сейчас я продемонстрирую вам эталонный скрипт первичной инициализации базы данных. Взгляните на экран:
// 1. Считывание конфигурации из внешнего файла .env
const config = {
host: process.env.DB_HOST || '127.0.0.1',
port: parseInt(process.env.DB_PORT) || 5432,
database: process.env.DB_NAME || 'warehouse_db',
user: process.env.DB_USER,
password: process.env.DB_PASSWORD,
max: 5, // max_pool_size
idleTimeoutMillis: 300000,
connectionTimeoutMillis: 5000
};
// 2. Инициализация пула соединений
const pool = new Pool(config);
// 3. Health Check: первичный пинг СУБД запросом SELECT 1
async function testDatabaseConnection() {
try {
const client = await pool.connect();
const result = await client.query('SELECT 1 AS health_check;');
client.release(); // ОБЯЗАТЕЛЬНО возвращаем сокет в пул!
console.log('[INFO] Успешное подключение к СУБД. База данных готова к работе.');
} catch (err) {
console.error('[ERROR] [DB_CONN] Сбой подключения к базе данных:', err.message);
// Логируем инцидент и предотвращаем запуск сломанного приложения
process.exit(1);
}
}
Обратите внимание на строку
client.release(). Если вы забудете вернуть клиент в пул, в вашем приложении возникнет утечка соединений —
Connection Leak. Десять запросов — и ваше приложение намертво зависнет, потому что все сокеты останутся занятыми.
Теперь я намеренно останавливаю службу базы данных через терминал:
sudo systemctl stop postgresql и перезапускаю наш скрипт. Смотрите: скрипт не упал с "синим экраном" или аварийным завершением. Блок
catch перехватил системную ошибку
ECONNREFUSED и красиво записал в лог время, код ошибки и рекомендацию проверить службу PostgreSQL. Именно такого поведения я жду от ваших практических работ!»
Этап 4. Самостоятельный инженерный практикум
Хронометраж: 80 минут — 40 минут | 60 минут — 32 минуты
Преподаватель: «Коллеги, открываем интерактивный рабочий лист
zanyatie.html в браузере. В шапке листа укажите ваши реальные фамилию, имя и номер учебной группы. Лист поддерживает автосохранение всех введенных данных в LocalStorage вашего браузера, поэтому ничего не потеряется.
У нас предусмотрено 4 дифференцированных инженерных сценария:
- Вариант 1 (Складской учет): вы настраиваете
config.json для локального PostgreSQL, конфигурируете пул из 5 соединений и реализуете первичный пинг SELECT 1.
- Вариант 2 (Медицинская регистратура): работа с конфиденциальными данными пациентов. Требуется настроить сертификаты SSL/TLS в режиме
sslmode=verify-full и обработать таймаут связи при падении сервера.
- Вариант 3 (Банковский терминал): финансовый узел с высокими требованиями к доступности. Настройка пула HikariCP/pgBouncer, перехват ошибки
connection refused и вывод записи в системный журнал.
- Вариант 4 (Образовательный портал): высоконагруженный веб-сервис. Настройка переменных окружения
.env, выполнение первого проверочного запроса и замер времени установления сессии (latency benchmark).
Приступайте к работе. Я нахожусь в аудитории и готов ответить на индивидуальные вопросы».
Преподаватель (промежуточный комментарий в ходе работы): «Минутку внимания! Вижу у двоих студентов типичную ошибку: вы указываете connect_timeout = 0. Запомните: нулевой таймаут в большинстве драйверов означает БЕСКОНЕЧНОЕ ожидание. Если оборвется кабель или зависнет коммутатор, ваша программа зависнет навсегда. Всегда задавайте таймаут от 2 до 5 секунд! Исправьте это в своих конфигурациях».
Этап 5. Подведение итогов, рефлексия, оценивание и домашнее задание
Хронометраж: 80 минут — 10 минут | 60 минут — 6 минут
Преподаватель: «Время практической работы подошло к концу. Давайте взглянем на слайд 15 нашей презентации. Перед вами знаменитый классический комикс от Randall Munroe про маленького Бобби Таблицу — Little Bobby Tables.
Мама записала сына в школу под именем
Robert'); DROP TABLE Students; --. И когда школьное приложение попыталось выполнить конкатенацию строки запроса к СУБД, база данных стерла всех студентов за текущий учебный год! Этот мем напоминает нам: безопасность и надежность базы данных закладываются на самом первом этапе — при сборке исполняемого файла, настройке строки подключения, пула и экранировании запросов.
Давайте подведем итоги занятия:
- Сегодня вы научились собирать конфигурационные файлы подключения к СУБД без опасного хардкода учетных данных в репозиторий.
- Вы освоили математический расчет пула соединений и поняли, почему пул на 1000 коннектов убивает сервер, а пул на 10 сокетов обеспечивает максимальную скорость.
- Вы реализовали процедуру Health Check и научились структурированно логировать сетевые исключения.
Оценки за сегодняшнюю работу будут выставлены в соответствии с 4-балльной критериальной рубрикой. Проверьте сохранение данных в
zanyatie.html и выполните экспорт страницы.
В папке занятия размещен резервный файл
domashnee_zadanie.html. Обратите внимание на плашку: вы выполняете это домашнее задание только в том случае, если я назначу его индивидуально или для группы при пропуске пары. Всем спасибо за продуктивную инженерную работу, занятие окончено!»