2013-12-28

О пони

Ну раз уж праздники, поговорим о понечках.


Кому лень читать нижележащее графоманство, сразу отправляю на подробное изложение культурного феномена понечек на Луркоморье. Тех, кто сразу хочет погрузиться в мир понечек и их фанатов, велкам, например, на everypony.ru.


Впервые я услышал о понечках в декабре 2011 года. Когда было несколько часов до поезда из Екатеринбурга и мы с коллегой шатались по городу. Вот она как раз закупила так называемые blind pack с понечками. Это такие пакетики, в которых скрывается маленькая фигурка понечки, неизвестно какая.
А где-то год спустя мы с дочей увидели одну серию на канале "Карусель". И я понял, что нужно посмотреть и остальные серии. Посмотрел. Не пожалел. Кстати, если будете смотреть, не жадничайте и возьмите Full HD. И обязательно оригинальную звуковую дорожку, переводы не блещут качеством и полны ляпов. Когда-то все серии можно было найти на Ютубе, но Хасбро прикрыло лавочку :(


Итак, известная игрушечная фирма Hasbro издавна делала игрушечных пластмассовых пони. Чтобы маленькие девочки могли их расчесывать. Собственно, это и были My Little Pony (сокращенно MLP). И, конечно же, делала мультики про этих пони. Как обычно, невнятные слюнеточивые игрушечные мультики.
Но вот в 2010 году они решили перезапустить серию и пригласили для этого некую Лорен Фауст, тусившую на DeviantArt и несколько фанатевшую от поней. И это был вин. Лорен полностью перерисовала поней, переосмыслила концепцию, переписала сценарии. Так родились My Little Pony: Friendship is Magic (MLP: FiM). И этот новый сериал оказался чрезвычайно популярным, в том числе и среди бородатых мужиков, назвавших себя брони (bro + pony).
Собственно, сериала было выпущено четыре сезона (четвертый сезон состоялся в конце 2013 года и еще не дошел до российского телевидения), по 26 двадцатидвухминутных серий на сезон (в третьем сезоне - 13 серий). Плюс полнометражный мультик Equestria Girls, где понечек превратили в девочек, что вызвало бурю негодования фанатов.


Пони живут в стране Эквестрии (Equestria, от лат. equus - лошадь). Столицей страны является город Кантерлот. Основное же действие происходит в городе Понивилль (Моя доча так и говорит: "Включи мне Пони Мили". Милые пони.)
Пони бывают трех видов. Собственно, обычные "земные" пони. Единороги, с рогом, владеющие магией. Пегасы, с крыльями, умеющие летать и ходить по облакам. Есть еще аликорны, пони с рогом и крыльями. Их всего несколько штук и их называют принцессами. Собственно, все аликорны - девочки. И вообще поней женского рода в тамошнем мире значительно больше, чем мужского.
Главных персонажей - шесть. Все девочки. Плюс юный дракончик Спайк - помощник Искорки. Познакомимся с ними и некоторыми второстепенными персонажами.

Пинки Пай (Pinkie Pie, Pinkamina Diane Pie). Самая знаменитая пони. Собственно, если вы не брони, то вы всех пони будете называть Пинки Пай. Запомните, только Пинки - розовая. Остальные - не розовые. Самая сумасшедная пони, способна нарушать целостность пространства и времени. Любит устраивать вечеринки (в девчачьем смысле этого слова).

Радуга (Rainbow Dash). Заметна по своей радужной прическе. Самый быстрый летун Эквестрии. Умеет преодолевать скорость звука и делать Sonic Rainboom (непереводимый термин), завершающийся радугой. Хулиганка.

Эплджэк (Applejack). Осторожней с другими значениями слова "applejack" в английском языке. Кристалльно честная и работящая пони. Выращивает яблоки. Верная боевая подруга.

Рарити (Rarity). Красотка. Имеет слабость к бриллиантам и другим драгоценным камням и умеет их находить. Модельер. Модница.

Флаттершай (Fluttershy). Очень застенчивый пегас (flutter - дрожать, shy - застенчивый). Умеет разговаривать с животными и очень их любит. Добрейшая пони.

Сумеречная Искорка (Twilight Sparkle). Главная героиня. Ученица принцессы Селестии. Самая разумная и начитанная пони. В конце третьего сезона, за особые заслуги перед отечеством, обзавелась крыльями, стала аликорном и стала зваться Princess Twilight Sparkle.

Спайк (Spike). Юный дракон. Секретарь, помощник и связной Искорки. Любит есть драгоценные камни. Влюблен в Рарити. Романтичен, самовлюблен, добр. Мальчик :)

