PostgreSQL profiling

Профилирование хранимых процедур в PostgreSQL не является тривиальной задачей. Сложность заключается в том, что в журнал попадает обобщенное время выполнения хранимой процедуры. Таким образом, если Ваша хранимая процедура вызывает какие-то запросы или другие хранимые процедуры, в журнал попадет исключительно суммарное время выполнения.

Что касается трассировки выполнения вложенных в процедуру вызовов других функций, существенно облегчить задачку может установка переменной track_functions в значение pl. Например,

SET track_functions TO pl;

После вызова желаемой функции в таблице pg_stat_user_functions будет суммарная статистика о ней.

Для более детального профилирования можно воспользоваться RAISE LOG или модулем log_functions. О использовании первого очень подробно рассказывается в руководстве PROFILING STORED PROCEDURES/FUNCTIONS, второго — Profiling PL/pgsql functions. Для второго подхода потребуется собрать дополнительный модуль для PostgreSQL. Однако у него есть и неоспоримое преимущество — он не потребует модификации исходной хранимой процедуры, что обязательно потребуется в случае использования RAISE LOG.

psql editor feature

В psql есть встроенная возможность вызова редактора через выполнение мета-команды \e. Писать команду в ней существенно приятнее, чем в обычной psql оболочке. Однако при выходе из редактора он сразу выполняет команду. В большинстве случае это допустимо, однако в ряде случаев нужно подстраховаться. Можно это сделать двумя путями:

BEGIN; -- 
\e 
COMMIT; -- 

Второй вариант заключается в том, что в редакторе Вы набираете команду и не закрываете ее точкой с запятой (;) на конце.

PostgreSQL >= 9.3 и SHMMAX

Начиная с PostgreSQL >= 9.3 изменился механизм выделения памяти для shared buffers. Теперь он использует mmap() и необходимость в изменении системных параметров kernel.shmmax и kernel.shmall отпала.

At 9.3 the mechanism for shared memory allocation has changed. Now it uses
mmap(). And so, there is no more need to adjust the SHMMAX parameter (at
least not more than the 1KB).

So the answer to your question is basically that you don’t need to mess
sysctl.conf anymore to change SHMMAX (at least not in CentOS). You still
may need to tweak there to change semaphores limits.

Источник

Импорт s9y в wordpress

Больше историческая запись. Давно зародилась идея перевести текущий журнал на wordpress. В s9y совсем примитивная система борьбы с нежелательными сообщениями (junk). Небольшое сообщество пользователей, небольшой набор дополнений.

В wordpress все это реализуется большей частью в несколько мышиных нажатий, что в рамках текущий задачи меня полностью устраивает.

Однако процесс перехода не оказался таким простым как первоначально предполагалось. Хотелось сохранить историю комментариев, сохранить предыдущие гипертекстовые ссылки, ссылки на RSS ленту. Большим подспорьем послужило дополнение для импорта s9y в wordpress, которое потребовалось допиливать напильником. В частности при его текущем алгоритме некорректно мигрируют комментарии, вызывая ошибки duplicate violation.

Несколько десятков прогонов миграции и результат не заставил себя ждать. По времени вышло в районе 4-6 часов. Из некритичного хочется перенести стили.

Особенности pdebuild и dpkg-checkbuilddeps: Unmet build dependencies

Если ответить кратко, то процитирую lists.debian.org:

>> >> dpkg-checkbuilddeps: Unmet build dependencies: python-all cdbs docbook-xsl
>> >> W: Unmet build-dependency in source
>> >> ...
>> >>
>> >> How can I make pbuilder fetch these required dependencies?
>
> pbuilder does install the necessary dependencies. But it does so
> *inside* the chroot. But before it runs the "clean" target in the
> debian rules file. So you need the build-dependencies at your system
> too. But there pdebuild won't install them. You have to change the
> pdebuild action orders:
>
> [snip]
>> dpkg-checkbuilddeps: Unmet build dependencies: python-all cdbs docbook-xsl
>> dpkg-buildpackage: warning: Build dependencies/conflicts unsatisfied;
>> aborting.
>> ...
>
> Read about the --use-pdebuild-internal switch:
> http://www.netfort.gr.jp/~dancer/software/pbuilder-doc/pbuilder-doc.html#pdebuild

