четверг, 16 января 2014 г.

Делаем алгоритм шардинга. Два решения: простое и красивое

Надеюсь все знают что такое шардинг. Ну а если вы всего лишь "слышали об этом где-то", то вы по-своему счастливый человек. Шардинг данных это решение "последнего выхода", когда никакие другие оптимизации системы хранения данных вроде индексации, денормализации и кластеризации уже не помогают. При шардинге вы берёте свою таблицу (или коллекцию для noSQL) размером в десять миллионов записей и разрезаете её, к примеру, на 10 таблиц по миллиону. Эти таблицы могут лежать в разных базах (нодах) на разных серверах. Так мы получаем то, за что разработчики высоконагруженных проектов так любят шардинг: бесконечное горизонтальное масштабирование. Чтобы определить для каждой записи ноду в которую её нужно положить или где её следует потом искать мы должны реализовать алгоритм определения ноды исходя из ключа и общего числа нод. Для записей определённой структуры с заранее известным "ключевым" полем это сделать несложно. Если вы режете таблицу пользователей с инкрементным id в качестве ключа, то можно просто определить диапазоны id  для каждой ноды. То же самое при разбиении набора записей с датой в качестве ключа. Но возьмём тяжёлый случай: ключ имеет произвольную структуру. Это не последовательное число и не дата. Просто строка. Как гарантированно отнести такой ключ к определённой ноде?
Ниже я предложу два решения: первое я сочинил сам, второе "подсмотрел" в исходниках jedis - популярного "драйвера" для подключения java-приложений к noSQL хранилищу Redis. 

понедельник, 30 декабря 2013 г.

Java EE: Делаем логи ещё удобнее

Где-то месяц назад я описывал простую систему логирования для web-приложений, которая позволяла, кроме всего прочего, группировать данные в логах в контексте одного запроса. Фича весьма удобная и я с удовольствием использую её в своих проектах. Но, как водится, в процессе эксплуатации вылезли и недоработки. Этот прост о том, как развивалась система, и о том, как она теперь работает.

Что было не так со старой системой?
Да, она группировала вывод в контексте одного запроса к серверу. Вернее в контексте одного потока, запущенного servlet-контейнером для обработки этого запроса. Если servlet не порождал новых потоков, то всё было нормально, но вот если новые потоки создавались, мы тут получали проблему. Логгер писал их отдельно, а значит, вперемешку с остальными потоками. Мелочь, а неприятно.

пятница, 15 ноября 2013 г.

Включаем multitouch и "natural scroll" для touchpad-а Dell Inspiron n5110

Сегодня переехал на "новый" Dell. Поставил свой любимый дистрибутив Elementary OS, с привычным удовольствием настроил софт, разложил всё по местам... И тут - разочарование: прокрутка двумя пальцами, к которой уже привык на предыдущем ноуте не работает. То, что система не предлагала эту опцию навело меня на мысль, что мой touchpad не определился как multitouch-устройство. А ведь это не так! Несколько минут поиска по форумам дали достаточно простое решение:
Скачиваем ALPS dlkm driver
Распаковываем его. Каталог psmouse-alps-1.3 с правами root копируем в /usr/src
Выполняем 
sudo dkms add /usr/src/psmouse-alps-1.3
sudo dkms autoinstall

sudo rmmod psmouse
sudo modprobe psmouse
После этого multitouch определяется корректно и мы можем включить нашу любимую опцию.
Если этого не случилось, попробуйте подменить 
/usr/src/psmouse-alps-1.3/src/alps.c исходниками взятыми отсюда и повторить процедуру. 
Собственно вся технология взята тут, судя по коментариям она отлично работает на большинстве linux-дистрибутивов.

Попутно нашёл ещё один полезный фокус для любителей этого няшного дистрибутива. Если вам нравится "natural scroll" в стиле Mac, в файле /usr/share/X11/xorg.conf.d/50-synaptics.conf добавляем строки:
Option "VertScrollDelta" "-111" 
Option "HorizScrollDelta" "-111"
так чтобы секция выглядела следующим образом:
Section “InputClass”
Identifier “touchpad catchall”
Driver “synaptics”
MatchIsTouchpad “on”
Option “VertScrollDelta” “-111”
Option “HorizScrollDelta” “-111”
MatchDevicePath “/dev/input/event*”
EndSection