Прицесса Селестия (Princess Celestia). Самая главная правительница Эквестрии. Учит Искорку. Ей более тысячи лет. Оттого всегда говорит меньше, чем знает, чем напоминает мне Гэндальфа. Иногда троллит подчиненных и Искорку, за что фанаты называют её Троллестия. 

Дискорд (Discord). Злодей в паре серий. Лучший злодей всего сериала. Технически - бог и повелитель хаоса. Однако обладает чудесным чувством юмора. В результате его хаос, например, включает в себя зефирные облака с шоколадным дождем (от чего была в восторге Пинки Пай). 


Собственно, одним сериалом все не органичивается. Фанаты рисуют своих пони, в том числе и в человеческом обличье. Половина ДевиантАрта - сплошные пони (оттуда удобно брать понечек для презентаций, можно найти любой сюжет и любое выражение лица). Фанаты умудряются устраивать косплей (хотя, казалось бы, для четвероногих персонажей это затруднительно). Фанаты снимают свои мульты. Фанаты пишут свои рассказы (осторожно, "Кексики" читайте только после того, как хорошо изучите характеры всех персонажей). А всякие Gameloft делают мобильные игры.


Ну и, в конце концов, можно покупать игрушечных пони и приносить прибыль Хасбро. Ради чего все это и задумывалось. Что мы с дочей и делаем.


Ну и, собственно, почему понечки. Я знаю только один мультсериал добрее, поучительнее и полезнее MLP - это отечественные "Смешарики". Ну а с "Понечками" можно и английский подучить ;)

2013-12-22

Об HA-JDBC

Есть такая, довольно молодая и еще малоизвестная, библиотека HA-JDBC. HA - это High Availability. JDBC - это, очевидно, Java DataBase Connectivity.


Как и следует из названия, библиотека предназначена для осуществления доступа к вашему высокодоступному кластеру БД посредством JDBC. Это прокси и балансировщик SQL соединений и запросов.
Обычно балансировка SQL запросов осуществляется отдельными демонами. Например, Pgpool для PostgreSQL или MySQL Proxy. HA-JDBC же позволяет добавить "интеллекта" прямо в ваше ява-приложение, добавив возможность подключения одновременно к нескольким СУБД.
Вообще-то что постгресовый, что мускульный JDBC драйверы сами умеют подключаться к нескольким хостам. И, собственно, каким-то образом балансировать нагрузку. Но HA-JDBC позволяет более тонко настраивать как балансировщик, так и другие плюшки.
HA-JDBC также может взять на себя репликацию и восстановление после сбоев. Вы можете взять две (или более) независимых БД, а HA-JDBC будет распараллеливать все запросы записи, чтобы данные во всех базах были одинаковыми. При этом он даже может позаботиться, чтобы всяческие последовательности и автоинкременты в базах совпадали. В случае, если какая-то из БД кластера уходила, а потом вернулась, HA-JDBC может синхронизировать данные на ней, вплоть до снятия дампа с рабочей базы и накатывания его на восстановленную. А чтобы несколько инстансов HA-JDBC (несколько экземпляров вашего приложения) не подрались друг с другом за то, кто будет восстанавливать, HA-JDBC сами объединяются в свой кластер посредством JGroups.

Впрочем, пока я предпочитаю полагаться на встроенные механизмы репликации, а HA-JDBC оставить лишь балансировку нагрузки и отработку падений узлов БД. Собственно, мы имеем два PostgreSQL 9.3 с потоковой репликацией между ними, несколько клиентов с HA-JDBC на борту и балансировку запросов чтения. Характер данных таков, что чтения значительно больше чем записи. Поэтому мы спокойно (и без всяких балансировщиков) пишем в единственный мастер. Полагаемся на потоковую репликацию. И интенсивно читаем с балансировкой и отказоустойчивостью.


HA-JDBC весьма универсальная штука. Она может проксировать как старые добрые JDBC драйвера (где адрес и свойства подключения задаются в URL вида jdbc:blablabla), так и DataSource и даже ConnectionPoolDataSource. Впрочем, библиотека не умеет преобразовывать одни виды подключения в другие. Если вы запрашиваете HA-JDBC подключение у DriverManager, библиотека ожидает, что проксируемые подключения тоже определены через URL драйвера. Если же вы запрашиваете DataSource, то и проксируемые подключения должны быть определены как DataSource. Тут главное - не запутаться. Видимо, в сложных случаях стоит использовать либо URL драйверов (где каждое подключение хорошо идентифицируется именно URL), либо DataSource, помещенные в JNDI (тогда у каждого подключения будет свое JNDI имя).
Вариантов синхронизации БД (после сбоев) - множество. Но так как у меня репликация осуществляется средствами БД, я поставил просто passive, т.е. ничего не делать.
Хранить состояние кластера (какие узлы живые и т.п.) можно в памяти, можно в SQLite под боком, а можно в любой SQL базе. В моем случае simple в памяти выглядел достаточным.
HA-JDBC поддерживает кучу БД (диалектов), включая DB2, Oracle и Sybase. PostgreSQL, понятно, поддерживается.
Я пробовал пару балансеров: round-robin и random. Почему-то random показал более интересные результаты, запросы более равномерно распределялись между двумя Постгресами.
У HA-JDBC есть еще различные политики работы с транзакциями (повторять ли транзакцию целиком на втором узле, если она сломалась на одном), идентификаторами, последовательностями и т.п. Так как у меня через библиотеку проходят запросы только на чтение, я все эти возможности просто поодключал.

