MCP

суббота, 31 декабря 2016 г.

Итоги моего 2016 года

Я иногда подвожу итоги года в этом блоге, когда есть о чём сказать, а также есть желание и время. В прошлом году вроде бы особо ничего не добился, но есть немного времени, поэтому можно и написать.
Т.к. блог фактически технический, то напишу только про профессиональные результаты.

На работе проект, в котором я был архитектором благополучно завершился (ну и будет продолжаться дальше). Архитектура мне до сих пор нравится, несмотря на большое количество частей, мы делали относительно безумные вещи, но делали их сами и результат получался гораздо интереснее, чем война со сторонними библиотеками.
Также, наконец-то добрался до .NET Core и PostgreSQL. По обоим проектам чувства весьма смешанные. Надо бы написать отдельные посты, про это, но пока лень (хотя есть небольшой про Core, но надо подробнее. Ну и в следующем году, будет ещё одна волшебная платформа (но тут не я автор, а просто участник, посмотрим что выйдет). Также выиграл один из хакатонов на работе и успешно внедрил результат в компании. Горжусь.

Вообще, на работе всё было весьма и весьма неплохо. У нас собралась отличная команда, которая может делать любые безумные вещи, и пусть другие команды нам завидуют!

Также в этом году, я впервые за долгое время не поехал на DevCon, какой-то он унылый и неправильный был в этом году. Зато съездил на микротиковский MUM, понял, что админы странные существа, и некоторые "проблемы", которые они решают, для программистов проблемы только в выяснении того, что значат всякие аббревиатуры, а сами задачи простейшие, но админы героически их решают.

В свободное от работы время приобщаюсь к Github'у и Nuget'у. В феврале сделал небольшую, но очень быструю библиотеку для копирования объектов: DeepCloner, потом разошёлся и сделал одну из самых быстрых (или самую быструю) библиотеку для вычисления CRC32: Crc32.NET. Вообще, началось с того, что мне понадобилась реализация CRC32C, а т.к. автор что-то медленно правил свою (версию для плюсов он уже обновил, а вот .NET никак не может, хотя спустя полгода таки принял мой pull request). Ну и в общем, раз я взялся за CRC32C, то надо было поглядеть на обычный CRC32. На нюгете и гитхабе проекты оказались неушстрыми и полузаброшенными. Пришлось сделать свой.
Ну и в конце-концов, я допилил свой архиватор до рабочего состояния, называется он красиво: Blazer. В прицнипе, нужно его повылизывать и попилить, но в целом там уже достаточно клёвых фич, типа сжатия с шаблоном. Ну и большинство фич сосредоточено в библиотеке, а не в консольном экзешнике.

Ну и ещё научился находить плавающие баги в коде, сидя в гостинице в Москве с ноутбуком. Хотя фактически несколько часов искал их с помощью головы. Оригинальные впечатления.

В общем, желаю всем читающим меня отличного Нового Года, покорения новых профессиональных вершин, а также чистого, гладкого и красивого кода.

четверг, 15 декабря 2016 г.

Краткий анализ .NET Core/Standard/Framework

Тут в очередной раз разбирался с тем, что в Microsoft нагородили с .NET, пока окончательно не понял, но в целом, чтобы не пересказывать основные статьи про всё это дело, просто сообщу, то что я понял:

  • Есть .NET Core, это специальный кроссплатформенный фреймворк
  • Есть .NET Framework, это классический .NET, работающий под винду
  • Есть .NET Standard, это набор API, который гарантированно будет работать в .NET Core и .NET Framework (ну и всяких ксамаринах)
  • Код, изначально написанный на .NET Framework, не будет работать под .NET Core
  • Если есть желание писать под .NET Standard, чтобы работало под большим .NET Framework, то лучше не делать так. Ибо работать будет, но плохо.
  • Потому что вся магия в том, что API похожее, но разное. 
  • Другими словами, версия под .NET Framework будет использовать очень похожий, но отдельный набор API. Если он весь скрыт внутри. то ничего страшного, никто не увидит. Если же вылезает наружу, могут быть неприятные последствия.
  • Ещё раз уточню, что если написано, что Standard 1.2=Framework 4.5.1, то это не значит, что код, написанный под 4.5.1 будет работать на уровне 1.2. Это просто значит, что если написать код, под 1.2, то его с помощью бубна можно заставить работать на 4.5.1. При этом набор API в 1.2 особо нигде не расписан, например, там нет криптографии. Странно, но вот так.
  • Судя по всему, самая популярная версия .NET Standard — 1.3, что в ней такого особого, пока не разобрался
Т.е. в целом ситуация складывается странная. Если хочется разрабатывать универсальные вещи, то лучше тупо не думать, а разрабатывать под .NET Core, забив на .NET Framework полностью. Потому что поставить .NET Core не сложно, а существущий код под .NET Framework всё равно придётся переделывать, так что, смысла во взрослом фреймворке на мой взгляд немного.

PS: Возможно, после дальнейшего знакомства со всем этим делом, моё мнение изменится, тогда обновлю пост. Но пока есть ощущение ужасной неразберихи и путаницы в API, чего стоит только набор версий фреймворка 4.5, 4.5.1, 4.6, 4.6.1, 4.6.2 — надо долго гуглить, чтобы найти отличия, но при этом для каждого из них, есть своя версия .NET Standard, но, поскольку, скоро будет .NET Standard 2.0, использовать .NET Standard 1.5 и 1.6, не рекомендуется из-за проблем с совместимостью. Ещё раз, стандартная версия фреймворка, сделанная для совместимости, будет несовместима сама с собой. А нам с этим жить...

воскресенье, 6 ноября 2016 г.

Задача на вероятность

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

Собственно, сама задача. Сформулирую её приближенно к реальности, чтобы было понятнее.

У нас есть условный Киндер-Сюрприз и мы знаем, что в нём есть n различных вариантов игрушек, мы купили k киндеров (k ≥ n). Какая вероятность того, что мы соберём всю коллекцию игрушек? 

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

понедельник, 31 октября 2016 г.

История одной грабли

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

Итак, поехали.

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

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

Основные сервера, которые копируют данные на него видят его, и пытаются скопировать, по факту сделали снапшоты от текущей версии и пытаются передать разницу. Разница никак не передаётся. Разница растёт. Растёт эта разница долго и упорно (да, по закону подлости это всё случилось на выходные) и в понедельник в 5 утра на одном из серверов тупо кончается место. Занято всё снапшотами. Место кончилось, все виртуалки ушли на аварийную паузу. И всё сломалось...

Т.е. все данные есть, ничего не потеряно, но ничего не работает. Починилось это банально — выдёргиванием проблемного диска (ну и заменой его). Рейд развалился, сервер забегал, снапшоты долились.

А теперь проблема в целом: грабли с диском на неважном сервере вывели из строя основной. Т.е. по факту, следить надо за такими фантастическими вещами. Такие вот пироги с котятами.

воскресенье, 28 августа 2016 г.

Моя месть node_modules

К node.js у меня странные чувства. С одной стороны, это простая и очень быстрая среда для разработки приложений, которая работает на всём, что шевелится. С другой, большинство библиотек написаны на таком уровне, что программисты на C# и прочих серьёзных языках хренеют от ужаса. Впрочем, об этом другой раз.

Один из больших, бесящих пунктов у ноды, это папка node_modules. С зависимостями зависимостей и зависимостями зависимостей зависимостей. И эти файлы ещё любят вылезать за 255 символов, что приводит винду в истерику (да, в npm3 это таки пофиксили). И кроме всего прочего, этот миллиард мелких файлов ещё дофига копируется. А копируется он часто, обычно каждый билд. Т.е. тупое перемалывание толпы файлов.

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

В общем, нарисовал я проект с гениальным называнием nmisf (node modules in single file). Не буду переписывать его документацию, просто скажу, что вам надо один раз запустить создание бандла и папка node_modules превратится в один файл, в котором будут только нужные файлы, да ещё и без дублей (а с учётом зависимостей зависимостей зависимостей, это актуально). Ну и рядышком ещё один файл для индекса. В итоге, два файла вместо толпы. Что положительно сказывается на времени копирования, ну и количестве хлама на диске.

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

В общем, вэлкам на тестирование данной библиотеки. На мой взгляд, весьма интересная вещь, и стоит её попробовать. В понедельник заюзаю на боевом проекте (пока никто не видит) и посмотрю на время билдов и запуска. Думаю, результат всех порадует.

воскресенье, 7 августа 2016 г.

Мегафичи моего архиватора

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

Полное шифрование без раскрытия информации

Т.е. вообще ничего не говорим. Даже что это архив нужного типа. Безопасность и красота. Есть рандомный набор байт, а является ли он архивом, или ещё чем-то — науке неизвестно. При этом оставлено обычное шифрование, которое можно оптимизировать в плане скорости, но оно раскрывает базовую информацию об архиве, что для обычных ситуаций не проблема, а наоборот удобство, но ведь надо периодически делать фичи и для параноиков.  

Управляющие блоки 

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

Восстановление данных (не сделано)

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


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

воскресенье, 29 мая 2016 г.

Проблемы архиваторов, о которых никто не говорит

Я тут давно начал "писать" архиватор. Ну, т.е. как начал, сделал самое интересное, а допинать до конца терпения не хватило. Точнее почти хватило, но меня не устроили результаты, а, как известно, первая заповедь перфекциониста-прокрастинатора: лучше сделать хорошо, но никогда, чем плохо, но сейчас!

В общем, я полтора года не трогал свой архиватор, а тут решил, что всё-таки стоит пожертвовать 0.2% степени сжатия и 30МБ/c скорости, и выбрать простую и дубовую реализацию, так что. архиватор почти дописан, осталось сделать последний рывок и вычистить все баги, и добавить лоска. Впрочем, про сам мой алгоритм расскажу как-нибудь в другой раз.
Для начала расскажу про одну небольшую проблему, из-за которой, я начал писать архиватор, а не тупо взял готовый. Проблема весьма специфическая, но если вы в неё встрянете, то будет плохо, очень плохо.

В общем, вкратце, проблема называется Flush. Т.е. нормальная поддержка архиватором данной команды. Что это значит? А то, что большинство реализаций архиваторов внутри работают с блоками определённой длины (самой любимой) независимо! Т.е. команда Flush приводит к тому, что внутренний буфер отправляется в нижележащий стрим. И если в буфере много данных, то и проблем нет, а если мало, то результат становится очень неприятным с точки зрения качества сжатия.

Но ситуация может быть ещё хуже, архиваторы могут тупо проглатывать команду Flush (передаю привает реализации GZip в .NET, да она проглатывает Flush)! Что это значит? Это значит, что в некоторых задачах вы в принципе не можете использовать данную реализацию.

Собственно, про задачи-то я и забыл рассказать. Представьте, у вас есть TCP канал, в котором вы обмениваетесь сообщениями, например JSON'ом (так сейчас модно). Сообщения вам надо проталкивать на другую сторону и очень хочется их сжимать. А поскольку сообщения зависимые и похожие, то и сжимать зависимо. Проталкивать их надо командой Flush, что очевидно, а сообщения у вас небольшие. Что получается? Смотрите картинку:

В качестве тестовых данных, дамп википедии в 100 Мб (один из стандартных шаблонов для сжатия). По оси X - размер блока, который флушится, по оси Y - степень сжатия.
Что видно? ЧТо на блоке в 16 байт накладные расходны на заголовок превысили все ожидания (только GZip рулит за счёт мелкого заголовка). В дальнейшем, всё становится лучше, но до блока в 16Кб счастья особого нет. Т.е. сжимается, что уже неплохо, но могло быть и лучше.

Вот, если для примера взять бекап какой-нить базы (какой уже не помню, но это настоящая продуктовая база была). Особенность бекапа базы — очень выражена периодичность на 64Кб, да и вообще много похожести. Хотя флушить бекап базы не очень логично, но для примера пойдёт:

Результаты те же, только более явно выражены. И эта явность как раз и намекет, что 64Кб — это таки нашё всё. А лучше больше. Тот же график, но у него подрезано начала, чтобы был лучше виден масштаб:

Тут видно, что LZ4 за счёт максимального блока в 262Кб (по дефолту у данной реализации), вполне неплохо идёт дальше, но такой размер блока уже как-то лучше подходит для статичных данных, а там можно и более серьёзные аргументы, начинающиеся на цифру 7 применить.

В общем, цель моего архиватора: сделать так, чтобы Flush'и не оказывали серьёзного влияния, чтобы вы могли использовать архиватор не особо парясь. И как видите, всё вроде бы получается (мой - красненький). 1024 байта уже позволяют его весьма эффективно использовать.

Правда на блок уходит 8 лишних байт... и это меня бесит... Ждите следующий пост через пару лет...