Клиент
Карту курса ты уже видел в 00-01 — теперь первый технический шаг. Где-то крутится Postgres с меню, заказами и клиентами Brew, и тебе с ним работать: писать приложение, которое читает и пишет данные, а не администрировать сервер. Прежде чем браться за типы, индексы и транзакции, нужно понять две скучные, но обязательные вещи: что вообще находится на том конце соединения, и как завести локальную копию, которую не страшно сломать.
Это весь сюжет юнита. Никакого SQL-героизма — подключиться, спросить у сервера его версию и прочитать меню. Дальше на этот же конвейер ляжет весь курс.
Клиент и сервер: что на том конце сокета
Postgres — это серверный процесс. Он владеет файлами с данными, и никто, кроме него, в эти файлы не лезет. Всё, что у тебя в руках — psql, драйвер pgx в Go, веб-морда Adminer — это клиенты. Клиент открывает соединение, по сети (TCP на порт 5432 или локальный unix-сокет) отправляет текст запроса и читает обратно строки. Протокол между ними фиксированный — Postgres wire protocol, — но тебе его знать не нужно: за тебя его говорит драйвер.
Одно соединение, два конца:
N клиентов один сервер-процесс
(шлют SQL, читают строки) (делает всю работу)
psql ──┐
pgx ───┼──▶ соединение по сокету (TCP :5432 / unix) ──▶ postgres
Adminer┘ wire protocol • парсит SQL, строит план
• исполняет, держит MVCC и блокировки
• владеет файлами данных — только онПочему это важно на практике, а не только в теории администрирования. Каждое слово, которое всплывёт дальше — «соединение», «пул», «таймаут», «коннект отвалился» — про этот самый сокет. Работу делает сервер: парсит SQL, строит план, исполняет его, держит MVCC и блокировки. Клиент только посылает запрос и забирает результат. Один сервер обслуживает много клиентов одновременно — и вся история про конкурентность (модуль 05) растёт именно отсюда: несколько соединений, один набор данных.
Практический вывод на сегодня простой: чтобы что-то сделать с данными, нужно соединение. Откроем его и проверим, что на том конце действительно живой Postgres 18.
Песочница: один Postgres на весь курс
Локальный стенд — один контейнер Postgres 18 плюс Adminer как веб-клиент. Поднимается из корня репозитория:
docker compose up -dОдна база brew обслуживает весь курс. Каждый юнит докатывает свою схему поверх общей схемы Brew (schema/brew.sql) командой make db-reset — отдельный контейнер на юнит не нужен. Базовые таблицы — это таблицы кофейни: drinks (меню), customers, orders, shops и другие. Их мы наполняем детерминированными seed-данными: фиксированные id, фиксированные created_at. Поэтому вывод демо воспроизводится дословно — и его не стыдно вставить прямо в README.
make db-reset идемпотентен: внутри он накатывает схему через IF NOT EXISTS, а seed — через TRUNCATE ... RESTART IDENTITY перед вставкой. Гоняй сколько угодно раз — состояние БД всегда одно и то же. Это не косметика: воспроизводимость — то, что отличает «у меня на машине работает» от «работает у всех».
Adminer висит на http://localhost:8090 (System: PostgreSQL, Server: postgres, логин/пароль brew/brew) — удобно глазами посмотреть на таблицы, но это опционально. Рабочий клиент курса — Go.
Параметры подключения по умолчанию заданы под этот стенд:
DATABASE_URL=postgres://brew:brew@localhost:5432/brew?sslmode=disableinternal/pg.NewPool читает DATABASE_URL (или собирает строку из PG*-переменных), так что в коде юнита строки подключения нет — есть один вызов.
Что показывает наш код
В центре урока — query.sql. Это не вспомогательный файл, а сам урок: SQL мы пишем руками, и он остаётся читаемым SQL, а не растворяется в билдере запросов.
-- name: ServerVersion :one
SELECT version();
-- name: ListDrinks :many
SELECT id, sku, name, category, base_price
FROM drinks
ORDER BY id;Команда make gen запускает sqlc generate: sqlc читает query.sql вместе со схемой (схема Brew + добавки юнита) и генерирует типизированный Go-код в internal/db/. Из -- name: ListDrinks :many получается метод ListDrinks(ctx) ([]ListDrinksRow, error), где ListDrinksRow — структура с полями ровно тех типов, что в таблице (base_price BIGINT → int64). Сгенерированный код мы коммитим: он часть репозитория, ревьюится в diff'е и не требует кодогенерации на чужой машине.
main.go после этого — тонкий. Вся его суть в четырёх строчках:
pool, err := pg.NewPool(ctx) // пул соединений к песочнице
queries := db.New(pool) // типизированная обёртка из sqlc
version, err := queries.ServerVersion(ctx)
drinks, err := queries.ListDrinks(ctx)Никакого ручного rows.Scan, никаких строковых литералов SQL в коде Go — всё это сгенерировано из query.sql. Этот конвейер — «SQL руками → sqlc generate → типизированный pgx-код» — позвоночник каждого юнита курса. Дальше меняются только запросы.
Запуск
Подними песочницу (из корня репозитория) и накати схему Brew:
docker compose up -d
make lecture L=00-getting-connected/00-02-client-server-and-sandbox T=db-resetЗапусти демо:
make lecture L=00-getting-connected/00-02-client-server-and-sandbox(T=run — значение по умолчанию, поэтому без T=... сразу запускается демо. Изнутри каталога юнита это просто make db-reset и make run.)
Вывод:
Сервер: PostgreSQL 18.4 on aarch64-unknown-linux-musl, compiled by gcc (Alpine 15.2.0) 15.2.0, 64-bit
Напитков в меню Brew: 5
ID SKU НАЗВАНИЕ КАТЕГОРИЯ ЦЕНА
1 ESP-01 Эспрессо coffee 3.00
2 CAP-01 Капучино coffee 4.50
3 LAT-01 Латте coffee 4.80
4 CLD-01 Колд брю cold 5.20
5 TEA-01 Зелёный чай tea 2.50Строка версии у тебя может отличаться хвостом — aarch64 против x86_64, версия gcc — это зависит от архитектуры машины, на которой собран образ postgres:18-alpine. Главное — PostgreSQL 18.x в начале: на том конце сокета именно та версия, на которую рассчитан курс. Таблица меню воспроизводится дословно: id, цены и порядок заданы seed-данными.
Заборчик
- Песочница — это
docker composeна твоём ноуте. В проде сервер держит и обслуживает DBA: версия Postgres и апгрейды,max_connections, память, диск, бэкапы — их зона, не твоя. Ты на том конце сокета — клиент, а не оператор сервера. sslmode=disableгодится только для локального стенда. В проде соединение шифруют (TLS), а пароль приходит из секрет-менеджера — он не лежит вDATABASE_URLпод версионным контролем.- Одна общая база
brewна весь курс — учебное упрощение. На реальном проекте окружения разведены по разным базам и инстансам, аmake db-reset(внутри —TRUNCATE) по живым данным не запускают никогда.
Что забрать с собой
- Postgres — сервер;
psql,pgx, Adminer — клиенты, которые говорят с ним по соединению. Данных клиент не трогает, он шлёт SQL и читает строки. - Песочница — один контейнер на весь курс;
make db-resetидемпотентно возвращает БД в эталонное состояние, поэтому вывод демо воспроизводим. - Конвейер курса:
query.sql(руками) →make gen(sqlc) → типизированный код вinternal/db/(коммитим) → тонкийmain.go.
Дальше — юнит 00-03 «psql survival kit»: тот же сервер, но смотрим на него через psql напрямую — \dt, \d, \x, \timing. Это рабочий инструмент, который пригодится в каждом следующем юните, когда захочется заглянуть в БД руками, без Go.