Previous Entry Share
2016-01

on software

Ищу best practices по версионированию и интеропу версий.

Особенно интересны чеклисты или какие-то basics guides, чтобы не писать их самому.

Примеры вопросов, на которые оно должно отвечать:
- Есть кластер, хочется обновлять ноды постепенно, и не тушить при этом кластер. Как облегчить оверхед / сделать более надежные тесты / чтобы ничего не ломалось при апгрейде протокола?
- Есть БД, к ней коннектятся клиенты разных версий. Например, мобильные приложения, которые небыстро апдейтятся. Как правильно это всё поддерживать?
- Фронтенд ходит к нескольким backend services, которые пишутся разными командами. Хочется максимально отвязать друг от друга release cycles этих сервисов, ну и не ломать ничего, понятно.

Если говорить количественно, то хочется сократить
- время релиза новой фичи (вызывающей изменения в протоколах/схемах/форматах)
- время на согласование протоколов (чем больше можно автоматизировать/автоматически проверить - тем лучше)
- количество багов протекающих в продакшен
- время диагностики бага

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

This entry was originally posted at http://wizzard.dreamwidth.org/479299.html. It has comment count unavailable comments. Please comment there using OpenID.

  • 1
swamp_agr January 18th, 11:28
1. Не хочется. Надо планировать окно и гасить кластер. Обновить ноды и поднять. Иметь поднятую копию приложению и все скрипты миграции данных за период обновления, чтобы их перенести в момент Х.
2. Держать разные серверы БД для разных версий приложения. Минимизировать рассинхронизацию.
3. Консолидированная шина, через которую ходят все клиенты. Не позволять им стучаться на разные endpoint-ы.

wizzard0 January 20th, 7:32
1) См. ниже http://www.livejournal.com/manage/subscriptions/comments.bml?journal=wizzard0&talkid=6171126
2) Разные серверы БД кстати частично тоже уже да, и частично решается тем, что квери тупо гоняются по разным версиям и джойнятся. Машин только становится нужно до чертиков...

vit_r January 18th, 11:30
Какие могут быть "тупые и надежные гайды", если это всё зависит от процессов и опыта? Тем более, требование противоречивые. И времени на согласование протоколов жалко, и ошибки, возникшие от их несогласования искать не хочется.

Тут или серьёзно разрабатывается протокол, а потом все добавления идут просто дополнениями, игнорируемыми старой версией, или опять пляски с бубнами.

Для того, чтобы код сливался без проблем, нужны очень жёсткие правила кодирования, вплоть до того, сколько пробелов где отступом ставить. (Хотя, дурное форматирование и глупые правила, в свою очередь, будут множить ошибки, так что это тоже место очень серьёзных вложений и планирования)

wizzard0 January 20th, 7:27
Зависит, конечно. Только вот у Боинга во внутренней документации по кодописанию почему-то гайды есть, хоть это и тоже зависит от процессов и опыта.

vit_r January 20th, 7:47
Там стандартизация по этим параметрам.

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

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

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

wizzard0 January 20th, 7:38
И это. Понятно, что требования конфликтят, как и везде, впрочем.

Процессы мы и так постепенно ставим. Но люди людьми, а хочется еще и автоматизации. Есть возможность втащить в любой элемент продакшена любую rocket science фигню. Автопроверки, автотесты, то-се. Хоть Хаскел, хоть Агду, хоть что. Хоть нейросетями код генерить. Где оно, когда оно все так надо-то? )

vit_r January 20th, 8:04
Практически всегда для автоматизации я начинаю с формализации процессов на бумаге. Потом, когда понятно, как оно работает, где узкие места, и что ещё нужно, перехожу на уровень Эксела. А уже когда видны потребности, возможности и потенциалы, смотрю, что производит индустрия и какие идеи, которые можно приспособить под наши задачи, есть у народа. (Часто оказывается, что или нет ничего, или есть не то, что нужно)

Практически во всех случаях, когда в работающие процессы пытались слепо впихнуть очередной пучёк best practices, результаты были от печальных до плачевных.


Edited at 2017-01-20 08:05 am (UTC)

wizzard0 January 20th, 21:10
Бумажки есть, дыа. Ну и понятно что впихивать бездумно никто ничего не будет, не тот масштаб конторы чтобы это прокатило ггг

vit_r January 20th, 21:12
На бумаге сложно не сделать, а соптимизировать. Потому что все траблы сразу вылезают, а как их решать - не понятно.