Если ответить развернуто. Без использования --use-pdebuild-internal выполнение pdebuild приводит к выполнению следующего набора команд

    if ! dpkg-checkbuilddeps -B ; then
        log "W: Unmet build-dependency in source"
    fi
    echo "dpkg-buildpackage -S -us -uc -r${BUILDSOURCEROOTCMD} $DEBBUILDOPTS" | \
        perl -pe 's/(^|\s)-[AbBF](\s|$)/$1$2/g' | \
        /bin/bash
    ${PBUILDERROOTCMD} \
        ${PDEBUILD_PBUILDER} \
        --build \
        ${EXTRA_CONFIGFILE[@]/#/--configfile } \
        --buildresult "${BUILDRESULT}" \
        --debbuildopts "" \
        --debbuildopts "${DEBBUILDOPTS}" \
        "$@" \
        ../"${PKG_SOURCENAME}_${PKG_VERSION}".dsc

Таким образом pdebuild изначально проверяет присутствие зависимостей пакета на основной системе через вызов dpkg-checkbuilddeps -B. В случае не успешного завершения выполнение ограничивается исключительно предупреждением и продолжением выполнения сценария.

После этого предпринимается отчаянная попытка создания исходного (source) набора файлов вызовом команды dpkg-buildpackage -S -us -uc. Только в случае успешного завершения этого этапа будет выполнена команда pbuilder --build ... *.dsc, которая соберет в чистом chroot окружении пакет.

Подвох кроется в выполнении команды dpkg-buildpackage -S -us -uc, которая на одном из своих этапов выполняет команду в основной системе

fakeroot  debian/rules  clean

Для успешного завершения которой требуется установка всех Build-Dep на основной системе.

Необходимое лекарство кроется в $HOME/.pbuilderrc
USE_PDEBUILD_INTERNAL=yes
или указании ключа --use-pdebuild-internal при каждом запуске pdebuild.

gearman и постоянное хранилище PostgreSQL

В новой версии gearman поменялся синтаксис для подключения PostgreSQL хранилища в качестве persistent backend. В частности большинство руководств оперирует оператором -q libpq. Попытки запустить gearman версии 1.0.6 с указанной комбинацией приведет к ошибке

  ERROR 2013-12-11 14:21:45.985137 [  main ] Unknown queue libpq initialize(UNKNOWN_OPTION) -> libgearman-server/queue.cc:214

Для корректной работы необходимо использовать -q postgres или --queue-type=postgres. Например,

/usr/sbin/gearmand --queue-type=postgres --libpq-conninfo='host=localhost user=gearman password=passoword dbname=gearman' --libpq-table=queue

Приключения продолжаются. Разработчики заботливо позаботились о том, чтобы нам не пришлось править скрипты инициализации. О чем свидетельствуют строчки в /etc/default/gearman-job-server:

# This is a configuration file for /etc/init.d/gearman-job-server; it allows
# you to perform common modifications to the behavior of the gearman-job-server
# daemon startup without editing the init script (and thus getting prompted by
# dpkg on upgrades).  We all love dpkg prompts.

В последних версиях скрипт инициализации видоизменили и теперь он использует upstart. Однако зависимость /etc/default/gearman-job-server забыли подключить. Таким образом сервис будет запускаться всегда как

exec start-stop-daemon --start --chuid gearman --exec /usr/sbin/gearmand -- --log-file=/var/log/gearman-job-server/gearman.log

Идеальным вариантом является сборка локального пакета с необходимыми модификациями upstart сценария. Для флегматиков можно предложить дождаться включения необходимых изменений в основной пакет. Для холериков можно предложить вариант модификации /etc/init/gearman-job-server.conf на месте. Для этого необходимо привести его к следующему виду

# -*- upstart -*-

# Upstart configuration script for "gearman-job-server".

description "gearman job control server"

start on (filesystem and net-device-up IFACE=lo)
stop on runlevel [!2345]

respawn

script
    . /etc/default/gearman-job-server
    exec start-stop-daemon --start --chuid gearman --exec /usr/sbin/gearmand -- $PARAMS --log-file=/var/log/gearman-job-server/gearman.log
end script

Из-за особенностей экранирования не получится просто указать в конфигурационном файле /etc/default/gearman-job-server следующую настройку (ссылка на обсуждение):

# РАБОТАТЬ НЕ БУДЕТ!
PARAMS="--queue-type=postgres --libpq-conninfo='host=localhost user=gearman password=passoword dbname=gearman' --libpq-table=queue"

В качестве обходного варианта можно использовать переменные окружения

export PGHOST=localhost
export PGUSER=gearman
export PGPASSWORD=password
export PGDATABASE=gearman
PARAMS="--queue-type=postgres --libpq-table=queue"

upstart vs InitV

Сегодня столкнулись с ситуацией, когда поведение service snmpd restart отличается от поведения /etc/init.d/snmpd restart.

root@develop /etc/init # /etc/init.d/snmpd restart
 * Restarting network management services:                                      root@develop /etc/init # ps uaxwww| grep snmpd
snmp      1097  0.2  0.0  52372  4412 ?        S    11:50   0:00 /usr/sbin/snmpd -Lsd -Lf /dev/null -u snmp -g snmp -I -smux -p /var/run/snmpd.pid
root      1099  0.0  0.0  10612   940 pts/0    S+   11:50   0:00 grep --color=auto snmpd
root@develop /etc/init # snmpget -v1 -Cf -c public localhost .1.3.6.1.4.1.8072.1.3.2.4.1.2.6.105.111.115.116.97.116.161
Error in packet
Reason: (noSuchName) There is no such variable name in this MIB.
Failed object: iso.3.6.1.4.1.8072.1.3.2.4.1.2.6.105.111.115.116.97.116.161

root@develop /etc/init # service snmpd restart
 * Restarting network management services:                                      root@develop /etc/init # ps uaxwww| grep snmpd
snmp      1123  0.5  0.0  52372  4412 ?        S    11:50   0:00 /usr/sbin/snmpd -Lsd -Lf /dev/null -u snmp -g snmp -I -smux -p /var/run/snmpd.pid
root      1125  0.0  0.0  10612   944 pts/0    S+   11:51   0:00 grep --color=auto snmpd
root@develop /etc/init # snmpget -v1 -Cf -c public localhost .1.3.6.1.4.1.8072.1.3.2.4.1.2.6.105.111.115.116.97.116.161
iso.3.6.1.4.1.8072.1.3.2.4.1.2.6.105.111.115.116.97.116.161 = STRING: "3.23"
root@develop /etc/init # 

Наглядно видно, что для случая использования /etc/init.d/snmpd restart, OID не доступен. Командная строка запуска не отличается, конфигурационный файл сервиса не изменяется.

Разрушители легенд: мифы и реальность о MongoDB

Всем поклонникам подпихнуть MongoDB в любые мыслимые и немыслимые места посвящается. Некоторые выдержки

When a read lock exists, many read operations may use this lock. However, when a write lock exists, a single write operation holds the lock exclusively, and no other read or write operations may share the lock.

Beginning with version 2.2, MongoDB implements locks on a per-database basis for most read and write operations. Some global operations, typically short lived operations involving multiple databases, still require a global “instance” wide lock. Before 2.2, there is only one “global” lock per mongod instance.

Больше всего понравился тот факт, что на запись у них теперь лочиться вся база, а не весь процесс, они как великое достижение человечества записали. MySQL версии 3.x, если не более ранние, поддерживали аналогичное поведение, что было лет десять тому назад.

MongoDB, бесспорно, отличный инструмент. Но не следует применять его как затычку к каждой бочке.

Разрушители легенд: мифы о Redis

Крайне полезная для ознакомления документация (на анлийском языке), посвященная концептуальным вопросам взаимодействия подсистем операционной системы применительно к хранилищу Redis. Уделяется много внимания вопросу в каких подсистемах ОС искать виновника потерянных Вами данных.

Вторая статья, которую хочется порекомедовать к ознакомлению, Disks from the Perspective of a File System, известного Marshall Kirk McKusick, описывающая проблемы с которыми сталкиваются файловые системы и какие техники применяются в борьбе с уловками дисковых накопителей.

Восстанавливаем RAID1 с двумя неисправными жесткими дисками

Долгое время не выдавалось интересных задачек, которые можно было бы освятить в заметке. Вчера всё-таки нашлась интересная с моей точки зрения.

В исходных данных имеем RAID1 массив, построенный на mdadm, оба диска в котором имеют ошибки на чтение в нескольких секторах. Система говорит о том, что в обоих из них Unrecovered read error — auto reallocate failed:

Aug 21 21:53:11 one kernel: [112350.663076] sd 2:0:0:0: [sda] Unhandled sense code
Aug 21 21:53:11 one kernel: [112350.663081] sd 2:0:0:0: [sda] Result: hostbyte=DID_OK driverbyte=DRIVER_SENSE
Aug 21 21:53:11 one kernel: [112350.663089] sd 2:0:0:0: [sda] Sense Key : Medium Error [current] [descriptor]
Aug 21 21:53:11 one kernel: [112350.663133] sd 2:0:0:0: [sda] Add. Sense: Unrecovered read error - auto reallocate failed
Aug 21 21:53:11 one kernel: [112350.663144] sd 2:0:0:0: [sda] CDB: Read(10): 28 00 3a 38 53 b0 00 00 08 00
Aug 21 21:53:11 one kernel: [112350.663160] end_request: I/O error, dev sda, sector 976769972

Попытки пройти long тест в S.M.A.R.T не увенчиваются успехом. Вырезки из S.M.A.R.T

# smartctl -a /dev/sda
...
Num  Test_Description    Status                  Remaining  LifeTime(hours)  LBA_of_first_error
# 1  Extended offline    Completed: read failure       90%     32520         976769972
...
# smartctl -a /dev/sdb
...
Num  Test_Description    Status                  Remaining  LifeTime(hours)  LBA_of_first_error
# 1  Extended offline    Completed: read failure       90%     32519         55084971

Таким образом становиться ясно, что пока в массиве представлено 2-а жестких диска он «малость» работает исправно. Перекидывая неудачные попытки чтения с одного диска на другой диск. Это выражается в следующих сообщениях:

Aug 22 09:12:18 one kernel: [153097.501298] end_request: I/O error, dev sdb, sector 750558140
Aug 22 09:12:18 one kernel: [153097.508526] raid1: sdb4: rescheduling sector 695471160
Aug 22 09:12:18 one kernel: [153097.515736] raid1: sdb4: rescheduling sector 695471408
Aug 22 09:12:30 one kernel: [153109.603257] sd 4:0:0:0: [sdb] Unhandled sense code
Aug 22 09:12:30 one kernel: [153109.603259] sd 4:0:0:0: [sdb] Result: hostbyte=DID_OK driverbyte=DRIVER_SENSE
Aug 22 09:12:30 one kernel: [153109.603262] sd 4:0:0:0: [sdb] Sense Key : Medium Error [current] [descriptor]
Aug 22 09:12:30 one kernel: [153109.603277] sd 4:0:0:0: [sdb] Add. Sense: Unrecovered read error - auto reallocate failed
Aug 22 09:12:30 one kernel: [153109.603281] sd 4:0:0:0: [sdb] CDB: Read(10): 28 00 2c bc 9b b5 00 00 08 00
Aug 22 09:12:30 one kernel: [153109.603287] end_request: I/O error, dev sdb, sector 750558140
Aug 22 09:12:30 one kernel: [153109.610682] raid1:md3: read error corrected (8 sectors at 695471248 on sdb4)
Aug 22 09:12:30 one kernel: [153110.033913] raid1: sda4: redirecting sector 695471160 to another mirror
Aug 22 09:12:31 one kernel: [153110.177597] raid1: sda4: redirecting sector 695471408 to another mirror

В случае окончательного выхода из строя одного из дисков массив никогда не сможет восстановиться, потому что чтение с любого диска заканчивается провалом.

# dd if=/dev/sda of=/dev/null bs=1M
dd: reading `/dev/sda': Input/output error
476938+1 records in
476938+1 records out
500106223616 bytes (500 GB) copied, 5724.82 s, 87.4 MB/s

Разумеется, существует вариант сделать копию системы штатным tar архивом. После этого заменить жесткие диски и развернуть ранее сохраненную систему. Еще один из вариантов попытаться заменить один жесткий диск, создать на нем изначально RAID1 массив с одним missing диском, после этого попытаться синхронизировать систему с оставшегося диска с помощью tar или rsync.

Однако было замечено, что у дисков недоступные для чтения сектора находятся в различных местах. Таким образом возникла идея попытаться форсировать remapping испорченных секторов на одном из дисков и попытаться после этого восстановить данные в них с использованием другого полудохлого диска. В ходе этой операции мы получим один диск с полной копией наших данных, с которого можно эти данные полностью прочитать без ошибок. Останется заменить второй полудохлый диск новым, mdadm автоматически прочитает данные и синхронизирует их с новым диском. При желании после синхронизации можно заменить диск, на котором произвели remapping секторов.

Для того, чтобы форсировать жесткий диск произвести remapping секторов необходимо произвести запись в указанный сектор. С минимальной математической частью можно ознакомиться в статье Forcing a hard disk to reallocate bad sectors. В нашем случае эмпирическим путем было выяснено, что сектора с 976769972 по 976769979 имеют проблемы на жестком диске sda

# hdparm --read-sector 976769972 /dev/sda

/dev/sda:
reading sector 976769972: FAILED: Input/output error
...
# hdparm --read-sector 976769979 /dev/sda

/dev/sda:
reading sector 976769979: FAILED: Input/output error
#

Таким образом производим фиктивную запись в указанный диапазон секторов

for sector in $(seq 976769972 976769979); do
  hdparm --write-sector $sector --yes-i-know-what-i-am-doing /dev/sda;
done

После этого можно убедиться, что данные сектора доступны для чтения (предыдущей командой командой мы их полностью обнулили)

for sector in $(seq 976769972 976769979); do
  hdparm --read-sector $sector /dev/sda;
done

Теперь необходимо в указанные области записать информацию с соседнего диска из зеркала. Так как нам известно смещение 976769972 и что количество поврежденных секторов равно 8 копируем указанную область целым блоком. Обратите внимание копируем область с диска sdb, записываем на диск sda:

# dd if=/dev/sdb of=copy skip=976769972 count=8
8+0 records in
8+0 records out
4096 bytes (4.1 kB) copied, 9.3381e-05 s, 43.9 MB/s
# dd if=copy of=/dev/sda seek=976769972 oflag=direct count=8
8+0 records in
8+0 records out
4096 bytes (4.1 kB) copied, 0.000874658 s, 4.7 MB/s

Обнуление через вызов hdparm не является обязательным. Можно напрямую записать сектора требуемыми данными через dd командой

for sector in $(seq 976769972 976769979); do
  dd if=/dev/sdb of=/dev/sda skip=$sector seek=$sector oflag=direct count=1
done

Осталось убедиться, что все данные с диска можно прочитать

dd if=/dev/sda of=/dev/null bs=1M
476940+1 records in
476940+1 records out
500107862016 bytes (500 GB) copied, 5622.88 s, 88.9 MB/s

Таким образом в ходе описанных манипуляций у нас есть условно рабочий жесткий диск sda, который мы оставляем в системе и меняем жесткий диск sdb. По завершении синхронизации на диск sdb производим замену sda. По завершении синхронизации имеем полностью исправный массив с целостными данными.

В заключении хочется отметить, что в поиске неисправных секторов незаменимой может оказаться команда

dd if=/dev/sda of=/dev/null bs=512 conv=sync,noerror

При желании можно перенаправить сохранение бинарной копии системы на отдельный носитель в этом случае ключ sync просто незаменим. В случае ошибок сбойные сектора на копии будут заполнены NUL (нулями). Таким образом полученная копия будет полностью соответствовать с точки зрения размера оригиналу.

Вторым незаменимым помощником может служить команда badblocks. Важно помнить, что для ее вызова необходимо указать размер блока равный 512 байтам. По умолчанию программа используется значение 1024. Таким образом вызов будет выглядеть следующим образом

badblocks -v -b 512 /dev/sda

Программа будет считывать сектор за сектором и отображать информацию о секторах, которые не удалось прочитать. Например,

# badblocks -v -b 512 /dev/sda 
Checking blocks 0 to 1565565871
Checking for bad blocks (read-only test): 578915880
578915881
578915882
578915883
578915884
578915885
578915886
578915887
578915888
578915889
578915890
578915891
578915892
578915893
578915894
578915895

В этом случае остается сохранить вывод программы в файл и последовательно перезаписать полученные сектора с другого диска.

Интересные материалы для ознакомления: