19 ноября 2010 г.

Грабли в MySQL

Сегодня, как обычно случайно, выяснил, что если подключиться к одной базе и начать там транзакцию с помощью START TRANSACTION/BEGIN, то любой запрос к другой базе приостанавливается — MySQL ждёт, когда вы закончите открытую транзакцию к первой базе.

21 октября 2010 г.

Вся мощь Vim

Я люблю Vim странной долгой любовью. :) Встречая такие мануалы, понимаю, что только начинаю изучать его. Осталось понять сей славный документ, и можно называться Vim-гуру. B-)

8 сентября 2010 г.

Server-side jQuery

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

25 июня 2010 г.

XtraDB и MariaDB

Оказывается, правильные пацаны потихоньку переходят на XtraDB. Как пишет Википедия:
В последнее время из-за излишней закрытости разработки MySQL компанией Sun Microsystems появилось много сторонних (например, от компании Google) патчей с улучшениями производительности и исправлениями ошибок, большинство из которых были включены в форк InnoDB под названием XtraDB, созданный компанией Percona.

Percona была создана в 2006 году для консультирования и поддержки MySQL-пользователей. И основатели её имеют знакомо звучащие имена, как то Peter Zaitsev и Vadim Tkachenko. В общем, наши люди.

Как известно, InnoDB — это лучший движок для таблиц MySQL с нормальной поддержкой транзакций, внешних ключей и т.п. XtraDB, по заявлениям авторов, работает быстрее плюс позволяет осуществлять бэкап таблиц на лету. У InnoDB тоже есть такой инструмент, но он платный, в отличие от XtraDB. Формат таблиц используют они один и тот же, т.е. можно существующую базу пересадить с одного движка на другой без проблем.

А ещё есть форк MySQL под названием MariaDB. Это детище основателя MySQL Michael "Monty" Widenius. Формат БД, API, имена файлов и т.п. полностью совместимы с MySQL. Взамен обещается ускорение и новые фичи. Ну и движок XtraDB включён в комплект вместо InnoDB.

9 июня 2010 г.

О MySQL в многопоточном Python'е

В проекте возникла необходимость доступа к MySQL из разных потоков (threads). Я внимаааааааааательно прочитал официальную документацию библиотеки для доступа MySQL из Python, и она заявляет, что нельзя использовать одно и то же подключение к БД в разных потоках.

На помощь спешит PySQLPool, в нём есть методы для создания пула подключений к MySQL.

Я использую движок таблиц (table engine) InnoDB с нормальной поддержкой транзакций. Совсем неинтересно каждый раз писать BEGIN TRANSACTION/COMMIT, это ж Python! Должен быть способ! И он нашёлся.

В версии 2.5 добавили оператор with и контекстные менеджеры. Я написал простенький патч, и использование транзакций стало простым и красивым:
q = PySQLPool.getNewQuery(connection)
with q: # внутренние запросы обёрнуты в транзакцию
            q.Query('INSERT INTO peer SET PeerName=%s', (name,))

Есть и маленькая ложка дёгтя в виде заявленного автором бага при работе с транзакциями из нескольких тредов. В общем-то, ко времени релиза нашего проекта либо я исправлю этот баг, либо автор, либо вы. ;)

14 мая 2010 г.

Сетевое программирование в Python

Нашёл хороший туториал (PDF) на английском про сетевое программирование в Python — чётко изложена сама теория, кое-чего я и сам не знал, и даны хорошие базовые примеры на Python.
На сайте автора есть и другие неплохие туториалы про Python.

5 мая 2010 г.

UDP-траспорт

В проекте, над которым я работаю, понадобилось использовать асинхронный UDP-траспорт. Как выяснилось, есть в этой задаче несколько подводных камней — о них и пойдёт речь.

Хотелось что-то максимально простое с помощью встроенных в Python средств.

Первый вариант — класс SocketServer.UDPServer. Минус его в том, что он использует блокирующие сокеты; для обхода этого в том же пакете есть и неблокирующие версии, реализованные с помощью тредов или даже отдельных процессов на каждого клиента. Но это не совсем асинхронность.

Второй способ — пакет asyncore, который как раз для асинхронных сообщений и предназначен. Правда, только для TCP. Создать нужный класс несложно, но есть несколько тонкостей:
class UdpTransport(asyncore.dispatcher, object):
    def __init__(self, addr=None):
        super(UdpTransport, self).__init__()
        self.ignore_log_types = set() # Для вывода дополнительных ошибок в asyncore, подсмотрел в исходниках asyncore :)
        self.create_socket(socket.AF_INET, socket.SOCK_DGRAM)
        # Размер этого буфера очень важен
        # Если входящий пакет будет больше него, остаток отбрасывается
        self.ReadBufferSize = self.socket.getsockopt(socket.SOL_SOCKET, socket.SO_RCVBUF)
        if addr:
            self.bind(addr) # Единственное отличие серверного сокета в использовании bind()
        
    def handle_close(self):
        self.close()

    def writable(self):
        return False

    def handle_read(self):
        try:
            data, addr = self.recvfrom(self.ReadBufferSize) 
        except socket.error: # Исключение возникает при чтении данных из сокета: когда они заканчиваются, следующая попытка вылетает
            pass
        # обработка данных

    def send(self, msg, toaddr):
        self.socket.sendto(msg, toaddr)

    handle_connect = handle_read # Нарыл где-то на просторах инета
    # Без этого asyncore не работает с UDP, первую порцию данных он интерпретирует как инициализацию подключения
Наследуемся и от object для использования super и отладки с помощью метакласса.

Вот, собственно, и всё, все хитрости прокомментированы в коде. Естественно, нам не требуется connect() для серверных сокетов, а вместо socket.send/recv нужно использовать методы sendto/recvfrom. Сперва я путался, в каких случаях что нужно применять — документация лаконична — но нашёл хороший источник с примерами, это наш родной `man`. ;) Например, `man bind` (или `man 2 bind`, если быть точным) показывает документацию С-шного сетевого API, обёрткой вокруг которого socket, собственно, и является. Если на вашем *nix такой странички нет, нужно установить пакет manpages-dev (в Debian и Ubuntu, по крайней мере).