metaclass January 18th, 11:56
Микросервисы это какие-то, причем в каждом апи придется добавить поле "версия апи" и поддерживать +-N версий. Если не поддерживается - жаловаться в мониторинг и дальше пусть умные разбираются.
Кроме тестов толком ничего не спасет. В стиле "выкатываем на 10% запросов новую версию и смотрим, как быстро она умрет". Умершие запросы перебалансируются на еще живые сервисы, мертвый откатывается до разборок и все в том же духе.
Короче, ломать все, пусть у роботов голова думает, они железные.

anonim_legion January 18th, 14:44
Вот, а говорили что ЖЖ не нужен.

metaclass January 18th, 15:28
Комментировать и читать жыж не запрещено:)

wizzard0 January 20th, 7:33
Да, в чем-то микросервисы. См. другие комменты.
Тесты да, вопрос как их правильно и быстро писать, а не ходить по граблям?

metaclass January 20th, 8:58
Я полноценно еще не пробовал делать такую архитектуру.
Но когда меня убедят что надо что-то улучшать когда и так все работает и дадут за это денег - все будет поверх MQ, API будет версионироваться - т.е. сервисы будут выгребать для себя запросы, вникать, могут ли они их обработать и обрабатывать (либо возвращать обратно, если не могут), инстансов сервисов будет более одного на каждую версию, запускаться это все будет в докере или systemd-nspawn и подниматься какой-нибудь системой управления конфигурацией и деплоится автоматом из CI/CD, запросы которые никто не смог обработать более чем за разумный интервал времени будут идти в спец-очередь, появление мессаг в которой будет вызывать вой мониторинга.

Причем mq я хочу втащить вообще на все уровни, начиная от приложений и веб-морд и заканчивая работой внутри процессов, потому что так асинхронность и изоляцию делать удобнее.

Данные, которым нужна строгая консистентность придется хранить в реляционной БД, сидящей за одним из микросервисов, причем что тут хорошо - что можно, в теории подписаться на события несколькими БД - основной, резервной, тестовой, девелоперской, итп. С полноценными транзакциями в масштабах всей системы, по моему, все плохо - если нужна транзакционность, то менеджер транзакций становится точкой отказа и вообще оно снижает throughput.


metaclass January 20th, 9:02
Еще одна мутная вещь, которую я у себя вынужден делать - это поддержка разных версий типов данных.
Т.е. если появляется новое поле, то старые сервисы, которые его не знают должны через себя его пропускать прозрачно, не теряя, а новые сервисы, получая данные от старых - должны в это поле, данные от которого отсутствуют, ставить какое-то разумное значение по умолчанию. И для всего этого нужен само собой аудит - чтобы потом можно было найти эти записи и исправить, если понадобится.


wizzard0 January 20th, 21:11
> поддержка разных версий типов данных.
У нас это уже есть, увы. Но адхок и авгиевы конюшни

alexeyk77 January 18th, 12:33
Все зависит от приложения. В банке как правило для центральных учетных системы плавная миграция не проканывает, система останавливаеся полностью и накатывается новая версия. Перед этим проводится тестирование на отдельной тестовой системе.
В одной из достаточно сложных SIEM-систем, которую эксплуатирую поддерживается разные версии API, т.е. реализован вариант metaclass-а. Даже процессинговые системы иногда приходится полностью останавливать и тогда все карточки не работают.
Но вообще конечно же использование MQ-шины как API-прослойки для передачи сообщений между разными системами сильно облегчает поддержку систем состоящей из разношерстных решений.

wizzard0 January 20th, 7:29
MQ уже есть, и да, помогает. Центральных учетных данных нет, их можно апгрейдить по частям. Т.е. доступ к данным оч хорошо разграничен. А вот условие сосуществования нескольких версий нод - обязательное.

veremeenko_alex January 18th, 13:22
Вот ты и напишешь :)!

wizzard0 January 20th, 7:35
))

zealer January 18th, 18:40
eBay хранит все предыдущие версии АПИ..

В одной из предыдущих компаний мы поддерживали две версии АПИ с помощью хедера, который потом от рутера посылал на нужный сервер. Старая версия оставалась запущенной неделю. Для более старой версии просило обновить клиента на странице (и в телефоне).

vissarion January 19th, 6:48
SalesForce тоже
версию надо сразу заносить в урл API даже если "приложение простое, ничего никогда не будет меняться"
типа mystartup.com/api/v1/rest/...