В общем, HA-JDBC тупо проксирует подключения к БД. Например, если приложение запросило два подключения (дважды вызвало getConnection() у DriverManager или DataSource), то HA-JDBC открывает по два подключения к каждой БД.
При этом запросы (именно запросы, Statement, а не подключения) вполне успешно балансируются между базами.
HA-JDBC сразу искаропки успешно отрабатывает проблемы с БД. Я, с помощью фаейрвола, блокировал подключение к одной из БД. Это приводило к исключению при выполнении одного из запросов. Это исключение перехватывалось HA-JDBC, она помечала базу, как недоступную. Запрос повторялся на живой базе. И все последующие запросы шли только к живой базе. Примечательно, что исключение даже не доходило до приложения.
С настройками по умолчанию HA-JDBC никогда не вернет отключенную базу обратно в кластер. Можно, конечно, перезапустить HA-JDBC (т.е. ваше приложение), тогда (если вы не использовали персистентное хранилище состояние кластера) снова будут открыты подключения ко всем сконфигурированным БД. В рантайме же можно дать понять HA-JDBC о том, что мы хотим вернуть БД в кластер, через JMX, есть соответствующий бин. Ну или можно просто задать расписание (в Cron-синтаксисе) проверки доступности отключенных узлов. Например, так: auto-activate-schedule="*/15 * * ? * *".
В моем тесте возникли сложности с активацией вернувшейся в строй БД. Приложение открывает подключения к БД в самом начале эксперимента. И понятия не имеет, что там какие-то узлы отключались и подключились обратно. HA-JDBC все тонкости успешно скрывает. Т.е. приложение имеет свои два экземпляра java.sql.Connection и продолжает использовать их. Однако, драйвер PostgreSQL тоже не дурак, он знает, что запрос на одной коннекции свалился из-за проблем с сетью, и помечает эту коннекцию как закрытую. Это не Connection, видимое приложению, это одно из Connection, проксируемых HA-JDBC. HA-JDBC, по указанному расписанию, соображает, что проблема со связью ушла, и начинает передавать запросы через закрытое соединение. Что приводит к новой ошибке, о том, что соединение уже закрыто. И HA-JDBC вновь помечает БД как неработоспособную. В результате БД никогда не возвращается в работоспособное состояние, а все запросы продолжают падать на БД, над которой не издевались.
Т.е. HA-JDBC не пересоздает подключения, если с ними возникли проблемы. У MySQL драйвера есть параметр autoReconnect, который, теоретически, как раз и решает подобную проблему. Но у PostgreSQL нет такого параметра.
Казалось бы, задачей проверки валидности подключений должны заниматься пулы коннекций. Я помучал C3P0 и DBCP. Первый вообще не удалось завести под HA-JDBC, по всей видимости потому, что в Яве 1.6 умудрились модифицировать java.sql.Connection, добавив метод isValid(), а в сгенерированных прокси C3P0 этого метода еще нет. Однако выяснилось, что оба пула проверяют коннекции только в трех случаях: при получении коннекции из пула, при возврате коннекции в пул и в фоне для неиспользуемых коннекций пула. Коннекции, используемые в приложении никем не проверяются.
Ну а если тупо для каждого запроса получать новую коннекцию и закрывать её потом, то получается все хорошо. При возвращении БД в строй запросы снова начинают честно балансироваться.
Конечно, открывать коннекцию на каждый запрос - неэффективно. Но, выходит, держать в приложении одну и ту же коннекцию часами - тоже неудобно. Нужно периодически запрашивать новые коннекции, иначе ни HA-JDBC, ни пулы коннекций правильно работать не будут.
Понятно, что нужно использовать пул коннекций, чтобы пул успешно переиспользовал и проверял коннекции. Однако тут возникает вопрос, что правильнее: пул HA-JDBC коннекций или HA-JDBC поверх нескольких пулов. Мне почему-то кажется, что второй вариант правильнее. Осталось только запилить его.

P.S. Код экспериментов и конфигурацию HA-JDBC можно подглядеть на Bitbucket.
P.P.S. Графики рисовались Graphite, по данным, собираемым Diamond.

2013-12-09

О HappyDev


Вот и завершилась очередная HappyDev. Конечно, в понедельник будет еще парочка мастер-классов. Но меня на них уже не будет.
В этот раз все было на базе отдыха им. Стрельникова. Это такая широко известная в узких кругах здравница всея Омска (ну и другие базы отдыха к северу от нефтезавода тоже). Однако мне, в силу ряда обстоятельств, не удалось вкусить всей прелести общения на природе. А именно, я пропустил преферанс до шести утра, глинтвейн, щедро разбавленный коньяком, ночное обсуждение Вопроса Итераторов (Шурик, если читаешь, кинь ссылкой, что ты там накодил в воскресенье), песни под гитару, горки, снежки, лепку грязевиков, йодо-бромный бассейн. Но и без этого было весьма весело. Судя по виду некоторых коллег, особенно в воскресное утро, эти выходные были мощнейшим ударом по печени омского ИТ сообщества. Надеюсь, не зря.
Успешно уклонился от взятия интервью двумя знакомыми журналистами. С ума сойти, у меня есть знакомые журналисты. Ох, поймают они меня еще...


На хороших конференциях случается, что некоторые моменты повторяются из доклада в доклад. Здесь такое случилось с Руби и Монгой. Многие докладчики сознались, что недолюбливают Руби на его неочевидность, но пишут в основном на нем. Многие кидались грязью на Монгу. Что она нестабильная. Что где есть node.js, внезапно возникает Монга и потом её приходится выпиливать. Александр Чистяков утверждал, что если роутер в Монге испортить не смогли, то вот реализацию хранилища (того самого, с memory mapped files) без смеха читать невозможно.
Идея организовать секции конференции в обратном порядке (относительно типичного жизненного цикла проекта) дала результаты. Было приятно слышать не "а вот об этом еще будет доклад", а "как вы уже слышали". Приятно, что "Тестирование" пересеклось с "Жизнью после релиза" к контексте Continuous Integration, а "Работа с требованиями" ссылалась на "Архитектуру" в контексте DDD.
Между делом, кстати, вывели концепцию идеальной архитектуры: отговорить заказчика делать Это, ничего не написать. Нет кода, значит, нет архитектуры, которая кому-то не понравится. Это очень здорово согласуется с концепцией идеального кода, согласно которой идеальным также является код, которого нет.
На конференциях всегда случается, что организаторы не угадывают популярность тем, в результате чего в маленькой аудитории набивается уйма народу. Здесь проблему дважды неизящно решили перестановкой секций. Внезапно. "Люди" оказались популярнее "Тестирования", а "Frontend" популярнее "Баз данных". В итоге, конечно, получилось лучше. Но я все думаю, а что, если сделать залы на всех потоках одинакового размера?


Мне посчастливилось делать вводные доклады и вести аж две секции: "Жизнь после релиза" и "Базы данных". Третий раз становлюсь докладчиком на больших конференциях и ловлю себя на ощущении, что восприятие других докладов при этом притупляется. Все переживания вертятся вокруг своего доклада, а на другие как-то пофигу. Это огорчает. Зато докладчикам достаются другие бесплатные плюшки :)
Меня, как ведущего секций, огорчило, что народ больше кинулся на параллельные потоки: "Процессы" и "Frontend". Как-то печально выступать перед полупустым залом. Впрочем, если быть честным, я бы, может, не будучи ведущим, тоже туда убежал бы :) В любом случае, доклады не пропадут и найдут свою аудиторию :)
Еще непривычно, после выступлений перед аудиторией в пять сотен человек, видеть перед собой лишь две сотни. Однако, по словам того же Александра Чистякова, меньший размер аудитории с лихвой компенсируется более живой реацией, большим количеством вопросов, и намного большим количеством разговоров потом. Чем на таких бОльших конференциях, как HighLoad++ и CodeFest. Может, сказывается атмосфера базы отдыха. И это хорошо.
Мои секции прошли хорошо. Огромное спасибо докладчикам. Без вас ничего бы не получилось. Еще встретимся.


Ну и напоследок, тем кто не был на конференции или не смог посетить все доклады (как я), вот список тех докладчиков, которых обязательно нужно послушать и посмотреть (безусловно, моё имхо). Надеюсь, видео по ссылкам на сайте конференции вскоре появится.
  • Александр Чистяков, мастер на все руки и душевнейший человек, здесь рассказывал про Chef и HBase.
  • Евгений Тюменцев, его доклады надо пересматривать несколько раз, сильно много надо вдумываться :). Здесь рассказал про тестирование с помощью Mock объектов, но я знаю, что у него есть много других интересных докладов в области ООП.
  • Николай Рыжиков, кратко и четко рассказал про архитектуру вообще.
  • Алексей Пименов, о его Мотивации 3.0 потом еще целый день вели разговоры.
  • Максим Цепков, этого человека надо слушать, а лучше разговаривать с ним лично :)
  • Дмитрий Лобасев, вы его знаете, и в этот раз речь была не сколько и не столько о Agile/Scrum, а скорее о методологиях вообще.
  • Кирилл Мокевнин, создатель ульяновского ИТ, знает, что делает и делает правильно.

2013-11-24

О JSON в PostgreSQL

Как известно, в последних версиях PostgreSQL появилась поддержка JSON. Соответствующий тип данных появился в версии 9.2. Набор функций для работы с JSON был существенно расширен в версии 9.3.
В связи с этим, а также потому что у нас JSON используется для передачи данных по сети, возник соблазн воспользоваться новым типом данных для того, чтобы сэкономить на сериализации реляционных данных. Еще раз, данные - преимущественно реляционные, мы их сериализуем в JSON каждый раз для передачи по сети. Вопрос: целесообразно ли хранить уже сериализованные данные в JSON поле (конечно же в ущерб удобству модификации этих данных)?
Еще в списке функций для работы с JSON соблазнительной выглядит row_to_json(), которая может представлять строку обычной таблицы (или выборки) в виде "плоского" JSON документа, содержащего поля одного уровня вложенности согласно колонкам результата. Примерно такого документа:
{ "column1" : value1, "column2" : value2 }

Чтобы проверить возможности PostgreSQL я написал простенький тест. Вот такие получились результаты для PostgreSQL 9.3 на моем локалхосте:
Я просто сгенерировал 100 тысяч строк с десятью текстовыми колонками по 100 байт случайных символов, а также 100 тысяч аналогичных JSON документов (в отдельной таблице) и пытался выбирать эти строки и документы различными способами. Я делал просто SELECT *, я просто выбирал JSON документ целиком, я делал ручную сериализацию, я преобразовывал реляционные строки в JSON с помощью row_to_json(). Я повторил это для половины всех колонок и полей JSON документа (красные полоски).
Быстрее всего оказалось просто извлечь JSON документ. Однако извлечь половину полей получалось значительно медленнее, позже объясню почему. Удивительно, что получение реляционной строки через row_to_json() практически идентично по времени с получением этих данных напрямую. Это значит, что если вас устраивает примитивный "плоский" JSON, то использование row_to_json() (т.е. сериализация на стороне PostgreSQL) может быть хорошим решением. Ну и сериализация в JSON на клиентской стороне - не так уж и медленна (я использовал Python, не самый быстрый вариант).

Я, конечно, не удержался и повторил эксперимент в MongoDB.
Монга слегка подпортила мне набор данных тем, что добавила _id ко всем документам. Еще (как минимум в Python) оказалось, что документ, возвращаемый драйвером Монги - вовсе не JSON, и его нужно явно сериализовать. Зато Монге не требуется титанических усилий, чтобы извлечь часть полей из документа.

Так почему же PostgreSQL такой медленный, когда приходится делать "срезы" JSON? Посмотрите еще раз на список функций и операций над JSON. Здесь есть извлечение значений отдельных полей (причем можно сразу в виде текста). Есть преобразование в другие типы. Есть возможность извлечь поле вложенного документа. Но нет ничего, чтобы сделать "срез". В результате приходится извлекать по отдельности n полей и затем склеивать их в новый JSON. И, судя по этому графику, извлечение поля - весьма затратная операция.
Я замерил время выборки 100 тысяц строк с различным числом колонок/полей: из обычной SQL таблицы, из JSON документа, хранящегося в PostgreSQL, из MongoDB.

В общем, PostgreSQL - это вам не MongoDB. JSON здесь - лишь CLOB с проверкой синтаксиса. И использовать его нужно целиком, "как есть". Есть, конечно, операции доступа к внутренностям этого CLOB, но они довольно дороги. И (в отличие от Монги) совсем нет операций для модификации JSON документа "на месте". Ну вы поняли.

Для себя мы остановились на обычных SQL таблицах и, по возможности, использовании row_to_json().

2013-11-17

О ворчунах

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

Гном Ворчун из диснеевской "Белоснежки" 1937 года. Классический ворчун. Единственный не поддавался женскому очарованию Белоснежки. Однако сдался после поцелуя в лысину и далее был исключительно на её стороне. В интернетах считают, что Ворчун мог бы составить с Белоснежкой гораздо более интересную пару, чем какой-то банальный принц.

В "Мишках Гамми" тоже был Ворчун. Больше я про него ничего не помню.

Есть еще и другие "Заботливые мишки". Со своим Ворчуном. Этот Ворчун более прагматик, нежели ворчун. Мне нравится. Знаменит своими невероятно вкусными печеньями, сделанными, тем не менее, как попало и из чего попало.

Мистер Брюзга (так уж перевели) из "Мистера Мэна" - французского мультсериала с толстым английским юмором. Самый обычный синий квадратный ворчун. Интересен не столько он сам, сколько мультик и, полагаю, книги. Действительно исконно английского автора. Почему-то мне кажется, что эти круглые, квадратные (а иногда треугольные) мистеры и мисс как-то вдохновили создателей отечественных "Смешариков".

Смурфный ворчун. Тоже синий. Идеальный ворчун в том отношении, что ему действительно ничего никогда не нравится.

Шрэк - замечательный ворчун. Тут добавить нечего. Хотя, пожалуй, излишне добродушный. Зеленый.

Капитан (тоже) Зеленый из "Тайны третьей планеты". Очень любимый мною ворчун. Прекрасно понимает, что ворчать бесполезно, а потому просто приходит и спрашивает: "Ну, что у нас плохого?"

В "Понечках" явных ворчунов не наблюдается. То ли потому, что контингент преимущественно женский. То ли потому, что главгероинь меньше семи.
Хотя Спайк иногда ворчит. Особенно когда его просят что-то сделать тогда, когда он хочет спать.
А вот Рэйнбоу Дэш, когда её покусал "заколдовал" Дискорд, превратилась вполне так в полноценного ворчуна. Всех посылала подальше с их заботами. Жаль, что ненадолго :)



P.S. Пока искал картинки с Рэйнбоу Дэш, обнаружил такой вот шедевр:
Это не про ворчунов, зато про лень :) Картинка, кстати, называется "Best job EVER". (с) AxemGR



2013-11-10

О Кассандре

На HighLoad++ я проиллюстрировал Кассандру портретом Зекоры.
С тех пор мое отношение к ним не изменилось :) И теперь есть пони про NoSQL ;)

Да. Мы используем Кассандру. Которая Apache Cassandra.

Каждый знает, что модель данных в Кассандре, это BigTable, или ColumnFamily или еще как-то. Нужно только помнить, что здесь таблица, это не совсем та, и даже совсем не та таблица, что в SQL. Здесь у каждой строки есть обязательный, уникальный и единственный ключ. В каком-то смысле это аналог первичного ключа в SQL. Только обязательный и уникальный. И не может быть составным. И не имеет явного имени. Ключ используется для шардинга. Строка - единица хранения, и именно строки распределяются между узлами кластера и реплицируются. Ключ используется для записи. Для любой операции записи нужен ключ строки и обязательно он (ну и имена и значения колонок, конечно).
Колонок в таблице может быть сколько угодно (реально, миллиарды). Схемы нет. Проблемы c null значениями в "лишних" колонках тоже нет. При желании, каждая строка может иметь абсолютно свой набор колонок. Для правильного использования Кассандры важно знать, что колонки у нее упорядочены по имени. В отличие от строк, которые упорядочены так, как при начальной конфигурации кластера было положено (а положено в 99% случаев в порядке хэшей значений ключа строки).
Собственно, тройка: ключ строки - имя колонки - значение, приводит нас к мартинфаулерскому определению BigTable как мапа мапов (ну или словаря словарей). Только в Кассандре были (и есть) еще супер-колонки, что приводит нас к мапу мапа мапов. А с появлением составных колонок появляется возможность изобразить мапы сколько угодной вложенности. И даже замапить произвольные объекты и даже документы. С нетерпением жду библиотеки, позволяющей сохранять JSON документы в Кассандру как это делает Монга...

Ко всем этим прелестям в виде семейств колонок, колонок, супер-колонок и вторичных индексов доступ осуществляется через бинарный RPC протокол Thrift. Обращаться можно к любому узлу кластера, он займется контролем ваших операций и будет пересылать данные с/на другие узлы кластера. Однако это может быть неэффективным, и даже может быть неприятным (если узел, к которому вы подключились, упадет). Поэтому настоятельно рекомендуется пользоваться Гектором (который брат Кассандры). Это такой умный клиент, который понимает, как располагаются данные в кластере Кассандры, а также мониторит доступность узлов кластера. В результате он передает запрос на правильные узлы, чтобы максимально быстро получить ответ.
Кроме Трифта есть еще CQL (я имею ввиду CQL 3.х). Внешне это язык запросов, обманчиво похожий на SQL. Только помните, что это не SQL. В условиях WHERE вы не можете использовать колонки, на которых нет индексов. Несколько условий в WHERE вы можете объединять только операцией AND, но не OR. UPDATE возможен только по первичному ключу (безо всяких WHERE)... Внутри же CQL коварным образом скрывает и преобразует структуру нашей BigTable, делая её чуть более похожей на реляционную модель. Однако лишая вас веселой возможности манипулировать миллионами колонок.