среда, 13 ноября 2013 г.

"Вечный Service" в Android

Когда мы делаем приложения, мы руководствуемся самыми гумаными соображениями. Мы хотим облегчить жизнь нашим клиентам, помочь им, защитить, вооружить против любых проблем этого ужасного мира. Но клиенты почему-то не хотят покоряться нашему мудрому руководству. Закрывают наше приложения, останавливают его, находят в списке "запущенных процессов" через настройки и опять-таки останавливают. Ну, как дети малые, ей-богу ;) Если вы в своей всеобъемлющей мудрости хотите спасти своих клиентов от их самих, вам наверняка прийдётся защититься от их жалких попыток упавлять своим смартфоном. Как же это сделать? Перенести логику в Service, чтобы закрытие приложения не останавливало его работу? Недостаточно. Сделать в нём вечный цикл, вызвать startService() в onDestroy() - тоже. В конце концов эти неразумные существа могут перезагрузить смартфон и мы утратим над ними власть не сможем помогать им. Можно, конечно ловить событие загрузки (и ещё стопицот других системных событий) и поднимать наш сервис. Однако система на то и система, чтобы жить своими, непредсказуемыми событиями, а значит никакой гарантии, что эти события произойдут когда нам нужно, увы, нет. Как же нам быть? Ответ очевиден:

среда, 6 ноября 2013 г.

UDP в Android: как приложения ищут друг друга в сети?

В большинстве случаев говря о передаче данных по сети мы имеем в виду TCP. Для большинства сетевых задач этот протокол лучший, но он вообще-то совсем не единственный. И есть вещи, которые с его помощью делать не удобно. Например: мы знаем порт, но не знаем IP-адреса получателя и нам нужно его найти. Опрашивать все адреса локальной сети по очереди? Жуть... Тут надо бы послать широковещательное сообщение, а для этого мы уже используем UDP. Этот протокол достаточно интересен. Например, мы можем слать сообщения и слушать на одном и том же проту одновременно. UDP при отправке широковещательных сообщений не создаёт соединения в привычном нам смысле. Мы не знаем, доставлено ли получателю наше UDP сообщение. Впрочем нам это и не нужно. Передавать данные мы будем уже по TCP. Итак как же экземплярам нашего приложения искать друг друга?

среда, 30 октября 2013 г.

Java EE: Удобные логи - это просто

- Привет, тут у меня клиент, он не видит в архиве своих операций. Глянь плиз, что у него не так? Только быстрее, он VIP, он злой и висит на линии...

Если вы пишите серверные приложения, то такие просьбы девочек из саппорта вам знакомы. И знакомо то чувство отвращения, злости и отчаяния с которым вы бросаете свой незаконченный рефакторинг в самом неподходящем месте, чтобы залезть в консоль и смотреть логи.
А логи, как на зло пишутся по старинке, потому что разбираться с log4j некогда, да и неохота... Вот если бы сливать логи в базу, да запилить админку... Но когда этим заниматься? 100500 задач в разработке а тут ещё эти логи.
Как же быть? Выделить день-другой и переписать логирование на какой-нибудь фреймворк? Это можно. Но мы ведь программисты, давайте сначала оценим задачу. Итак чего бы нам хотелось?
  • Логи не должны писаться вперемешку. Все записи в контексте одного запроса должны в логах находиться в одном месте, "пакетом".
  • Все записи одного уровня должны маркироваться общим тегом
  • Вместе с пакетом записей должна быть возможность вывести в лог произвольные данные (например id клиента и т.п.), относящиеся к этому запросу. 
И всё? И для этих трёх простых вещей тащить в свой проект ещё один фреймврок? Нет уж, давайте напишим несколько строк кода и покончим с этим ;)