jakobz January 19th, 11:29
Фейсбук вон graphql продвигает, он в частности, помогает с версионностью. Т.е. я думаю что правильный путь - строить более high-level API: не REST с методами на каждый чих, а что-то типа Query на входе, более-менее generic-формат на выходе.

Хотя сам GraphQL мне не нравится, мне кажется там опять наступают в ловушку Object-Relational Mismatch. С первого взгляда удобно - и запрос, и ответ - дерево, для UI попроще. Но когда ты с этим начинаешь хитрее работать - менять данные на клиенте, или делать посложнее запросы - все равно всё возвращается к реляционной модели.

Мне кажется что у Datomic наиболее прямая модель данных. С запросами я только пока не разобрался, непонятно можно ли там граф достать, или такой же треш как в SQL.

wizzard0 January 20th, 7:35
GraphQL совсем не в кассу, но схемохранилище, похоже, будем заводить... См. другие комменты.

А, и у нас к счастью нету обьектов ;)

Датомик совсем не пойдет, требования по перформансу очень жесткие, все кастомное.

jakobz January 20th, 15:49
Мы вот эту хрень у себя вводим https://www.confluent.io/ Там Kafka как MQ + AVRO для сериализации + SchemaRegistry. Последняя - хранит AVRO-схемы, и контроллирует их совместимость. Там вообще хитрожопо выходит: бинарники пролучаются без оверхеда на схему вообще, ну кроме int-а со schemaID вначале каждого сообщения. Т.е. 4 int-а займут 16 байт + 4 на заголовок. Но при этом формат сообщений может меняться, и десериализатор глядя в две схемы - свою и сообщения, может читать совместимые сообщения.

Пока всё неплохо работает, тулинг только у этой кафки - не сравним со всякими Rabbit MQ. Но, думаю, оно подтянется потихоньку, т.к. хреновина популярная.

wizzard0 January 20th, 21:13
Любопытно, я гляну. Спасибо. Про две схемы думал, но было лень продумывать до конца.

ady_1981 January 19th, 20:01
В мире Erlang + OTP это все решено :).

wizzard0 January 20th, 7:26
Гайды, ссылки на гайды и дизайн-доки где?

vit_r January 20th, 7:52
Предётся писать свой Эрланг :-)

ady_1981 January 20th, 19:57
>Есть кластер, хочется обновлять ноды постепенно, и не тушить при этом кластер
Если "eventual consistency" достаточно, то есть Riak Core, у которого "из коробки" какие-то плюшки есть.
Для 2-3 нод Erlang из коробки дает механизмы failover-takeover.
При обновлении gen_server в OTP Erlang есть специальный callback метод (code_change), который отвечает за обновление версии кода.
>Например, мобильные приложения, которые небыстро апдейтятся. Как правильно это всё поддерживать?
Общее правило: надо одновременно иметь обе версии протокола сервера.

Есть ссылки на книги по Erlang:
https://yadi.sk/i/KqGdI0fe3A4N5e
https://yadi.sk/d/ozejshMr3A4SDW

Edited at 2017-01-20 08:01 pm (UTC)

egorfine January 24th, 23:30
сонные мысли

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

Кроме того, во многие протоколы я с самого начала закладываю такие вещи:

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

* сообщение для человека: ZZZZ. Это в user-facing API полезно, чтобы пушить юзеру кастомные сообщения. Самый популярный use case: приходит древнючая версия приложения, API которой уже давно нет. Ему вместе с "подожди Infinity секунд" пихаем строчку "обнови софт, дурилка".

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

var resultData = Object.deepCopy(template);
Object.keys(incomingStats).forEach(key => { resultData[key] = incomingStats[key]; });
// упрощенно, конечно

Или, скажем, движение данных в обратном порядке: мы воркер, регулярно получаем апдейт данных с центрального сервера. Если мы смогли распарсить - окей, считаем транзакцию успешной, работаем дальше. Если не смогли - пользуемся предыдущей версией данных, работаем дальше. Если не смогли скачать свежую версию за время X - вот только тогда останавливаемся. Это позволяет в кластере одним быстрым движением обновлять центральные сервера с источником данных, в то время как сервера-воркеры продолжают работать независимо.

А вообще есть простая экономика. Что дороже:

1. простой в несколько минут на время апгрейда всего софта на кластере + риски неудачно накатиться ИЛИ
2. стоимость разработки совместимости версий + риски, связанные с такой сложностью кода?

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

А вообще ты задал слишком широкий вопрос. На него не может быть конкретного ответа кроме потока сознания.

  • 1
?

Log in