В общем, Кассандру надо уметь готовить. Например, нам понадобилось обновлять некую группу значений (закодированных в JSON, черт, антипаттерн) из разных источников так, чтобы победил сильнейший последний, кто внес изменения. Если бы время изменений определялось временем прихода их в Кассандру, не было бы проблемы. Просто перезаписываем значение и все, а Кассандра уже сама внутри кластера разберется, какое значение последнее. Но наши источники любят менять данные самостоятельно, по различным печальным причинам сообщая об этом Кассандре позднее. Т.е. разрешать конфликты нужно по таймстампу изменения, присланному вместе с данными. В SQL можно было бы воспользоваться транзакциями и сделать read-modify-write: прочитать таймстамп из базы, убедиться, что пришедшие данные новее и записать их. Но в Кассандре нет транзакций (по крайней мере до версии 2.0) и пока вы будете читать да думать, кто-то другой уже перезапишет ваши данные. В SQL можно было бы сделать и еще проще, что-то вроде условного апдейта:
UPDATE table SET data = new_data, timestamp = new_timestamp WHERE id = id_data AND new_timestamp > timestamp.
Но в Кассандре нельзя делать WHERE на UPDATE. В Монге можно было бы воспользоваться операцией findAndModify (которая во многом аналогична приведенному выше апдейту). Но в Кассандре нет findAndModify.
Что можно делать в Кассандре? Создавать столбцы и хранить все изменения. Собственно, упорядоченные столбцы, это очень даже естественный (для Кассандры) способ хранить time-series data. Ну т.е. берем и на каждый апдейт создаем новый столбец, чье имя содержит значение таймстампа. Получаем кучу столбцов вида: data:timestamp1, data:timestamp2... При чтении выбираем наибольший столбец и вуаля.
Однако тут возникает еще одна неприятная особенность Кассандры. Если мы хотим выбрать только определенные столбцы, то мы либо должны явно перечислить их имена (единственный способ, доступный в SQL), либо задать начальное имя столбца и количество столбцов в выборке (или же конечное значение имени столбца). Но вот нельзя выбрать и по именам, и по диапазону. В наших же данных, кроме чудесного столбца data с таймстампом, было еще и несколько обычных столбцов. Примерно так:
columnA, columnB, data:timestamp1, data:timestamp2
Конечно, можно выбирать все колонки и выбирать данные с последним таймстампом на клиенте. Но очень не хотелось гонять лишние данные по сети. Поэтому мы применили грязный хак военную хитрость. Помните, что колонки отсортированы? А количество "обычных" колонок - известно. А из таймстампов нужен наибольший. Так давайте назовем колонки так, чтобы они выстроились в нужном порядке:
_data:timestamp1, _data:timestamp2, columnA, columnB
А теперь выбираем нужное количество колонок (в данном примере - три), в обратном порядке (т.е. начиная с конца). И получаем только те данные, что нужно.

Столько мучений. Зачем же использовать Кассандру?
Ну вот, например, зачем. У нас все выглядит примерно так:
Кассандра размазана между четырьмя датацентрами. Данные реплицированы тоже четыре раза, т.е. в каждом датацентре есть своя копия. В каждом датацентре есть API, причем API без состояния, все запросы отражаются в Кассандре. А вот клиенты выбирают API для запросов в случайном порядке. Нет привязки клиентов или датацентров по каким-либо признакам вроде географического расположения. И получается совершенно честный мультимастер. Реликация и контроль целостности полностью обеспечиваются Кассандрой. Я не знаю, как такое сделать с каким-либо SQL или даже с Монгой (где имеются выделенные мастеры, принимающие все запросы записи).
Кассандра легко разворачивается, довольно быстра, надежна и практична. Пользуйтесь.

2013-11-06

Об идеальном гаджете

