2. Strictly ConfidentialStrictly Confidential
2
Обо мне
Виктор Ягофаров, Avito.ru
• Администратор Баз Данных
• 8 лет в качестве Системного Администратора FreeBSD
и Linux
• 2 года занимался развитием PostgreSQL-
инфраструктуры в Rambler
• Тем же занят в Avito :)
3. Strictly ConfidentialStrictly Confidential
3
О чем доклад?
• Как мы резервируем postgres в Avito
• Почему именно так, другие варианты
• Протокол pgsql и haproxy + pgbouncer
• Подводные камни
• Tips and tricks
4. Strictly ConfidentialStrictly Confidential
4
PgBouncer
• Мультиплексор: "многие к одному"
• transaction pooling для приложений (более
экономный)
• session pooling для разработчиков (тяжелее
сломать)
• Возможность совмещать разные виды пулов в 1.6
• pg_hba.conf -style конфиг, начиная с 1.7
5. Strictly ConfidentialStrictly Confidential
5
HAProxy
• Быстрый tcp/http load balancer
• Гибкость настройки способов балансировки для
разных типов нагрузки
• Тонкий тюнинг failover
• Custom http health checks: логика проверок
ограничена лишь фантазией
• Настраиваемое поведение при переключении на
backup и обратно
• Возможность с помощью health checks на
множестве нод агрегировать статистику и видеть
картину в целом
7. Strictly ConfidentialStrictly Confidential
7
Конфиг HAProxy и PgBouncer
listen pgsql-db_main_s
bind 127.0.0.1:16002
timeout client 20m
timeout connect 1s
timeout server 20m
balance roundrobin
option log-health-checks
option tcpka
option tcplog
option httpchk GET /db_main_s?username=app_ro&port=6432 # настройки чекера
http-check send-state
server host-sb01 host-sb01:6432 check addr 127.0.0.1 port 5777 inter 6s fall 5 rise 3
server host-sb02 host-sb02:6432 check addr 127.0.0.1 port 5777 inter 6s fall 5 rise 3
backup
server host-sb03 host-sb03:6432 check addr 127.0.0.1 port 5777 inter 6s fall 5 rise 3
backup
[databases]
db_main_s = host=127.0.0.1 port=16002 pool_size=10
8. Strictly ConfidentialStrictly Confidential
8
Health checking
cat /etc/xinetd.d/pgcheck
service pgcheck
{
disable = no
type = UNLISTED
flags = REUSE
socket_type = stream
port = 5777
wait = no
user = nobody
server = /usr/local/bin/pgcheck
log_on_failure += USERID
only_from = 127.0.0.1/32
per_source = UNLIMITED
}
9. Strictly ConfidentialStrictly Confidential
9
pgcheck - эмулятор web-сервиса
#!/usr/bin/env perl
…
$| = 1; # отключаем буферизацию
# Set whole script timeout to 5 seconds via alarm
$SIG{ ALRM } = sub {
http 504 => "Timeout checking database health";
};
alarm 5; ### время таймаута всего скрипта
…
my $dbh = DBI->connect("dbi:Pg:dbname=$db;host=$host;port=$port",
"$username", '',
{ PrintError => 0, RaiseError => 0, pg_server_prepare => 0 } ) or #
отключаем prepare чтобы избежать prepare на сервере
http 502 => "Error occured connecting database ($DBI::errstr)";
…
10. Strictly ConfidentialStrictly Confidential
10
pgcheck - эмулятор web-сервиса
… продолжение скрипта
# do not use database if check_ha() returns 'false'
my $sth = $dbh->prepare("select public.check_ha()");
my $rv = $sth->execute or
http 503 => "Error occured while 'select check_ha()' on '$db' at
'$host' ($DBI::errstr)";
my @row = $sth->fetchrow_array;
if ( $row[0] == 0 ) {
http 503 => "Error occured while 'select check_ha()' on '$db' at '$host': service
disabled manually";
}
…
# If everything is ok, return 200
http 200 => "Database '$db' at '$host' is alive";
12. Strictly ConfidentialStrictly Confidential
12
Поведение при разрывах tcp
select * from tbl where id = 1
select * from tbl where id = 1
# разрыв и переключение на другой сервер в haproxy ..
select * from tbl where id = 1
Вне транзакции выполним, подключившись к серверу через локальный
pgbouncer:
Последний запрос выполнится без ошибок!
Но в случае транзакции, последний select получит ошибку
select 'text' || pg_sleep(60) ;
Клиент получит ошибку "ERROR: server conn crashed"
Долгий запрос:
13. Strictly ConfidentialStrictly Confidential
13
Поведение при разрывах tcp
• Не транзакционные, быстрые запросы в одной
сессии (до 1 мс) могут не заметить разрыва tcp
(tcpkill/tcpdrop), следовательно - они более
"stateless" и имеют меньше шансов завершиться с
ошибкой. Это же касается быстрых транзакций.
• Локальный pgbouncer пересоздает подключение
незаметно для клиента, haproxy повышает HA.
• Но переключение в haproxy происходит не
мгновенно
• HAProxy по-умолчанию не разрывает
существующие сессии (см. on-marked-down
shutdown-sessions, on-marked-up shutdown-backup-
sessions)
15. Strictly ConfidentialStrictly Confidential
15
Особенности haproxy
• haproxy "релоадит" конфиг через порождение
нового процесса и передачу в него новых
коннектов. Старый процесс может ждать
бесконечно старые tcp states
• в http статистике haproxy не всегда показывает
переменную с описанием ошибки чекера
16. Strictly ConfidentialStrictly Confidential
16
Особенности psql
• Особенности флагов psql (-1, -f, -c)
• set local statement_timeout и psql -c
psql -Upostgres -c "set local statement_timeout = '3 s'; select pg_sleep(5)"
psql -1 -Upostgres -h avi-sql26 -p 6432 -f- << 'EOF'
set local statement_timeout = '3 s';
select pg_sleep(5);
EOF
17. Strictly ConfidentialStrictly Confidential
17
Особенности psql
• psql -v ON_ERROR_STOP=1 : обязателен при -f
• Что еще не работает в single-transaction (-1):
create index concurrently ;
create database;
vacuum;
- - что-то еще? вполне!
• timing включает в себя задержки сети
• set search_path в transaction pooling - так делать
опасно. Используйте set local в transaction pooling
mode