воскресенье, 6 апреля 2014 г.

Полезное видео о серверном железе

Наткнулся на прошлогоднюю серию видеороликов  о серверном железе. Информация на текущий момент актуальная. Очень познавательно, если вы не каждый день заниматься выбором серверный железа.


Серверы: как правильно выбрать HDD для сервера/RAID
Тут объясняют в том числе и проблемы с RAID-5 (и не только 5) на настольных SATA дисках и зачем нужны RAID версии SATA дисков.



Серверы: построение RAID-массивов для серверов

SAS JBOD



Серверы: обзор серверных чипсетов Intel
Интересно про память для многопроцессорных материнских плат. Как-то не задумывался раньше о последствиях  внедрения контролера памяти в процессор.


Серверы: обзор и применение серверных сетевых плат
Тут для не посвященных в серверное железо и аппаратное обеспечение виртуализации много интересного.



вторник, 29 мая 2012 г.

Erlang: первые шаги с Cassandra

Читая обзоры и  сравнения NoSQL решений, я нередко натыкался на мнение о том, что у Cassandra проблемы с документацией.  Пока я знакомился с архитектурой и  CLI-командами системы, проблема с документаций казалась устаревшей. Но  первая же попытка что-то сделать в Erlang  сразу уперлась в долгие часы гугления. По сему, для облегчения своей, и не только, дальнейшей трудовой деятельности выкладываю простенький "how to" по осуществлению базовых операций с Cassandra в Erlang.

1. Thrift 
Для работы с Cassandra в Erlang нужен клиент Thrift и для него сервис Cassandra.
Получить  клиент Thrift для Erlang можно так:
svn co http://svn.apache.org/repos/asf/thrift/trunk/ thrift
Далее скачиваем утилиту  thrift отсюда: http://thrift.apache.org/  и генерируем   сервис Сassandra (windows):
thrift-0.8.0.exe  --gen erl interface/cassandra.thrift
где interface/cassandra.thrift - thrift-файл из дистрибутива Cassandra.
Теперь у нас есть все, что необходимо для работы с Cassandra.

2. Подключение и запись