Выступление Дэна Ромеску на GDG DevFest Omsk 2013 разбередило мои давнишние фрустрации на тему идеального гаджета. Дэн пытался показать, что таскать на себе акселерометр, умночасы, парочку умнотелефонов, а также иметь дома умновесы - это нормально. Удивительно, что гуглоочки на DevFest принес не он :)
Сам я тоже обвешан гаджетами. В кармане всегда - Galaxy Nexus (4.65", 316 PPI). В рюкзаке - Asus Transformer TF101 (10.1", 149 PPI) и Asus Zenbook (13.3", 138 DPI). К чему привожу тут цифры? Размер имеет значение. Разрешение тоже, но размер - заметнее :) Способ ввода тоже важен.
Телефон хорош для СМСок, Твиттера и тому подобного общения максимум на пару предложений. На нем можно смотреть ленты того же Твиттера и Гуглоплюса. На нем можно оперативно воспользоваться переводчиком/словарем или картами. Но что-то более серьезное требует большего экрана.
Планшет (десятидюймовый) хорош для чтения. Статей, новостей, книг. Хорош для рисования. Я, конечно, не художник, но набросать диаграммку или какую-нибудь белиберду в момент великой скуки - можно. Хоть Трансформер у меня и с клавиатурным доком, самой клавиатурой я пользуюсь редко (в основном пользую как вторую батарейку). И причин неиспользования клавиатуры две. Первая - Андроид. Это замечательная по многим параметрам операционная система. Но для создания контента она не приспособлена. Просто сравните функционал того же Гмейла и Гуглодрайва в браузере и в андроидном приложении. В браузере можно все. В андроидном клиенте комфортно можно только смотреть. Не хватает многооконности, чтобы копипастить туда-обратно. Банально нет серьезных IDE (для меня создавать - это в первую очередь код). Вторая причина - размер экрана. 10 дюймов - это очень мало, чтобы вместить даже все панельки серьезного текстового редактора (не говоря уж об IDE).
Размер имеет значение. Поэтому у меня есть тринадцатидюймовый ноутбук. До этого был чудесный семнадцатидюймовый HP с матовым экраном. Чудесный экран. Но совершенно нетранспортабельный ноутбук. Экспериментально пришел к выводу, что 13 дюймов - это конфортный минимум для создания контента. На FullHD в 13 дюймов прекрасно умещается IDEA. И сенсорный экран на ноутбуке мне не нужен. Руки устанут тянутся и тыкать в него. Проверено на Трансформере, с подключенным доком удобнее пользоваться тачпадом, чем тыкать в экран.

Итак, размер имеет значение. А значит, идеальный гаджет должен быть с изменяемой геометрией экрана. Да, достаю из кармана маленький совочек. Стало тесно? Тянем за уголки и растягиваем. До лопаты. Как сейчас окно на десктопе. Растягиваем до семи, десяти, пятнадцати дюймов. Хотим посмотреть кино с друзьями - растягиваем до метра, вешаем на стену и смотрим. Фантастика? А то!
Пожалуй, первый подход на эту тему нам продемонстрировал Asus. Со своим PadPhone. Пожалуй, это все, что можно сделать на сегодняшнем уровне технологии. Хотите телефон - вот вам телефон. Хотите планшет - пристегните телефон к большому экрану. Хотите ноутбук - пристегните клавиатуру. Все хорошо. Но мы ограничены десятью дюймами. Дополнительные экраны и клавиатуры надо еще с собой таскать. Ну и не так это элегантно, пристегнуть вместо растянуть ;)
Второй подход обещают гибкие дисплеи. Моей фантазии пока хватает только на большой дисплей, свернутый в трубочку и помещенный в телефон. Жду таких моделей в ближайшие пару лет. Представляете, достаете телефон и вытягиваете из него семидюймовый экран для семейной демонстрации котофоточек.

Но с другой стороны. А так ли плохо таскать с собой три разных устройства вместо одного универсального? Меня лично это пока не напрягает. Напрягает другое. Отсутствие синхронизации.
Я хочу, начав редактирование какого-нибудь документа на ноутбуке, пойти куда-нибудь в переговорку и открыть этот же документ, на том же месте, на планшете. А потом "смахнуть" на проектор, чтобы показать коллегам. Или перечитать на телефоне, сидя в маршрутке. Да, Гуглодрайв подошел к задаче как никогда близко, но не хватает прозрачности. Ну хотя бы так, как синхронизируются открытые вкладки в Хроме. И даже еще более прозрачно. Чтобы я не думал, что нужно сделать, чтобы продолжить работу на другом устройстве. Чтобы работа просто ужа была там.
Если я правильно понял посыл Марка Шаттлворта, именно Каноникал хочет добавить прозрачную унификацию в работе на различных устройствах. Так что с нетерпением жду Убунту на десктопе (собственно, уже пользуюсь), планшете и телефоне. Вдруг хоть мечта о прозрачной синхронизации исполнится?