-include("cassandra_thrift.hrl").
-include("cassandra_types.hrl").
       *       *         *
{ok, C}=thrift_client_util:new("localhost", 9160, cassandra_thrift,[{framed, true}]).
{C1, _} = thrift_client:call(C, 'set_keyspace', ["my_keyspace"]).
thrift_client:call(C1,'insert', ["00000001", #columnParent{column_family="mq"}, #column{name="1234567",value="Hello World !", timestamp=0}, ?cassandra_ConsistencyLevel_ONE ]).



Erlang-клиент Thrift после каждой операции возвращает подключение. Это удобно для рекурсивных вызовов Erlang. Мудрые люди не рекомендуют использовать подключение однажды полученное   thrift_client_util:new(). Почему смотрите здесь: http://stackoverflow.com/questions/10503907/cassandra-thrift-erlang-insert
Не рекомендую  передовать подключение между процессами.
Осталось напомнить, что подключение необходимо закрывать, либо завершать процесс Erlang. Если отрыть слишком много подключений - Erlang  вернет ошибку 'system_limit', что в данном случае свидетельствует об исчерпании лимита системных handles. Количество handles  в windows  можно посмотреть с помощью Process Explorer.

Запись в super column:

{C1, _} =  thrift_client:call(Connect,'insert', [Mid, #columnParent{column_family=" cf_1 ", super_column = "col_A "}, #column{name="S",value= integer_to_list(MState), timestamp=0}, ?cassandra_ConsistencyLevel_ONE])

3. Чтение
try thrift_client:call(Connect,'get',[Key,  #columnPath{column_family="cf_1", super_column="col_A", column = "r"}, ?cassandra_ConsistencyLevel_ONE])  of
{_C1,{ok,Val}} -> dosome()
catch { _, {exception, {notFoundException} = Err}} -> doerr()
end. 
Как видите, спецификация колонки при чтении и записи различается. И Thrift-клиент  использует исключения (с Erlang я уже почти забыл что это такое).



4. Документация

суббота, 5 ноября 2011 г.

Mnesia - изменение таблицы

Erlang - интересная вещь. Обещает много полезного и быстрого. Например,  база данных Mnesia. В Mnesia много всего полезного: хранит данные в формате Erlang, репликация, транзакции, скорость и т.д. и т.п. Но есть одна проблемка - как изменить структур таблицы?

При создании таблицы необходимо указать список полей. Не будем сейчас углубляться в  рекомендации использовать  запись (record) одноименную с таблицей. Важно, что при изменении списка полей по предлагаемому варианту с простым  использованием  mnesia:transform_table (конвертирование полей и замена определения таблицы), похоже, придется останавливать работу системы, конвертировать, менять код приложения для работы с новым типом и запуск (тестирование).
Если посмотреть на тоже MS SQL то добавление поля - элементарная операция.
Для меня важно провести переход на новый тип без остановки всей системы. По крайнеё мере БД должна функционировать непрерывно.

Я исследовал чтение и запись в Mnesia. И вывод таков:
Mnesia жестко контролирует данные при записи. Записать можно только то что обявлено в структуре таблицы, иначе ошибка. Контролируется имя записи и количеств элементов -  т.е. ВСЕ.
Однако, при чтении  Mnesia отдает то, что было записано, не сверься с текущим сконфигурированным списком полей и названием записи. И список полей можно изменить и без фактической трансформации (mnesia:transform_table(Tab,ignore, Fildlist ).
Для примера создадим таблицу, запишем данные , а потом добавим новые поля:

-module(recordtest).
-compile(export_all).
-record(r1 ,{f1,f2}).
-record(r2 ,{f1,f2,f3,f4}).
dbtest()->
mnesia:create_table(r1,[{attributes, record_info(fields, r1)}]).
transform()->
mnesia:transform_table(r1,ignore,record_info(fields, r2), r2).


Испытаем:



14> mnesia:create_schema([node()]).
18> mnesia:start().
ok
19> recordtest:dbtest().
{atomic,ok}
20> mnesia:dirty_write(r1, {r1,l1,l2}).
ok
21> mnesia:dirty_write(r1, {r1,t1,t2,t3}).
** exception exit: {aborted,{bad_type,{r1,t1,t2,t3}}}
     in function  mnesia:abort/1
22> mnesia:dirty_write(r1, {r2,l1,l2}).   
** exception exit: {aborted,{bad_type,{r2,l1,l2}}}
     in function  mnesia:abort/1
23> mnesia:dirty_read(r1, l1).         
[{r1,l1,l2}]
24> mnesia:dirty_read(r1, l1).
[{r1,l1,l2}]

38> recordtest:transform().
{atomic,ok}
39> mnesia:dirty_read(r1, l1).   
[{r1,l1,l2}]
40> mnesia:dirty_write(r1, {r2,l1,l2}).
** exception exit: {aborted,{bad_type,{r2,l1,l2}}}
     in function  mnesia:abort/1
41> mnesia:dirty_write(r1, {r2,a1,a2,a3,a4}).
ok
49> mnesia:dirty_read(r1, l1).                
[{r1,l1,l2}]
50> mnesia:dirty_read(r1, a1).
[{r2,a1,a2,a3,a4}]

Теперь у нас в таблице записи обоих типов и мы их можем прочитать. Но если мы теперь попытаемся создать индекс на четвертом поле записи mnesia:add_table_index(r1,[l4]).
- мы получим фатальную ошибку, остановку mnesia и при следующем запуске этой базы снова произойдет ошибка. Поможет только удаление файла r1.dcm -  только после этого mnesia запустится. Необходимо будет еще удлаить определение таблицы из схемы.
Следовательно, пока все записи в таблицы не будут соответствовать спецификации индекса - ни в коем случае не индексировать.





пятница, 23 сентября 2011 г.

Проблемы SMPP-протокола


В мире сложно найти что-либо идеальное. Протокола SMPP  также не лишен некоторых изъянов. Опишу свои проблемы с этим протоколом. Надеюсь кому-то это поможет в принятии правильных решений.

Первый, самый проблемный недостаток: потеря message_id  при разрыве соединения. Страдают от этого  операции отправки (submit_sm и т.п.) для которых не успел прийти ответ. Протокол не содержит встроенных средств восстановления утерянных идентификаторов. Как следствие, когда придет статус сообщения, его не к чему привязать.  Более того, неизвестно принял ли это сообщение SMSC.
Если обмен осуществляется в синхронном режиме, то потеряется только одно сообщение. Но если работа производится в асинхронном режиме, тогда потери могут быть существенными.

Этот недостаток  SMPP, пожалуй, единственный нерешаемый средствами протокола из тех, которые я могу припомнить. Проблема, конечно, решается но не стандартизованными методами.

Остальные недостатки связаны с проблемами реализации. Их решении, как правило, заключается в достижении договоренности между SMSC и SMPP-клиентом и не выходит за рамки спецификации.

Второй недостаток, который мне сильно досаждает, связан с отчетам о доставке deliver_sm. В версии протокола 3.4 нет строго определения как  должна передаваться статусная информация. С одной стороны есть необязательная структура TLV  в которой в жестко типизированной форме передается message_state и сопутствующие параметры. Это вариант хорош, за исключением того, что SMSC  не сможет выслать в этой структуре какой-нибудь пространный комментарий. Но, повторюсь, нигде этот способ не указан как обязательный (MUST). Зато в приложении к протоколу приведен пример.  Подчеркиваю:   ПРИМЕР. Даже не рекомендация. Пример того, как SMSC может сообщать статусную информацию через (о боже, кто это придумал!!!) поле short_message. Т.е. в текстовом виде, странные сокращения, дикие форматы и т.д.
Вообще,  это ситуация выбора одного из возможных вариантов (MAY). По моим представления о реализации протоколов выбор одного из разрешенных протоколом варианта - прерогатива формирующей пакет стороны. В данном случае с пакетом отчета это SMSC. А принимающая сторона  обязана правильно обработать любой пакет соответствующий протоколу. Но суровая реальность говорит, что прав тот кто платит.  Подавляющее большинство SMPP-клиентов понимают только поле short_message.
Славо богу из спецификации пятой версии протокола убрали эту мину (приложение), но найдите-ка SMPP-клиентов пятой версии.

Третий недостаток - передача длинных сообщений. Спецификация ненавязчиво ссылается на стандарт [GSM 03.40] Technical Realisation of the Short Message Service Point to Point». Так ненавязчиво, что замечаешь ссылку только когда ищешь специально. Ссылка  на этот стандарт приведена в разделе 1.4 References спецификации версии 3.4.
 Вопрос: поле short_message предполагается протоколом использовать только в соответствии с GSM 03.40? GSM 03.40 предлагает длинный текст сообщения разбивается на серию конкатенированных sms, снабженных UDH-заголовками.  Спецификация SMPP неявно подстегивает на свободное использование - длина поля 254 октетов. Это две sms латиницей или почти четыре sms кириллицей.
Читаем внимательно спецификацию SMPP:

 4.4.1 “SUBMIT_SM” Syntax
   «... Up to 254 octets of short message user data. The exact physical limit for short_message size may vary according to the underlying network... »



Т.е. ограничения накладываются передающей сетью (underlying network). В нашем случае underlying network описывается GSM 03.40. Следовательно 140 байт данных. Зачем же такое длинное поле? Дело в том что использоваться может 7-bit кодировка, тогда символов уже 160. short_message это текстовое поле измеряющееся в октетах, а не бинарное в байтах. Возможно, создатели закладывались и на другие, более изощренные варианты.
Разработчик SMPP-клиента по понятным причинам хочет упростить себе задачу. И не стремится  на своей стороне связываться c конкатенированными SMS. В соответствии с протоколом  SMSC МОЖЕТ предоставлять такую услугу через поле message_payload (самостоятельно делить сообщение на смски, снабжать заголовками )  в форме по своему выбору (не стандартизировано). Но по протоколу не обязан. Да и применять это можно без страха только к обычным текстовым сообщениям. С точки зрения бизнеса вопрос тоже скользкий - как тарифицировать такие сообщения? А что если не все части сообщения имеют статус доставлено?

Четвертые недостаток связан с относительным форматом времени. Относительного чего отмерять указанное время? Когда нет тормозов ни на клиенте ни на SMSC и между ними хорошая связь, вопросов, как правило, не возникает. Но если в каком-то месте появляется задержка, то точки отсчета времени клиента и SMSC существенно расходятся.
Для  schedule_delivery_time в разделе 5.2.15 уточняется:
"..relative time from the current SMSC time at which delivery of this message will be attempted by the SMSC.."
Но проблему разных точек отсчета это не решает.

Литература
  • Short Message Peer to Peer Protocol Specification v3.4
  • [GSM 03.40] Technical Realisation of the Short Message Service Point to Point»


Статья и обсуждение  на habrahabr

Жду Ваши комментарии :)