tag:blogger.com,1999:blog-16237456934179959872024-02-03T00:29:22.153+03:00Бред программистаПрограммирование, извращения и всё остальноеforcehttp://www.blogger.com/profile/09205548202595699526noreply@blogger.comBlogger149125tag:blogger.com,1999:blog-1623745693417995987.post-64778603522261287432019-04-09T22:31:00.002+03:002019-04-09T22:31:42.403+03:00Странная реализация HttpClient в .NET Core<div dir="ltr" style="text-align: left;" trbidi="on">
Представьте что у вас есть приложение. Не очень маленькое, но и не гигантское, работает помаленьку развивается, и тут вы совершенно случайно замечаете в мониторинге, что один из сервисов жрёт 25% CPU. Хм... вроде многовато, но мало ли что. Может боты набежали на сайт и смотрят все странички, или пользователи ходят. Да и 25% немного, запас огромный (400% максимум). Но на всякий случай сервис перепускается и загрузка падает до 1%. Вы думаете — какой-то глюк, но через некоторое время загрузка процессора растёт опять.<br />
<br />
Вот с подобной ситуацией я столкнулся несколько недель назад. Есть какая-то утечка CPU (не памяти, а именно процессора), при этом ни истерики в логах ни каких-то явных знаков, указывающих на проблему. Приложение, хоть и не очень большое, но делает кучу разных вещей, отправляет письма, общается через собственную API-платформу, активно кеширует данные, связывается с другими частями и внешними сервисами, генерирует документы, в общем точек для анализа очень много. А утечка небольшая, несколько процентов в день, да и эти несколько процентов в моменте могут быть настоящей нагрузкой от живых пользователей. Т.е. даже не ясно как поступать с профилированием. Да и на деве это не повторяется.<br />
<br />
Когда я в очередной раз сдался, я не выдержал, и подцепился дебаггером к боевому серверу напрямую. И... ничего не понял, всё выглядит нормально. Отцепился — загрузка 0%. Тут у меня уже задёргался глаз. Через некоторое время повторил — да, подключение дебаггера явно лечит проблему, как и перезапуск приложения. Но это ведь не выход и не решение. А вдруг это какая-то проблема у нас, и если в этом приложении данную проблему можно замаскировать, то она может выстрелить где-то ещё. Так что пришлось разбираться дальше.<br />
<br />
Неожиданно удалось нагуглить <a href="https://github.com/Microsoft/ApplicationInsights-aspnetcore/issues/690">данный тред</a> на GitHub'е. Проблема у Microsoft'овской библиотеки и связана она с <a href="https://docs.microsoft.com/en-us/dotnet/api/system.net.http.httpclient?view=netframework-4.7.2">HttpClient</a>. Утверждалось, что нельзя его создавать на каждый HTTP запрос, а надо использовать статичный. Правда в документации есть пример использования:<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEj2h-t8zniY8bKUge7XffBsQwcfr6tsLZ7DP7GhjK0ebjm7-ip00q-7k30MoHIyCtLHf46TxWWXk4wh64hbB-nZNZaCLayfPdf_jTAozxVU94RPDv9wrd4QTcc1zNp_1ZrAKbNkqmQS7Ag/s1600/2019-04-09+22_15_20.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="503" data-original-width="940" height="341" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEj2h-t8zniY8bKUge7XffBsQwcfr6tsLZ7DP7GhjK0ebjm7-ip00q-7k30MoHIyCtLHf46TxWWXk4wh64hbB-nZNZaCLayfPdf_jTAozxVU94RPDv9wrd4QTcc1zNp_1ZrAKbNkqmQS7Ag/s640/2019-04-09+22_15_20.png" width="640" /></a></div>
<br />
В котором его заворачивают в using. Я попробовал покопать в данном направлении и выяснил первую забавную вещь: использование <b>статичного</b> HttpClient в .NET Core быстрее раз в 5, чем создавать его на каждый запрос. Но тут нюансы с самим временем запроса, но смысл в том, что уже есть причина использовать статичную версию. При этом под "большой" .NET разницы нет никакой.<br />
<br />
В общем, выложил новую версию приложения, но проблема осталась. (может я не туда копаю?). Попробовал переключиться с .NET Core 2.1 на 2.0 — проблема ушла. Т.е. проблема есть только под core, под Linux и версия должна быть больше чем 2.0 (более свежие не проверял). Читаем документацию и видим, как разработчики Microsoft, светясь от гордости, рекламируют новую реализацию Http Client Factory, через которую и идут все запросы (не может Microsoft просто написать библиотеку, делающие HTTP запросы, внутри обязана быть магия!). В общем теперь всё managed, в 10 раз лучше и в 50 раз быстрее. Ага, и сделано как раз в 2.1<br />
<br />
Прикидываю, где ещё скрылись HttpClient'ы и нахожу библиотечку для отправки Push-уведомлений, которая внутри себя создаёт данный класс. Собственно, поэтому в первый раз и не было найдено это использование. Переделываю использование данной библиотеки и проблема исчезает! Всё работает замечательно.<br />
<br />
Тут я начинаю громко материться, но нахожу в документации небольшую приписку:<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjiY-ljVVRSC303L2ojo5MJY3cMtICEWvGchAOl8RKmc_veZ_5VQ0Efzbj3L3h30jS0kqOh3hT6AgJoyRlQe3Ss0gvUsd22xj5ah-cna-mWqHCZEymLikhiVYNk0HsQE9FPuLR2LnGEzoI/s1600/2019-04-09+22_24_33.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="365" data-original-width="963" height="242" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjiY-ljVVRSC303L2ojo5MJY3cMtICEWvGchAOl8RKmc_veZ_5VQ0Efzbj3L3h30jS0kqOh3hT6AgJoyRlQe3Ss0gvUsd22xj5ah-cna-mWqHCZEymLikhiVYNk0HsQE9FPuLR2LnGEzoI/s640/2019-04-09+22_24_33.png" width="640" /></a></div>
Т.е. они знают о данной проблеме! Но явно про это написать было стыдно, так что засунули всё в сноску, зато не будет никаких претензий от разработчиков. Сноска есть? Есть! Значит читайте внимательнее.<br />
<br />
Получается совершенно странная ситуация: Disposable-класс, который надо использовать синглтоном. При том, что все предыдущие реинкарнации подобных классов были честными Disposable-объектами. Но тут решили сделать по-другому. Зачем? Я пока не понял. Но свинью подложили знатную. Представьте, у вас есть базовая библиотека, написанная под большой .NET, вы её мигрируете на Core, пользуетесь под Windows, всё нормально, а потом, спустя длительное время замечаете, что приложение жрёт много процессора. Вам нужно будет пройти квест, подобный моему, чтобы найти проблему, переписать библиотеку и весь остальной код, обновить всё что нужно и всё из-за странного архитектурного решения разработчиков из Microsoft.</div>
forcehttp://www.blogger.com/profile/09205548202595699526noreply@blogger.com3tag:blogger.com,1999:blog-1623745693417995987.post-38444470056554557342019-02-10T13:18:00.003+03:002019-02-10T14:07:22.118+03:00Деградация Windows Server<div dir="ltr" style="text-align: left;" trbidi="on">
Есть у меня простенький компьютер. Был куплен в порыве шопоголизма на распродаже. Я так и не придумал, чем занять данный компьютер, поэтому накатил на него Windows Server 2016 (По факту LTSB сборка 10-ой винды 1607, которая Anniversary Update, но с серверными фичами). Компьютер после запуска (и ожидания 8 минут, пока винда разберётся со своими внутренними делами) выглядел так:<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEg-I5sND6589nZJbpY8OHlrwWLRT1Dy1_5jjPR9UUIliQxQ491gg26uVsZuHGfDWoJJvgbef7TdEqmzEXvzsOUedUiz0C8OXEybNEBeoUvlF7XFZW4wMwRDNZXaakQ9SiCYrRApSFCdiBk/s1600/2019-02-09+21_10_19.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="595" data-original-width="666" height="357" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEg-I5sND6589nZJbpY8OHlrwWLRT1Dy1_5jjPR9UUIliQxQ491gg26uVsZuHGfDWoJJvgbef7TdEqmzEXvzsOUedUiz0C8OXEybNEBeoUvlF7XFZW4wMwRDNZXaakQ9SiCYrRApSFCdiBk/s400/2019-02-09+21_10_19.png" width="400" /></a></div>
<br />
Если не вдаваться в подробности, то запущено 54 процесса, 848 потоков и занято 0.9Гб оперативки. Вполне допустимая ситуация для пустого сервера, хотя всегда хочется меньше.<br />
<br />
Но я решил потестировать недавно вышедший Windows Server 2019, эта та же Windows 10, только уже злополучная 1809, October 2018 Update. Я просто обновил сервер, подождал джентльменские 8 минут и результат на экране:<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhLCy-mSCdKShAEC0JpW1SJEsdBLF8QSZgPt5JX7pscYW1QZpKyD7LTLE6YwZg-vzrhgT953-lasV7h7OfTBnB44d8Y1JnvHyP8tcn9E9uaWr_TiMBjfjGeTPsSs43IM78arlqxe9NPC0Y/s1600/2019-02-09+23_43_08.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="593" data-original-width="666" height="355" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhLCy-mSCdKShAEC0JpW1SJEsdBLF8QSZgPt5JX7pscYW1QZpKyD7LTLE6YwZg-vzrhgT953-lasV7h7OfTBnB44d8Y1JnvHyP8tcn9E9uaWr_TiMBjfjGeTPsSs43IM78arlqxe9NPC0Y/s400/2019-02-09+23_43_08.png" width="400" /></a></div>
<br />
Процессов уже стало 110 (ровно в два раза больше!), количество потоков увеличилось всего на 300 штук, что уже лучше, хендлов стало больше почти в 2 раза и сожрано стало на 400МБ оперативной памяти больше.<br />
<br />
Ещё раз, для понимания бреда. За 2 года разработки операционная система на свои личные нужды стала использовать гораздо больше ресурсов. Может от этого она стала быстрее или лучше работать? Как-то незаметно. Вместо оптимизации системы в итоге получаешь просто ещё больше внутреннего потребления. Зачем? А просто так, потому что программисты Microsoft могут тратить ресурсы как захотят. Железо же становится быстрее, памяти больше, никто и не заметит. И вот это бесит неимоверно.<br />
<br />
<b>UPD:</b> Оказывается, это <a href="https://docs.microsoft.com/en-us/windows/application-management/svchost-service-refactoring">сознательное решение</a> Microsoft, они в 1703 разгруппировали сервисы, если в системе больше чем 3.5ГБ памяти. Объяснили как всегда надёжностью и безопасностью. Но памяти это, конечно же, жрёт больше, о чём не скрывают. А то, что "стабильность" теперь зависит от количества оперативной памяти — выглядит это очень странно.<br />
<br />
<b>UPD2:</b> Поиск решения проблемы привёл на следующую <a href="https://winaero.com/blog/why-many-svchost-exe-are-running-in-windows-10-creators-update/#comment-17920">ссылку</a>, надо в реестре по пути:<br />
<span style="background-color: #eeeeee; font-family: "courier new" , "courier" , monospace;">HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control</span> отредактировать (создать?) ключ <span style="background-color: #eeeeee; font-family: "courier new" , "courier" , monospace;">SvcHostSplitThresholdInKB</span>, изменив там количество памяти для разделения процессов. Итог изменения ключа следующий:<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEh7DSUUBBqAetbSDgUqPfAd1Ucap7INAKZtqatUjIsWjbwVtQyArr0PDqIQESwgRJuDLj9kHR4aG4Ra95jE4DSGr0gNgdR2qrQ9U-KbPJb3BX1zNL_-sIEY8E8ptZvgHBnPPlIaqRKGOcM/s1600/2019-02-10+14_04_57.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="593" data-original-width="666" height="355" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEh7DSUUBBqAetbSDgUqPfAd1Ucap7INAKZtqatUjIsWjbwVtQyArr0PDqIQESwgRJuDLj9kHR4aG4Ra95jE4DSGr0gNgdR2qrQ9U-KbPJb3BX1zNL_-sIEY8E8ptZvgHBnPPlIaqRKGOcM/s400/2019-02-10+14_04_57.png" width="400" /></a></div>
<br />
Процессов и памяти резко уменьшилось. До старых значений ещё не доходит, но всё-таки результат стал получше. </div>
forcehttp://www.blogger.com/profile/09205548202595699526noreply@blogger.com0tag:blogger.com,1999:blog-1623745693417995987.post-13198322779889197732018-11-04T18:26:00.002+03:002018-11-04T18:26:19.201+03:00Yappi Days 2018. Впечатления<div dir="ltr" style="text-align: left;" trbidi="on">
Изначально не планировал идти на эту конференцию, но привлекли пара докладов и свободная суббота, так что решил сходить.<br />
<br />
В результате понял, что местные конференции, организованные местными компаниями весьма и весьма унылы. Впрочем, с организацией всё было хорошо, как у взрослых, тут придраться не к чему, проблема с докладами...<br />
<br />
Изначально у конфы не заявлена специализация, просто конференция для разработчиков (видно, чтобы собрать всех желающих программистов Ярославля, кстати, их удивительно много, и занимаются интересными вещами, удивился). В результате, темы были весьма разными (чтобы всем хватило), а сложность докладов очень низкая, местами очень капитанская.<br />
<br />
При этом, доклады от спонсоров очень хорошо выделялись своей очевидностью и рекламой. Например, доклад про PVS-Studio был всего-лишь рекламой PVS-Studio (продукт, кстати, хорош, и на стенде очень плодотворно пообщались с разработчиками), доклад от Тензора поразил своим абсолютным, нереальным отсутствием практического смысла (рекламировалась внутренняя утилита компании Тензор, т.е. её не увидеть, не пощупать, и использовать можно только если ты работаешь в Тензоре).<br />
<br />
Также, у организаторов вышла лажа с порядком докладов: вначале Владимир Ильмов рассказывал про Netflix стек для микросервисов и докер у него был как базовая концепция, а затем был доклад про то, что такое докер в принципе. Совсем не понял, в чём был смысл доклада от CUSTIS, назывался он "Как работает браузер", в реальности человек начал рассказывать про то, что такое DNS, как в http передаются заголовки. Т.е. такой вымученный доклад, лишь бы что-то рассказать (да, я даже в лекции для студентов старался держать уровень выше). При этом CUSTIS никак себя не рекламировал (не было стендов), и смысл им рассказать один бестолковый доклад — я так и не понял.<br />
<br />
Конечно, т.к. было 3 потока, я не мог посетить все доклады, возможно, я пропустил что-то интересное, но общее впечатление весьма посредственное. Причём именно из-за докладов — слишком обще, слишком просто, слишком про всё. Надо специализироваться и повышать уровень. И ещё раз скажу, с организацией всё было отлично, а халявные пиццы от Пиццы-Фабрики очень рулили.<br />
<br />
Да, и жестокий и беспощадный хантинг преследовал всю конференцию, слей контакты и поучаствуй в розыгрыше, заполни анкету и получи банку Колы, иди, сука, в Тензор! В общем, жду вторника, буду смотреть на результат сего действия (я же не сноб, и отдал свои контактные данные, чего жадничать-то). </div>
forcehttp://www.blogger.com/profile/09205548202595699526noreply@blogger.com0tag:blogger.com,1999:blog-1623745693417995987.post-80517983941956175502018-10-10T21:49:00.004+03:002018-10-10T21:49:40.699+03:00WireGuard<div dir="ltr" style="text-align: left;" trbidi="on">
Сегодня Роскомнадзор в очередной раз забанил подсеть Амазона (или хостер пофиксил "проблему"), в общем пришлось заворачивать всё в VPN, что привело к весьма печальной скорости работы сервера, т.к. VPN был далеко.<br />
<br />
Решил поднять его поближе, в Azure, а т.к. в нём весьма грустно с фаирволлом, решил попробовать новый хипстерский VPN под названием <a href="https://www.wireguard.com/">WireGuard</a>. Правда потом оказалось, что VPN всё-таки можно было попробовать устроить, но у меня оказалась неправильная виртуалка, в которой всё делается через жопу. Надо было создавать другую и гуглить как в азуровом фаирволле сделать проброс чего-то отличного от TCP и UDP.<br />
<br />
В общем, WireGuard позиционируется как простой и правильный VPN, без кучи опций и настроек. Просто работает. И в целом да, только оказалось, что это нифига не VPN в привычном понимании (хотя технически да, и виртуальная, и частная, и сеть).<br />
<br />
Что хочется получить от VPN:<br />
<br />
<ul style="text-align: left;">
<li>Стабильной работы (у всех существующих решений всё плохо)</li>
<li>Простой конфигурации (PPTP рулит)</li>
<li>Безопасности (SSTP, L2TP/IPSec, OpenVPN)</li>
<li>Выдачи IP адресов (все умеют)</li>
<li>Выдачи маршрутов (у всех очень грустно)</li>
<li>Выдачи доп. настроек (ещё хуже)</li>
</ul>
<div>
При этом, стоит напомнить про такую технологию как IPSec, которая не является туннелем сама по себе, т.е. не приносит дополнительных интерфейсов и IP-адресов, а просто безопасно заворачивает IP пакеты в себя. Т.е. идеально подходит для связи Server-Server или Сеть-Сеть через конкретные шлюзы. Для динамики и NAT подходит весьма плохо.</div>
<div>
<br /></div>
<div>
И вот тут вылезает WireGuard. Что же он делает?</div>
<div>
<ul style="text-align: left;">
<li>Создаёт отдельный сетевой интерфейс</li>
<li>На него <b>руками</b> необходимо назначить IP (wat?)</li>
<li>Клиент и сервер в целом не разделяются (привет IPSec), они равнозначные пиры (peers), но в целом можно использовать концепцию клиент-сервер, просто сделав определённые настройки</li>
<li>Если нужно устроить VPN, то можно сделать это ручками через iptables и маскарад</li>
<li>Каждый пир определяется парой ключей (публичным и приватным), так что заранее на сервере никого не добавить (можно нагенерить ключей, но как-то коряво выглядит)</li>
<li>По умолчанию роуты связаны со списком разрешённых IP'шников, т.е. делаются на клиенте, проброса нет</li>
</ul>
<div>
Так что получается, что это какой-то упрощённый IPSec, но с отдельным интерфейсом и IP-адресом (без добавления, думаю можно извернуться, но уже не очень дефолтная конфигурация). Т.е. использовать его как VPN — можно, но очень фигово (хотя если для себя делаете, то вполне норм). И с VPN'ами по-прежнему всё тухло, а WireGuard оказался каким-то странным созданием.</div>
</div>
<div>
<br /></div>
<div>
Но с другой стороны, настраивается он элементарно и начинает сразу же работать. Это ему плюс. Только имейте в виду, что конфигурация через wg-quick и ip работает по-разному, через ip можно сделать гораздо больше и и точнее.</div>
<br />
<br />
<br /></div>
forcehttp://www.blogger.com/profile/09205548202595699526noreply@blogger.com0tag:blogger.com,1999:blog-1623745693417995987.post-73927078009390592842018-02-27T22:33:00.003+03:002018-02-27T22:33:55.006+03:00Хранение данных в памяти<div dir="ltr" style="text-align: left;" trbidi="on">
Очень давно вынашиваю идею о том, что базу данных для приложения стоит размещать в памяти. Конечно, не полностью в памяти, основную базу оставить, но использовать очень и очень агрессивное кеширование. Зачем? Об этом ниже.<br />
<br />
Для начала скажу, что сам я, пока так и не реализовал эту идею в полной мере, были продукты, где это активно использовалось, но в основном, продукты, за которые я ответственен работают достаточно быстро и так, так что как-то и не требуется усложнение и смена парадигмы. Но, когда-нибудь, обязательно попробую!<br />
<br />
<h3 style="text-align: left;">
Почему не стоит полагаться на кеш SQL-сервера?</h3>
<div>
Потому что он построен именно на модели кеша для данных, и в него можно попадать или не попадать. Конечно, в MS SQL есть OLTP таблицы, которые хранятся в памяти, но они больше для очень активных данных, и вообще это на уровне SQL, причём MS SQL. А без них — используются стандартные алгоритмы для поиска, оптимизированные для данных, находящихся на диске, как результат, необходимый кеш для очень 100% попадания в память многократно превышает размер реальных данных.</div>
<div>
<br /></div>
<div>
Я же предлагаю забить на это, и максимально хранить все данные из базы в памяти (загружать их из базы на старте, или ленивым образом).</div>
<div>
<br /></div>
<h3 style="text-align: left;">
Что это даёт?</h3>
<br />
<ul style="text-align: left;">
<li>Резко упрощается внешняя логика пользователя. Во многих случаях можно отказаться от джойнов, если нет дополнительной фильтрации. Например, классический джойн с данных с пользователем (например, вывести автора данных) можно заменить на выборку данных и доставание всех пользователей по ID. Это очёнь дешёво</li>
<li>Можно использовать очень тупые алгоритмы, и это всё равно будет очень быстро. Фуллскан 10000 записей почти незаметная вещь, а если надо будет ещё быстрее — всегда можно будет прикрутить "индекс" в виде словарика</li>
<li>Можно избавиться от отдельного кеша для тяжёлых данных, ибо и так всё в памяти</li>
<li>Тупо быстрее из-за отсутствия запросов к внешнему SQL-серверу</li>
<li>Легко хранить данные, которые тяжело забирать из SQL (объект с зависимыми детишками, слабо-структурированные документы, JSON/XML поля)</li>
<li>При этом, если приложение написано с использованием LINQ, то местами можно вообще не заметить разницы между базой и памятью (если грамотно спланировать архитектуру приложения)</li>
</ul>
<br />
<h3 style="text-align: left;">
А в память влезет?</h3>
<div>
А вот это как раз главный вопрос, который всех останавливает и которого все боятся. И из-за этого, всё так и останавливается на уровне идей. Но давайте прикинем необходимое количество памяти.</div>
<div>
<ul style="text-align: left;">
<li>База данных в 20ГБ содержит где-то 20ГБ данных (логично, да?), в памяти это будет занимать примерно столько же. Найти подходящий сервер — не так уж и сложно. Естественно, базы в 100МБ вообще влезут в память без проблем</li>
<li>Очень часто в <i>"больших"</i> базах большой объём занимают всякие полумёртвые данные — журналы, результаты импорта, файлы, подписанные данные, акксесс логи... Эти данные нужны очень редко, их можно не хранить в памяти, тем самым кардинально снизив объём <i>"реальных"</i> данных</li>
<li>Многие данные нужны только в небольшом количестве. Например, у вас есть 10000 пользователей в системе, но активны только 1000, тут можно использовать какой-нить LRU кеш и не держать в памяти все объекты, а только активные. Опять же, очень сокращает необходимый объём памяти</li>
<li>Ну и для реально огромных баз данных можно уж держать в памяти только специально выделенные объекты (например, справочники). Хотя, с такими объёмами у вас будет проблем побольше чем просто держать в памяти</li>
</ul>
<h3 style="text-align: left;">
Как реализовать?</h3>
</div>
<div>
Поскольку я ещё не делал это в полном виде, то могу только предположить следующие варианты:</div>
<div>
<ul style="text-align: left;">
<li>Собственный кеш класса, ратающего с сущностью (e.g. <i>UserManager</i>), он сам решает, что и как кешировать. Проблемы в куче аналогичного кода в разных классах и сложность с инвалидацией. Плюсы: в каждом конкретном случае можно использовать самые эффективные варианты</li>
<li>Мемоизация и автомемоизация методов. Плюсы: очень упрощается код, минусы: сложно инвалидировать и оптимально использовать данные. </li>
<li>Обёртка над ORM (или использование встроенных средств типа Second Level Cache), которая сделает всё сама. Проблемы: сложно в реализации и конфигурировании. Плюсы: полная прозрачность в использовании со стороны кода</li>
</ul>
<h3 style="text-align: left;">
Краткий итог</h3>
</div>
<div>
Данное агрессивное кеширование и разработка с учётом того, что оно есть, может кардинально поменять работу с данными в вашем приложении. Код будет проще, тупее и понятнее, но при этом весьма быстро работать, даже если выполняет сложные операции с данными.При этом сама база данных останется и в ней всегда можно покопаться, поделать аналитику, но при этом не сильно думать об её оптимизации (индексах, статистике и прочем тюнинге), это всё будет на уровне приложения, которое знает, что оно хочет от данных и как оно будет их использовать.</div>
</div>
forcehttp://www.blogger.com/profile/09205548202595699526noreply@blogger.com7tag:blogger.com,1999:blog-1623745693417995987.post-40455224650822417852017-12-29T15:30:00.002+03:002017-12-29T15:30:44.768+03:00Итоги моего 2017 года<div dir="ltr" style="text-align: left;" trbidi="on">
Решил в этом году повторить пост относительно <a href="http://force-net.com/2016/12/2016.html">прошлого</a>, ибо оказалось даже для меня полезным и интересным посмотреть, что было в прошлом и сравнить, что стало (хотя уже было) в этом. Так что, нашёл время (единственный свободный день под конец года оказался, хотя сейчас пытаюсь восстановить угробленные виндой данные с диска в 3Тб), и решил написать, что же произошло технического в этом году.<br />
<br />
Получилось так, что я почти забросил этот блог. ибо хочется писать про интересное, а это интересное надо долго и упорно изучать, чтобы пост не получился глупым и ошибочным. А в итоге получается, что занимаюсь или банальщиной, или не сильно углубляюсь, или лезу в такие дебри, что объяснить их не представляется возможным. А вот так, чтобы что-то интересное... На новый год принято давать себе обещания, типа бросить пить, заняться спортом, вести блог... Так вот не буду, ибо не знаю ещё, чем буду заниматься в следующем году...<br />
<br />
А в этом активно и вплотную занялся .NET Core под Linux, фактически все новые проекты теперь там и живут, и живут весьма неплохо. На работе кроме всякой мелочи был сделан очень крутой проект. Он. конечно выглядит просто и банально, но внутри там очень всё круто, микросервисно, распредёлённо и кешированно. При этом, пока не замучали новыми фичами, он умудрялся жить на мелких виртуалках, используя по максимуму всё, что они предоставляли. Но в конце-концов, один проект превратился в два, с общей и своей частью, я сдался и развёл проект на 3 сервера и разрешил жрать память (хотя слишком разрешил 16 Гигов, съеденных из-за баги доставили много радости в поиске бага).<br />
<br />
В остальном, год был похож на прошлый. Был хакатон, но участвовал я один, в итоге "почётное" третьё место. И ещё не внедрено. Продолжаю пилить на гитхабе свой <a href="https://github.com/force-net/DeepCloner">клонятор</a>, <a href="https://github.com/force-net/Crc32.NET">Crc32</a> (теперь ещё быстрее), и <a href="https://github.com/force-net/blazer">архиватор</a>, который уже помаленьку использую в бою и вижу, что у него есть очень интересные фичи. Также нарисовал свой аналог IPSec под названием <a href="https://github.com/force-net/AutoTunnel">AutoTunnel</a>, получилось интересно, но надо бы чуть допилить, ибо склейка фрагментированных UDP пакетов со стороны Windows — это боль.<br />
<br />
С нетехнической стороны — год отметился путешествиями: Баку, Минск, Кострома, Мюнхен, Тбилиси, Амстердам, Прага... Посетил кучу мест, получил много впечатлений и не собираюсь останавливаться на достигнутом!<br />
<br />
К сожалению, со всей этой беготнёй, забыл про новогоднее настроение, но у меня есть ещё 2 дня его найти, буду упорно стараться. Ну а вам его тоже желаю, надеюсь, что оно у вас уже есть и вы готовитесь к самому весёлому празднику, Новому Году.<br />
<br />
С Наступающим!<br />
<br />
<br />
<br /></div>
forcehttp://www.blogger.com/profile/09205548202595699526noreply@blogger.com0tag:blogger.com,1999:blog-1623745693417995987.post-18686918502094849492017-11-27T23:53:00.001+03:002017-11-27T23:53:31.394+03:00Ненавистный .NET<div dir="ltr" style="text-align: left;" trbidi="on">
Последнее время совсем не пишу в блог, как-то нет подходящих тем, могу только сообщить, что такой ненависти к Microsoft я давно не испытывал. Попытка поработать с .NET Core 2.0 сразу же привела к идиотским ошибкам, типа 2 entry point у приложения. При этом второй генерируется самостоятельно (!), другими словами, у Microsoft новые отличнейшие идеи, <b>как</b> всё должно работать по <b>их</b> мнению, вместо того, чтобы просто сделать рабочий продукт.<br />
<br />
Если кончится мат и появятся слова, постараюсь написать что-то более членораздельное. Но, блин, у Microsoft был отличнейший .NET, приложения на котором просто работали... Теперь не так, они могут падать по совершенно различным причинам, а Dll Hell уже перешёл все границы. Ну, как, как можно так портить жизнь разработчикам за их же деньги...</div>
forcehttp://www.blogger.com/profile/09205548202595699526noreply@blogger.com0tag:blogger.com,1999:blog-1623745693417995987.post-84160093832883218732017-10-02T23:44:00.001+03:002017-10-02T23:44:22.443+03:00Версионирование .NET Core<div dir="ltr" style="text-align: left;" trbidi="on">
Когда-то писал про <a href="http://force-net.com/2016/12/net-corestandardframework.html">.NET Core</a> и обещал написать больше, но было лениво, так что руки не дошли.<br />
<br />
Сейчас просто для понимания бреда, который творится с версионированием краткое описание разных версий. Это может быть полезно, т.к. вышел .NET Core 2.0 и .NET Standard 2.0, и версии слились в экстазе. Но на самом деле они разные, и скоро разъедутся и будут опять портить всем жизнь. Итак, временно забудем про существование 2.0, и вспомним, что есть:<br />
<br />
<br />
<ul style="text-align: left;">
<li>Вы собрались писать под .NET Core, соответственно выбираете, какую версию хотите, вы можете выбрать версию 1.0 (на момент написания 1.0.7) или 1.1 (на момент написания 1.1.4), при этом, особой разницы в этом нет. </li>
<li>На самом деле, вы можете выбрать рантайм или сдк Логично, что для разработки нужен SDK, для версии рантайма 1.0.7, сдк имеет версию 1.1.4 (т.е. рантайм разрабатывается и старый и новый одновременно, сдк только новый)</li>
<li>После этого, вы можете решить, что использовать, .NET Standard или .NET Core. Для библиотек лучше использовать Standard, у него версии: 1.0,1.1,1.2,1.3,1.4,1.5,1.6, для запускаемых файлов лучше Core, у него версии 1.0 и 1.1</li>
<li>Впрочем, вы можете писать библиотеки на Core, а экзешники на Standard, в этом не очень много смысла, но в целом он есть</li>
<li>Версии Standard для удобства используют стандартную библиотеку NETStandard.Library, она бывает версий 1.6.0 и 1.6.1</li>
<li>В этой библиотеке есть стандартные библиотеки, которые любят называться как большие и иметь версию 4.3.0 (большие имеют версию 4.0.0). Впрочем, иногда бывают и 4.2.0 и 4.1.0, и всякие разные</li>
</ul>
<div>
Т.е. приложение мод .NET Core 1.0 может запускаться в рантайме 1.1.4, иметь зависимость на библиотеку .NET Standard 1.3, которая использует библиотеку NETStandard.Library 1.6.1 и это всё будет замечательно работать! Главное надо понять, что это просто разные версии разных библиотек. </div>
<div>
<br /></div>
<div>
Сейчас вышел .NET Standard 2.0, и всё стало совсем просто: приложение под .NET Core 2.0 запускается в рантайме 2.0, имеет зависимость на библиотеку .NET Standard 2.0, которая использует библиотеку NETStandard.Library 2.0.0. К сожалению, скоро все эти версии опять разъедутся в разные стороны, и опять будет путаница. Но. надеюсь, вы теперь будете во все оружии.</div>
<div>
<br /></div>
<div>
PS: Сейчас слушаю про version hell в .NET Core 2.0, и становится страшно, там добавили совместимости из-за которой много всего развалилось, несмотря на обещанную совместимость.</div>
<div>
<br /></div>
<div>
<br /></div>
</div>
forcehttp://www.blogger.com/profile/09205548202595699526noreply@blogger.com1tag:blogger.com,1999:blog-1623745693417995987.post-23598482450097997982017-06-03T13:05:00.000+03:002017-06-03T13:06:42.801+03:00Подлые порты<div dir="ltr" style="text-align: left;" trbidi="on">
Я тут настроил логгирование входящих запросов из интернета на мой комп, конечно же, меня периодически (раз в час) сканируют. Решил просто поделиться списком портов, которые интересуют <i>"хакеров"</i>. Зачем? Просто для информации, на какие порты обращать внимание, и на какие порты не стоит вешать ваши сервисы, если они настолько популярны, что их ломают, то и ваши сервисы могут попасть под раздачу или флуд.<br />
<br />
Собственно, список:<br />
<table border="0" cellpadding="0" cellspacing="0" style="border-collapse: collapse; width: 64px;">
<colgroup><col style="width: 48pt;" width="64"></col>
</colgroup><tbody>
<tr height="20" style="height: 15.0pt;">
<td align="right" height="20" style="height: 15.0pt; width: 48pt;" width="64">7</td>
</tr>
<tr height="20" style="height: 15.0pt;">
<td align="right" height="20" style="height: 15.0pt;">9</td>
</tr>
<tr height="20" style="height: 15.0pt;">
<td align="right" height="20" style="height: 15.0pt;">13</td>
</tr>
<tr height="20" style="height: 15.0pt;">
<td align="right" height="20" style="height: 15.0pt;">21</td>
</tr>
<tr height="20" style="height: 15.0pt;">
<td align="right" height="20" style="height: 15.0pt;">22</td>
</tr>
<tr height="20" style="height: 15.0pt;">
<td align="right" height="20" style="height: 15.0pt;">23</td>
</tr>
<tr height="20" style="height: 15.0pt;">
<td align="right" height="20" style="height: 15.0pt;">25</td>
</tr>
<tr height="20" style="height: 15.0pt;">
<td align="right" height="20" style="height: 15.0pt;">26</td>
</tr>
<tr height="20" style="height: 15.0pt;">
<td align="right" height="20" style="height: 15.0pt;">37</td>
</tr>
<tr height="20" style="height: 15.0pt;">
<td align="right" height="20" style="height: 15.0pt;">53</td>
</tr>
<tr height="20" style="height: 15.0pt;">
<td align="right" height="20" style="height: 15.0pt;">79</td>
</tr>
<tr height="20" style="height: 15.0pt;">
<td align="right" height="20" style="height: 15.0pt;">80</td>
</tr>
<tr height="20" style="height: 15.0pt;">
<td align="right" height="20" style="height: 15.0pt;">81</td>
</tr>
<tr height="20" style="height: 15.0pt;">
<td align="right" height="20" style="height: 15.0pt;">88</td>
</tr>
<tr height="20" style="height: 15.0pt;">
<td align="right" height="20" style="height: 15.0pt;">106</td>
</tr>
<tr height="20" style="height: 15.0pt;">
<td align="right" height="20" style="height: 15.0pt;">110</td>
</tr>
<tr height="20" style="height: 15.0pt;">
<td align="right" height="20" style="height: 15.0pt;">111</td>
</tr>
<tr height="20" style="height: 15.0pt;">
<td align="right" height="20" style="height: 15.0pt;">113</td>
</tr>
<tr height="20" style="height: 15.0pt;">
<td align="right" height="20" style="height: 15.0pt;">119</td>
</tr>
<tr height="20" style="height: 15.0pt;">
<td align="right" height="20" style="height: 15.0pt;">143</td>
</tr>
<tr height="20" style="height: 15.0pt;">
<td align="right" height="20" style="height: 15.0pt;">144</td>
</tr>
<tr height="20" style="height: 15.0pt;">
<td align="right" height="20" style="height: 15.0pt;">179</td>
</tr>
<tr height="20" style="height: 15.0pt;">
<td align="right" height="20" style="height: 15.0pt;">199</td>
</tr>
<tr height="20" style="height: 15.0pt;">
<td align="right" height="20" style="height: 15.0pt;">389</td>
</tr>
<tr height="20" style="height: 15.0pt;">
<td align="right" height="20" style="height: 15.0pt;">427</td>
</tr>
<tr height="20" style="height: 15.0pt;">
<td align="right" height="20" style="height: 15.0pt;">443</td>
</tr>
<tr height="20" style="height: 15.0pt;">
<td align="right" height="20" style="height: 15.0pt;">444</td>
</tr>
<tr height="20" style="height: 15.0pt;">
<td align="right" height="20" style="height: 15.0pt;">465</td>
</tr>
<tr height="20" style="height: 15.0pt;">
<td align="right" height="20" style="height: 15.0pt;">513</td>
</tr>
<tr height="20" style="height: 15.0pt;">
<td align="right" height="20" style="height: 15.0pt;">514</td>
</tr>
<tr height="20" style="height: 15.0pt;">
<td align="right" height="20" style="height: 15.0pt;">515</td>
</tr>
<tr height="20" style="height: 15.0pt;">
<td align="right" height="20" style="height: 15.0pt;">543</td>
</tr>
<tr height="20" style="height: 15.0pt;">
<td align="right" height="20" style="height: 15.0pt;">544</td>
</tr>
<tr height="20" style="height: 15.0pt;">
<td align="right" height="20" style="height: 15.0pt;">548</td>
</tr>
<tr height="20" style="height: 15.0pt;">
<td align="right" height="20" style="height: 15.0pt;">554</td>
</tr>
<tr height="20" style="height: 15.0pt;">
<td align="right" height="20" style="height: 15.0pt;">587</td>
</tr>
<tr height="20" style="height: 15.0pt;">
<td align="right" height="20" style="height: 15.0pt;">631</td>
</tr>
<tr height="20" style="height: 15.0pt;">
<td align="right" height="20" style="height: 15.0pt;">646</td>
</tr>
<tr height="20" style="height: 15.0pt;">
<td align="right" height="20" style="height: 15.0pt;">873</td>
</tr>
<tr height="20" style="height: 15.0pt;">
<td align="right" height="20" style="height: 15.0pt;">990</td>
</tr>
<tr height="20" style="height: 15.0pt;">
<td align="right" height="20" style="height: 15.0pt;">993</td>
</tr>
<tr height="20" style="height: 15.0pt;">
<td align="right" height="20" style="height: 15.0pt;">995</td>
</tr>
<tr height="20" style="height: 15.0pt;">
<td align="right" height="20" style="height: 15.0pt;">1025</td>
</tr>
<tr height="20" style="height: 15.0pt;">
<td align="right" height="20" style="height: 15.0pt;">1026</td>
</tr>
<tr height="20" style="height: 15.0pt;">
<td align="right" height="20" style="height: 15.0pt;">1027</td>
</tr>
<tr height="20" style="height: 15.0pt;">
<td align="right" height="20" style="height: 15.0pt;">1028</td>
</tr>
<tr height="20" style="height: 15.0pt;">
<td align="right" height="20" style="height: 15.0pt;">1029</td>
</tr>
<tr height="20" style="height: 15.0pt;">
<td align="right" height="20" style="height: 15.0pt;">1110</td>
</tr>
<tr height="20" style="height: 15.0pt;">
<td align="right" height="20" style="height: 15.0pt;">1433</td>
</tr>
<tr height="20" style="height: 15.0pt;">
<td align="right" height="20" style="height: 15.0pt;">1720</td>
</tr>
<tr height="20" style="height: 15.0pt;">
<td align="right" height="20" style="height: 15.0pt;">1723</td>
</tr>
<tr height="20" style="height: 15.0pt;">
<td align="right" height="20" style="height: 15.0pt;">1755</td>
</tr>
<tr height="20" style="height: 15.0pt;">
<td align="right" height="20" style="height: 15.0pt;">1900</td>
</tr>
<tr height="20" style="height: 15.0pt;">
<td align="right" height="20" style="height: 15.0pt;">2000</td>
</tr>
<tr height="20" style="height: 15.0pt;">
<td align="right" height="20" style="height: 15.0pt;">2001</td>
</tr>
<tr height="20" style="height: 15.0pt;">
<td align="right" height="20" style="height: 15.0pt;">2049</td>
</tr>
<tr height="20" style="height: 15.0pt;">
<td align="right" height="20" style="height: 15.0pt;">2121</td>
</tr>
<tr height="20" style="height: 15.0pt;">
<td align="right" height="20" style="height: 15.0pt;">2717</td>
</tr>
<tr height="20" style="height: 15.0pt;">
<td align="right" height="20" style="height: 15.0pt;">3000</td>
</tr>
<tr height="20" style="height: 15.0pt;">
<td align="right" height="20" style="height: 15.0pt;">3128</td>
</tr>
<tr height="20" style="height: 15.0pt;">
<td align="right" height="20" style="height: 15.0pt;">3306</td>
</tr>
<tr height="20" style="height: 15.0pt;">
<td align="right" height="20" style="height: 15.0pt;">3389</td>
</tr>
<tr height="20" style="height: 15.0pt;">
<td align="right" height="20" style="height: 15.0pt;">3986</td>
</tr>
<tr height="20" style="height: 15.0pt;">
<td align="right" height="20" style="height: 15.0pt;">4899</td>
</tr>
<tr height="20" style="height: 15.0pt;">
<td align="right" height="20" style="height: 15.0pt;">5000</td>
</tr>
<tr height="20" style="height: 15.0pt;">
<td align="right" height="20" style="height: 15.0pt;">5009</td>
</tr>
<tr height="20" style="height: 15.0pt;">
<td align="right" height="20" style="height: 15.0pt;">5051</td>
</tr>
<tr height="20" style="height: 15.0pt;">
<td align="right" height="20" style="height: 15.0pt;">5060</td>
</tr>
<tr height="20" style="height: 15.0pt;">
<td align="right" height="20" style="height: 15.0pt;">5101</td>
</tr>
<tr height="20" style="height: 15.0pt;">
<td align="right" height="20" style="height: 15.0pt;">5190</td>
</tr>
<tr height="20" style="height: 15.0pt;">
<td align="right" height="20" style="height: 15.0pt;">5357</td>
</tr>
<tr height="20" style="height: 15.0pt;">
<td align="right" height="20" style="height: 15.0pt;">5432</td>
</tr>
<tr height="20" style="height: 15.0pt;">
<td align="right" height="20" style="height: 15.0pt;">5631</td>
</tr>
<tr height="20" style="height: 15.0pt;">
<td align="right" height="20" style="height: 15.0pt;">5666</td>
</tr>
<tr height="20" style="height: 15.0pt;">
<td align="right" height="20" style="height: 15.0pt;">5800</td>
</tr>
<tr height="20" style="height: 15.0pt;">
<td align="right" height="20" style="height: 15.0pt;">5900</td>
</tr>
<tr height="20" style="height: 15.0pt;">
<td align="right" height="20" style="height: 15.0pt;">6000</td>
</tr>
<tr height="20" style="height: 15.0pt;">
<td align="right" height="20" style="height: 15.0pt;">6001</td>
</tr>
<tr height="20" style="height: 15.0pt;">
<td align="right" height="20" style="height: 15.0pt;">6646</td>
</tr>
<tr height="20" style="height: 15.0pt;">
<td align="right" height="20" style="height: 15.0pt;">7070</td>
</tr>
<tr height="20" style="height: 15.0pt;">
<td align="right" height="20" style="height: 15.0pt;">8000</td>
</tr>
<tr height="20" style="height: 15.0pt;">
<td align="right" height="20" style="height: 15.0pt;">8008</td>
</tr>
<tr height="20" style="height: 15.0pt;">
<td align="right" height="20" style="height: 15.0pt;">8009</td>
</tr>
<tr height="20" style="height: 15.0pt;">
<td align="right" height="20" style="height: 15.0pt;">8080</td>
</tr>
<tr height="20" style="height: 15.0pt;">
<td align="right" height="20" style="height: 15.0pt;">8081</td>
</tr>
<tr height="20" style="height: 15.0pt;">
<td align="right" height="20" style="height: 15.0pt;">8443</td>
</tr>
<tr height="20" style="height: 15.0pt;">
<td align="right" height="20" style="height: 15.0pt;">8888</td>
</tr>
<tr height="20" style="height: 15.0pt;">
<td align="right" height="20" style="height: 15.0pt;">9100</td>
</tr>
<tr height="20" style="height: 15.0pt;">
<td align="right" height="20" style="height: 15.0pt;">9999</td>
</tr>
<tr height="20" style="height: 15.0pt;">
<td align="right" height="20" style="height: 15.0pt;">10000</td>
</tr>
<tr height="20" style="height: 15.0pt;">
<td align="right" height="20" style="height: 15.0pt;">32768</td>
</tr>
<tr height="20" style="height: 15.0pt;">
<td align="right" height="20" style="height: 15.0pt;">49152</td>
</tr>
<tr height="20" style="height: 15.0pt;">
<td align="right" height="20" style="height: 15.0pt;">49153</td>
</tr>
<tr height="20" style="height: 15.0pt;">
<td align="right" height="20" style="height: 15.0pt;">49154</td>
</tr>
<tr height="20" style="height: 15.0pt;">
<td align="right" height="20" style="height: 15.0pt;">49155</td>
</tr>
<tr height="20" style="height: 15.0pt;">
<td align="right" height="20" style="height: 15.0pt;">49156</td>
</tr>
<tr height="20" style="height: 15.0pt;">
<td align="right" height="20" style="height: 15.0pt;">49157</td>
</tr>
</tbody></table>
<br />
<div>
Это только за сегодняшнее утро, но я не ставил себе целью собрать весь список. Некоторые порты ожидаемые, некоторые красивые, а некоторые очень удивительные, даже интересно, что в них такого особенного.<br />
<br /></div>
</div>
forcehttp://www.blogger.com/profile/09205548202595699526noreply@blogger.com1tag:blogger.com,1999:blog-1623745693417995987.post-11469308785649814332017-05-23T23:33:00.001+03:002017-05-24T13:07:24.876+03:00Какой тип VPN лучше<div dir="ltr" style="text-align: left;" trbidi="on">
Тут разбираюсь с тем, что лучше в качестве протокола VPN подобрать, с учётом того, что с обоих концов стоят похожие железки, соответственно друг с другом их можно подружить любой комбинацией. Соответственно много читал про типы VPN, и решил поделиться тем, что выяснил (не перепечатывая один и тот же текст из вики).<br />
<br />
<h3 style="text-align: left;">
PPTP</h3>
<div>
Классический протокол, работает везде. На всём что шевелится. Очень быстрый (с ним только IPSec может соперничать при определённых условиях). Но есть один недостаток — дырявый. Стандартный MSCHAPv2 позволяет выяснить пароль, если перехватить сессию. Но, судя по всему, проблема только в случае MITM. Т.е., если не считаете, что ваш трафик перехватывают, то жить вроде бы можно. Есть ещё протоколы вида EAP, но с их поддержкой всё достаточно грустно.</div>
<div>
Второй недостаток — использование в качестве транспорта протокола GRE (тоже самое что TCP, UDP или ICMP, только GRE). Иногда режется.</div>
<div>
В общем, по общему мнению, использовать небезопасно, но быстр</div>
<div>
<br /></div>
<h3 style="text-align: left;">
IPSec</h3>
<div>
Не совсем VPN, а нечто очень клёвое и могучее, которое может шифровать или один порт между двумя компьютерами, или связывать целые подсети безопасным образом. Очень хорошо поддерживается аппартное шифрование, и сам весь шифрованный, может хоть по сертификатам, хоть по PSK ходить, работает в винде на низком уровне, в общем, чудо а не протокол. Есть только пара жирных минусов: первоначальная настройка может быть весьма муторной (с перебиранием галочек, и чтением логов), неосторожное действие может зарубить железяку (будет считать, что трафик к ней должен идти через IPSec, который не настроен), ну и настройка этого через NAT — могучий квест, для настоящих джедаев.</div>
<div>
В общем, по жизни рекомендую связывать удалённые компьютеры с фиксированными IP в безопасную псевдо-локальную сеть. Тут он волшебен. Остальное — на любителя.</div>
<div>
Ходит через UDP, EH и ESP протоколы, что очень хорошо для транспорта, но мутновато для фаривола. с NAT'ом добавляется UDP 4500, и куча мути.<br />
<br /></div>
<h3 style="text-align: left;">
L2TP/IPSec</h3>
<div>
Немного дурацкое название связано с тем, что сам туннель нешифрованный, соответственно поднимается туннель поверх IPSec, что приводит по мнению многих к двойной инкапсуляции и приличному оверхеду. Но т.к. IPSec сам по себе хорош, не так уж и плохо. Живьём попробовать не удалось, уж очень большой квест по настройке. Предпочитаю голый IPSec. В общем, как вы понимаете, мне не очень нравится этот туннель, но если вам кто-то его настроили он работает, то будет весьма безопасный туннель.<br />
Ходит через UDP 1701, EH и ESP протоколы, EH не обязателен.</div>
<div>
<br /></div>
<h3 style="text-align: left;">
SSTP</h3>
<div>
Как программисту, мне очень нравится этот туннель. Ибо это тупой SSL-стрим (по умолчанию на 443-ем порту), в который всё заворачивается. Т.е. с криптографией всё нормально TLS1.2, все плюшки. Проверка сертификатов сервера, возможно клиента. Работает дубово и стабильно. Но один маленький нюанс: хорошо работает только на винде начиная с Висты и более или менее на Микротиках. Под линухом кое-как, под андроидом из коробки ничего нет, ну и в целом не очень распространён.</div>
<div>
Тем не менее, если есть возможность его использовать со стороны системы — будет работать. </div>
<div>
Утверждается, что протокол закрытый, поэтому могут быть дыры, но снаружи это чистый SSL-стрим (не отличить от обычного обращения к сайту, кроме объёма данных), так что все правила безопасности соответствуют https.</div>
<div>
Ещё один недостаток, кроме ограниченной поддержки — TCP канал для тоннеля. В чём проблема? В плохой сети. Ибо TCP-пакеты могут теряться и запрашиваться повторно. Тут получается ситуация TCP over TCP, что при потере пакетов верхнего уровня приводит к куче проблем нижнего. Т.е. два уровня начинают заниматься попытками перепосылки пакетов, что сильно проваливает скорость. Однако, при хорошей сети — всё отлично.</div>
<div>
<br /></div>
<h3 style="text-align: left;">
OpenVPN</h3>
<div>
Последний вариант, о котором я хочу рассказать, но не самый плохой. Это отдельный OpenSource клиент подо всё что шевелится, который позволяет сделать всё что угодно. Хоть примешаться к существующему SSL-трафику на 443-ем порту сервера. В общем, есть всё. Куча алгоритмов, куча вариантов. Минусов только два: нужно ставить отдельно и слегка мутновато настраивать. Если справитесь, то всё будет хорошо, хотя пользователям придётся писать развёрнутую инструкцию.</div>
<div>
Ну и по-возможности, следует настроить его на использование UDP, а не TCP, чтобы не было проблем, аналогичных SSTP. По скорости примерно соответствует SSTP.</div>
<div>
<br />
<h3 style="text-align: left;">
Скорость</h3>
</div>
<div>
Всё очень depends, зависит от тонкой настройки, аппаратной поддержки и прочего. Но мои тесты показали, что в целом скорость распределяется следующим образом</div>
<div>
<ul style="text-align: left;">
<li>PPTP — самый быстрый. Очень и очень быстрый</li>
<li>L2TP/IPsec — чуть медленнее (протоколы серьёзныее)</li>
<li>SSTP — сильно медленнее</li>
<li>OpenVPN — примерно соответствует SSTP, но чуть медленнее (проверял только TCP вариант, думаю UDP будет гораздо быстрее)</li>
</ul>
</div>
<div>
<br /></div>
<h2 style="text-align: left;">
Итоги</h2>
<div>
На самом деле, выбор весьма сложен. Старые протколы или сложные или дырявые, но поддерживаются везде и максимально быстро. Новые стараются сделать удобнее, но с поддержкой грустнее. Я пока не выбрал, что лучше, но думаю про SSTP, когда всё хорошо и PPTP, когда плохо с качеством и скоростью, но очень надо. При хорошей подготовке, возможно лучшим будет всё-таки IPSec, ну а хитрый OpenVPN можно настроить как нравится.</div>
</div>
forcehttp://www.blogger.com/profile/09205548202595699526noreply@blogger.com0tag:blogger.com,1999:blog-1623745693417995987.post-37301244510468528192017-03-08T13:44:00.002+03:002017-03-08T13:44:34.264+03:00Visual Studio 2017 и очередная упоротость от Microsoft<div dir="ltr" style="text-align: left;" trbidi="on">
Данный пост навеян статьёй о выходе <a href="https://blogs.msdn.microsoft.com/dotnet/2017/03/07/announcing-net-core-tools-1-0/">VS2017</a>, в которой есть такая шикарнейшая фраза:<br />
<br />
<blockquote class="tr_bq">
<span style="background-color: white; color: #454545; font-family: WOL_Reg, "Segoe UI", Tahoma, Arial, sans-serif; font-size: 14px;">We’re now encouraging Visual Studio 2015 users to migrate to MSBuild and csproj from project.json. As I stated above, we will not be supporting any of the new .NET Core tools in Visual Studio 2015. We also won’t be updating the Visual Studio 2015 project.json-based tools.</span></blockquote>
И вот эта фраза меня убило, хотя новости ходили давно, но тут они всё-таки сделали всё "в самом лучшем виде". И я не могу не высказаться по этому поводу.<br />
<br />
Дело в том, что я не очень люблю новые технологии в плане использования в продакшене (в пет проджектах — в самый раз!). Дело в том, что их интерфейсы и функционал постоянно меняется, авторы ищут правильные идеи и каждая версия как маленький взрыв.<br />
<br />
В результате, часть времени в проекте уходит на миграцию на новую версию, часть на обучение разработчиков как сегодня правильно писать и часть на исправление незамеченных регрессов. В общем, это хорошо так перекликается с одним из моих ранних постов <a href="http://force-net.com/2015/12/blog-post.html">про велосипеды</a>. Я, собственно, до сих пор предпочитаю использовать .NET 4.0: он работает начиная с XP и VS2010, он не мёртвый (апдейты выходят и для него, и свежие .NET улучшают его код, ибо в реальности одно и тоже), да и в новых не так уж и много полезных фич, чтобы переходить на него. Кроме того, никаких проблем с версионированием, которые поехали дальше: 4.5, 4.5.1, 4.5.2, 4.6, 4.6.1, 4.6.2 — без гугла различия и не вспомнишь.<br />
<br />
Но тут решили делать всё по-новому и перешли на .NET4.6 и .NET Core, всё-таки у Core есть хорошие плюшки в виде нативной компиляции и гарантированной работы под Linux, да и разрабатывается она давно.<br />
<br />
Про сам .NET Core писать не буду, ибо мелочь уже <a href="http://force-net.com/2016/12/net-corestandardframework.html">написана</a>, на на ещё один пост не набирается, но вот что понравилось безоговорочно, это файлы <i>project.json</i>, которые очень похожи на <i>package.json</i>, но для Core. Собственно, наконец-то у Microsoft получился вменяемый формат проекта, который можно редактировать ручками не в плане хотфиксов, а прямо-таки изменять поведение проекта. Ну и работать с проектом не только в студии, но хоть в саблайме.<br />
<br />
И тут приходит Microsoft и говорит, извините, факир был пьян, мы возвращаем всё назад. При этом новые фичи будут работать только в новой студии, а старые поддерживать мы больше не будем. Т.е. фактически кинули всех владельцев 2015-ой студии, которые работали с проектами на Core. При этом Microsoft всю жизнь тащила обратную совместимость, ибо так <i>правильно</i> и <i>ынтырпрайзно</i>. Но, судя по всему, в команде .NET царит атмосфера вида: как <i>хочется</i> и как <i>им удобнее</i>. В результате, мало того, что ты летишь в самолёте, у которого по ходу дела меняют крылья на другие, ещё и забирают двигатели с фразой — они отстой, в новых всё лучше, покупайте наших слонов!<br />
<br />
Собственно, теперь кроме обновления студий всем связанным участникам необходимо опять переделывать весь тулинг (тестирование, билды, пекеджи), опять изучать косяки всех инструментов, мигрировать проекты. Вот задолбало конкретно. Я хочу писать код, а не бороться с очередными новшествами и изучать очередные инструменты, которые не сделают мою жизнь лучше.<br />
<br />
Эээх... как же хорошо было в .NET4.0...</div>
forcehttp://www.blogger.com/profile/09205548202595699526noreply@blogger.com0tag:blogger.com,1999:blog-1623745693417995987.post-76730224310190108942017-02-24T16:17:00.001+03:002017-02-24T16:17:35.458+03:00Немного про оптимизацию<div dir="ltr" style="text-align: left;" trbidi="on">
Есть такая программистская мудрость, что <i>преждевременная оптимизация — это зло</i>, народ верит в эту в целом правильную мысль и пишет код со статусом — когда будет надо, тогда и прооптимизируем.<br />
<br />
Идея это логичная, ибо зачем тратить время на оптимизацию того, что ещё три раза перепишется, но есть один нюанс который забывают во всей этой истории, а именно: <i>если вы собираетесь когда-то в будущем, оптимизировать код, то он должен иметь возможность оптимизации</i>. В переводе на русский, когда пишете код, думайте — будет он работать всю жизнь в таком виде, или у него будут проблемы под нагрузкой и с ними что-то надо делать. И если считаете, что надо что-то делать, сразу думайте о том, <i>как</i> это надо будет делать. Заранее. Возможно даже стоит сразу потратить время на некоторую оптимизацию, улучшив её потом, но получить результат сразу и понимание того, можно ли двигаться дальше.<br />
<br />
На мой взгляд, основное действие, которое требуется для возможности оптимизации — <i>локализация кода в одном месте</i> и уменьшение точек входа. Меньше точек входа — меньше поверхность оптимизации и всё становится проще.<br />
<br />
Давайте приведу пример. Есть у нас список пользователей в системе, и нужен классический CRUD с аццким уклоном в R, ибо пользователи достаются на каждый запрос а меняются всё-таки пореже. Логичный способ улучшить производительность — кеширование. Но чтобы удобно всё кешировалось, работа с пользователями должа быть сосредоточена в каком-нибудь <i>UserManager</i>. И тут сразу вылезает проблема классических приложений, которые, доставая данные, джойнят данные с пользователями (автор, ответственный, владелец). Это просто и удобно, но очень сильно рушит идею кеширования — в нём теряется множество смысла, ибо всё равно идёт запрос в базу.<br />
<br />
Т.е. в случае необходимости кешировать пользователей мы сталкиваемся с проблемой: низкая эффективность кеширования без переписывания всего кода (убирание джойнов и прочей работы с пользователями). Но при этом, если мы изначально не включаем кеширование — то приложение будет доставать пачку пользователей на каждый чих и работать не будет в принципе. Такая вот дилемма.<br />
<br />
Что с этим делать? Я уже расписал:<br />
<br />
<ol style="text-align: left;">
<li>Думайте заранее о том, <i>как</i> вы будете оптимизировать</li>
<li>Если необходимо, делайте простую оптимизацию, которую можно улучшить в будущем</li>
</ol>
<div>
Т.е. в нашем случае с пользователями, мы можем держать вытаскивать их из базы и держать в памяти. На каждое изменение пользователя — сбрасывать кеш. В дальнейшем, можем подключить более эффективную инвалидацию, LRU, уменьшить объём данных в памяти, и идти за редко нужными ними в базу. Это потом. Но сейчас у нас мы оставили одну точку входа (мы знаем, что берём пользователей достаточно быстро), и имеем возможность улучшить кеш.</div>
<div>
<br /></div>
<div>
Тоже самое актуально и для оптимизации скорости работы алгоритмов по CPU, IO и прочему: уменьшите количество точек входа, и подумайте, что можно будет сделать, если всё будет плохо <i>до</i> того, как реализуете задачу целиком.</div>
<div>
<br /></div>
<div>
Данный пост был навеян идеями по оптимизации одного из проектов, которые весьма сложные и трудозатратные, просто потому, что в своё время отложили эту задачу со статусом: <i>решим проблему, когда будет актуально.</i> В результате, довели ситуацию до весьма плачевных последствий.</div>
</div>
forcehttp://www.blogger.com/profile/09205548202595699526noreply@blogger.com0tag:blogger.com,1999:blog-1623745693417995987.post-89189579755842232192016-12-31T19:54:00.002+03:002016-12-31T19:54:45.058+03:00Итоги моего 2016 года<div dir="ltr" style="text-align: left;" trbidi="on">
Я иногда подвожу итоги года в этом блоге, когда есть о чём сказать, а также есть желание и время. В прошлом году вроде бы особо ничего не добился, но есть немного времени, поэтому можно и написать.<br />
Т.к. блог фактически технический, то напишу только про профессиональные результаты.<br />
<br />
На работе проект, в котором я был архитектором благополучно завершился (ну и будет продолжаться дальше). Архитектура мне до сих пор нравится, несмотря на большое количество частей, мы делали относительно безумные вещи, но делали их сами и результат получался гораздо интереснее, чем война со сторонними библиотеками.<br />
Также, наконец-то добрался до <a href="https://www.microsoft.com/net/core#windowsvs2015">.NET Core</a> и <a href="https://www.postgresql.org/">PostgreSQL</a>. По обоим проектам чувства весьма смешанные. Надо бы написать отдельные посты, про это, но пока лень (хотя есть небольшой про <a href="http://force-net.com/2016/12/net-corestandardframework.html">Core</a>, но надо подробнее. Ну и в следующем году, будет ещё одна волшебная платформа (но тут не я автор, а просто участник, посмотрим что выйдет). Также выиграл один из хакатонов на работе и успешно внедрил результат в компании. Горжусь.<br />
<br />
Вообще, на работе всё было весьма и весьма неплохо. У нас собралась отличная команда, которая может делать любые безумные вещи, и пусть другие команды нам завидуют!<br />
<br />
Также в этом году, я впервые за долгое время не поехал на <a href="http://www.msdevcon.ru/">DevCon</a>, какой-то он унылый и неправильный был в этом году. Зато съездил на микротиковский <a href="http://mum.mikrotik.com/">MUM</a>, понял, что админы странные существа, и некоторые "проблемы", которые они решают, для программистов проблемы только в выяснении того, что значат всякие аббревиатуры, а сами задачи простейшие, но админы героически их решают.<br />
<br />
В свободное от работы время приобщаюсь к <a href="https://github.com/">Github</a>'у и <a href="https://www.nuget.org/">Nuget</a>'у. В феврале сделал небольшую, но очень быструю библиотеку для копирования объектов: <a href="https://github.com/force-net/DeepCloner">DeepCloner</a>, потом разошёлся и сделал одну из самых быстрых (или самую быструю) библиотеку для вычисления CRC32: <a href="https://github.com/force-net/Crc32.NET">Crc32.NET</a>. Вообще, началось с того, что мне понадобилась реализация CRC32C, а т.к. автор что-то медленно правил свою (версию для плюсов он уже обновил, а вот .NET никак не может, хотя спустя полгода таки принял мой pull request). Ну и в общем, раз я взялся за CRC32C, то надо было поглядеть на обычный CRC32. На нюгете и гитхабе проекты оказались неушстрыми и полузаброшенными. Пришлось сделать свой.<br />
Ну и в конце-концов, я допилил свой архиватор до рабочего состояния, называется он красиво: <a href="https://github.com/force-net/blazer">Blazer</a>. В прицнипе, нужно его повылизывать и попилить, но в целом там уже достаточно клёвых фич, типа сжатия с шаблоном. Ну и большинство фич сосредоточено в библиотеке, а не в консольном экзешнике.<br />
<br />
Ну и ещё научился находить плавающие баги в коде, сидя в гостинице в Москве с ноутбуком. Хотя фактически несколько часов искал их с помощью головы. Оригинальные впечатления.<br />
<br />
В общем, желаю всем читающим меня отличного Нового Года, покорения новых профессиональных вершин, а также чистого, гладкого и красивого кода.</div>
forcehttp://www.blogger.com/profile/09205548202595699526noreply@blogger.com1tag:blogger.com,1999:blog-1623745693417995987.post-49846825924191253122016-12-15T23:07:00.001+03:002016-12-15T23:15:33.462+03:00Краткий анализ .NET Core/Standard/Framework<div dir="ltr" style="text-align: left;" trbidi="on">
Тут в очередной раз разбирался с тем, что в Microsoft нагородили с .NET, пока окончательно не понял, но в целом, чтобы не пересказывать основные <a href="https://blogs.msdn.microsoft.com/dotnet/2016/09/26/introducing-net-standard/">статьи</a> про всё это дело, просто сообщу, то что я понял:<br />
<br />
<ul style="text-align: left;">
<li>Есть .NET Core, это специальный кроссплатформенный фреймворк</li>
<li>Есть .NET Framework, это классический .NET, работающий под винду</li>
<li>Есть .NET Standard, это набор API, который гарантированно будет работать в .NET Core и .NET Framework (ну и всяких ксамаринах)</li>
<li>Код, изначально написанный на .NET Framework, не будет работать под .NET Core</li>
<li>Если есть желание писать под .NET Standard, чтобы работало под большим .NET Framework, то лучше не делать так. Ибо работать будет, но плохо.</li>
<li>Потому что вся магия в том, что API похожее, но разное. </li>
<li>Другими словами, версия под .NET Framework будет использовать очень похожий, но отдельный набор API. Если он весь скрыт внутри. то ничего страшного, никто не увидит. Если же вылезает наружу, могут быть неприятные последствия.</li>
<li>Ещё раз уточню, что если написано, что Standard 1.2=Framework 4.5.1, то это не значит, что код, написанный под 4.5.1 будет работать на уровне 1.2. Это просто значит, что если написать код, под 1.2, то его с помощью бубна можно заставить работать на 4.5.1. При этом набор API в 1.2 особо нигде не расписан, например, там нет криптографии. Странно, но вот так.</li>
<li>Судя по всему, самая популярная версия .NET Standard — 1.3, что в ней такого особого, пока не разобрался</li>
</ul>
<div>
Т.е. в целом ситуация складывается странная. Если хочется разрабатывать универсальные вещи, то лучше тупо не думать, а разрабатывать под .NET Core, забив на .NET Framework полностью. Потому что поставить .NET Core не сложно, а существущий код под .NET Framework всё равно придётся переделывать, так что, смысла во взрослом фреймворке на мой взгляд немного.</div>
<div>
<br /></div>
<div>
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, не рекомендуется из-за проблем с совместимостью. Ещё раз, стандартная версия фреймворка, сделанная для совместимости, будет несовместима сама с собой. А нам с этим жить...</div>
</div>
forcehttp://www.blogger.com/profile/09205548202595699526noreply@blogger.com0tag:blogger.com,1999:blog-1623745693417995987.post-36825680199957498632016-11-06T13:22:00.000+03:002016-11-06T13:22:24.040+03:00Задача на вероятность<div dir="ltr" style="text-align: left;" trbidi="on">
Тут недавно возникла в голове задача по теории вероятности, задал её в твиттере, но из-за ограничений его формата меня, видно не очень поняли. Несмотря на то, что я забыл терверы, пришлось их упорно вспоминать, чтобы решить самостоятельно <img src="http://kolobok.us/smiles/light_skin/smile.gif" style="border: 0px currentcolor; padding: 0px;" /> или хотя бы приблизиться к решению. При этом казалось, что я хожу по очевидной и банальной проблеме, но никак не мог подобрать правильные слова для гугла. В общем, вроде бы решил, но если кто лучше помнит всё это, может прокомментирует с подсказками, куда смотреть и как всё это называется.<br />
<br />
Собственно, сама задача. Сформулирую её приближенно к реальности, чтобы было понятнее.<br />
<br />
<i>У нас есть условный Киндер-Сюрприз и мы знаем, что в нём есть <b>n</b> различных вариантов игрушек, мы купили <b>k</b> киндеров (k ≥ n). Какая вероятность того, что мы соберём всю коллекцию игрушек? </i><br />
<i><br /></i>
Подумайте над решением, задача мне понравилась в итоге... Для тех, кто не хочет думать, решение ниже.<br />
<br />
<a name='more'></a><br />
<br />
<br />
<br />
Итак, моё решение. Я руководствовался такими соображениями:<br />
<ul style="text-align: left;">
<li>Всего вариантов набрать k киндеров из n вариантов <b>n<sup>k</sup></b>, т.е. размещения с повторениями, если верить <a href="https://ru.wikipedia.org/wiki/%D0%A0%D0%B0%D0%B7%D0%BC%D0%B5%D1%89%D0%B5%D0%BD%D0%B8%D0%B5">Wiki</a>, а фактически число длиной <b>k</b> из <b>n</b> цифр. Т.е. если у нас есть 10 различных игрушек, мы можем считать их цифрами и выкладывать числа.</li>
<li>Нам по факту надо посчитать количество чисел длиной <b>k</b>, которое состоит из различных <b>n </b>цифр. Т.е. если есть цифры 0, 1, 2 и число длиной 4, то нам подходят 2110, 2001, 0021, но не подходят 1110, 2212 и прочие.</li>
<li>Попробуем посчитать количество чисел, у которых меньше чем <b>n</b> разных цифр. Их будет <b>(n-1)<sup>k</sup> * n</b>. Т.е. исключаем одну цифру, получаем <b>(n-1)<sup>k</sup></b> вариантов. А цифр мы можем исключить <b>n</b>.</li>
<li>Вроде всё хорошо... но есть нюанс. В данной формуле мы несколько раз посчитали числа, у которых чисел меньше чем <b>n-1</b>. Т.е. их надо добавить в эту формулу.</li>
<li>А когда мы их будем добавлять, мы добавим лишнего, и нам надо будет исключать...</li>
</ul>
<div>
Короче, в итоге моя формула выглядит так:</div>
<div>
<b>((n<sup>k</sup> - (n-1)<sup>k</sup> * C(n, n-1) + (n-2)<sup>k</sup> * C(n, n-2) - .... + 1<sup>k</sup> * C(n, 1)) / n<sup>k</sup></b><br />
<br />
Где C(n, k) - число <a href="https://ru.wikipedia.org/wiki/%D0%A1%D0%BE%D1%87%D0%B5%D1%82%D0%B0%D0%BD%D0%B8%D0%B5">сочетаний</a>.<br />
Т.е. если у нас есть 3 разных киндера, и мы берём 4, то всего у нас есть 81 вариант, нам подходит:<br />
((81 - (2)<sup>4</sup> * 3 + (1)<sup>4</sup> * 3) / 81 = 36 / 81 = 0.4444<br />
<br />
Т.е. вероятность меньше 0.5, для остальных можно посчитать ручками.<br />
<br />
Что мне не нравится в этой формуле? Ощущение что она некрасивая и неоптимальная, или я как обычно изобрёл велосипед чего-то известного. В общем, жду комментариев.</div>
</div>
forcehttp://www.blogger.com/profile/09205548202595699526noreply@blogger.com2tag:blogger.com,1999:blog-1623745693417995987.post-21019687171502005672016-10-31T22:37:00.001+03:002016-10-31T22:37:29.908+03:00История одной грабли<div dir="ltr" style="text-align: left;" trbidi="on">
Сегодня хочу рассказать о том как маленькая аппаратная проблема привела к хорошему отвалу сервисов. Ситуация интересна тем, что вроде бы несвязанные вещи могут привести к фатальным результатам, так что надо продумывать всё на несколько шагов вперёд.<br />
<br />
Итак, поехали.<br />
<br />
Есть у нас виртуалки, реплицируются они на отдельный выделенный сервер (кстати, он уже заменён). Собственно, от этого сервера требовалось хранить на себе пачку виртуалок и на случай проблем позволить запустить реплику, или восстановить её на основном. Т.е. сам он не принимал участие в работе виртуалок. Тупо такой специфичный бекап.<br />
<br />
И вот на этом сервере отваливается один диск из рейда. Причем отваливается очень интересным образом, вроде бы он есть, но с записью всё плохо. В результате рейд в раздумьях, сервер работает но стоит колом.<br />
<br />
Основные сервера, которые копируют данные на него видят его, и пытаются скопировать, по факту сделали снапшоты от текущей версии и пытаются передать разницу. Разница никак не передаётся. Разница растёт. Растёт эта разница долго и упорно (да, по закону подлости это всё случилось на выходные) и в понедельник в 5 утра на одном из серверов тупо кончается место. Занято всё снапшотами. Место кончилось, все виртуалки ушли на аварийную паузу. И всё сломалось...<br />
<br />
Т.е. все данные есть, ничего не потеряно, но ничего не работает. Починилось это банально — выдёргиванием проблемного диска (ну и заменой его). Рейд развалился, сервер забегал, снапшоты долились.<br />
<br />
А теперь проблема в целом: грабли с диском на неважном сервере вывели из строя основной. Т.е. по факту, следить надо за такими фантастическими вещами. Такие вот пироги с котятами.</div>
forcehttp://www.blogger.com/profile/09205548202595699526noreply@blogger.com0tag:blogger.com,1999:blog-1623745693417995987.post-35584536934637238042016-08-28T21:59:00.001+03:002016-08-28T21:59:42.611+03:00Моя месть node_modules<div dir="ltr" style="text-align: left;" trbidi="on">
К <a href="https://nodejs.org/en/">node.js</a> у меня странные чувства. С одной стороны, это простая и очень быстрая среда для разработки приложений, которая работает на всём, что шевелится. С другой, большинство библиотек написаны на таком уровне, что программисты на C# и прочих серьёзных языках хренеют от ужаса. Впрочем, об этом другой раз.<div>
<br /></div>
<div>
Один из больших, бесящих пунктов у ноды, это папка <i>node_modules</i>. С зависимостями зависимостей и зависимостями зависимостей зависимостей. И эти файлы ещё любят вылезать за 255 символов, что приводит винду в истерику (да, в npm3 это таки пофиксили). И кроме всего прочего, этот миллиард мелких файлов ещё дофига копируется. А копируется он часто, обычно каждый билд. Т.е. тупое перемалывание толпы файлов.</div>
<div>
<br /></div>
<div>
Мне надоело с этим мириться, и я пошёл мучать исходники ноды, чтобы сделать одну подлую вещь. Оказалось, что мелким хаком обойтись не получилось, надо взять костыль побольше, но для node это вполне нормально, и после этого можно избавиться от этой долбаной папки.</div>
<div>
<br /></div>
<div>
В общем, нарисовал я проект с гениальным называнием <a href="https://github.com/force-net/nmisf/">nmisf</a> (node modules in single file). Не буду переписывать его документацию, просто скажу, что вам надо один раз запустить создание бандла и папка node_modules превратится в один файл, в котором будут только нужные файлы, да ещё и без дублей (а с учётом зависимостей зависимостей зависимостей, это актуально). Ну и рядышком ещё один файл для индекса. В итоге, два файла вместо толпы. Что положительно сказывается на времени копирования, ну и количестве хлама на диске.</div>
<div>
<br /></div>
<div>
Ну а после того, как вы сделали эти файлы, удаляйте <i>node_modules</i>, и загружайте модуль nmisf в своё приложение. Он подменит лоадер для ноды, и всё будет шоколадно (правда нативные модули будут распаковываться всё равно, есть технические ограничения).</div>
<div>
<br /></div>
<div>
В общем, вэлкам на тестирование данной библиотеки. На мой взгляд, весьма интересная вещь, и стоит её попробовать. В понедельник заюзаю на боевом проекте (пока никто не видит) и посмотрю на время билдов и запуска. Думаю, результат всех порадует.</div>
</div>
forcehttp://www.blogger.com/profile/09205548202595699526noreply@blogger.com0tag:blogger.com,1999:blog-1623745693417995987.post-47539828882903148542016-08-07T23:47:00.002+03:002016-08-08T00:01:23.550+03:00Мегафичи моего архиватора<div dir="ltr" style="text-align: left;" trbidi="on">
Я тут всё хвастаюсь, что делаю свой архиватор (и уже конкретно задолбал всех окружающих), а он всё делается, правда как-то весьма не спеша. В общем, решил рассказать про пару фич, которые на мой взгляд относительно уникальны для подобной задачи.<br />
<br />
<h3 style="text-align: left;">
Полное шифрование без раскрытия информации</h3>
<div>
Т.е. вообще ничего не говорим. Даже что это архив нужного типа. Безопасность и красота. Есть рандомный набор байт, а является ли он архивом, или ещё чем-то — науке неизвестно. При этом оставлено обычное шифрование, которое можно оптимизировать в плане скорости, но оно раскрывает базовую информацию об архиве, что для обычных ситуаций не проблема, а наоборот удобство, но ведь надо периодически делать фичи и для параноиков. </div>
<div>
<br /></div>
<h3 style="text-align: left;">
Управляющие блоки </h3>
<div>
Вот с этим у других совсем грустно. Смысл в том, что у нас есть поток, который посылает много данных, мы их сжимаем, передаём дальше. Всё хорошо, но тут нам потребовалось послать дополнительную информацию, которая хоть и относится к этим данным, но по факту является метой. Что с этим можно сделать? Можно открыть другой поток, что решает все проблемы, но усложняет логику. Можно данные бить на блоке и в начале каждого блока ставить флажок о типе данных. Неплохое решение, но опять же, всё усложняет, плюс портит сжатие данных, т.к. данные разнородные.</div>
<div>
В моём формате архиватора дофига места для подобных флагов, поэтому вы можете вместе с основными данными послать управляющие. Они не будут сжиматься, а пойдут сбоку, позволяя контролировать поток. Может быть (да и скорее всего), название выбрано неудачно, но смысл в том, что можно в один поток отправить два независимых набора данных, которые не очень сильно будут друг-другу мешать. При этом данные концептуально разные, так что у них есть логический смысл.</div>
<div>
<br /></div>
<h3 style="text-align: left;">
Восстановление данных (не сделано)</h3>
<div>
Мой архиватор по умолчанию использует CRC32C для проверки целостности, но хочется ещё и восстанавливать слегка повреждённые байты. И у меня есть прототип кода. Мало у кого есть подобное. У меня будет. Но пока не сделано, и это печально. Как будет сделано, буду хвалиться.</div>
<div>
<br /></div>
<div>
<br /></div>
<div>
К чему я всё это написал? (Конечно же похвастаться!) К тому, что несовершенство мира раскрывается с каждой маленькой задачей. Вначале оказалась проблема с потоками и флашем, что привело к написанию моего архиватора, потом отсутствие управляющих последовательностей, восстановления данных... Что же будет дальше? Что ещё отсутствует из забавных фич в текущем наборе библиотек? Что ещё волшебного стоит мне добавить в архиватор?</div>
</div>
forcehttp://www.blogger.com/profile/09205548202595699526noreply@blogger.com0tag:blogger.com,1999:blog-1623745693417995987.post-45797459186084116032016-05-29T23:58:00.000+03:002016-05-29T23:58:11.694+03:00Проблемы архиваторов, о которых никто не говорит<div dir="ltr" style="text-align: left;" trbidi="on">
Я тут давно начал "писать" архиватор. Ну, т.е. как начал, сделал самое интересное, а допинать до конца терпения не хватило. Точнее почти хватило, но меня не устроили результаты, а, как известно, первая заповедь перфекциониста-прокрастинатора: <i>лучше сделать хорошо, но никогда, чем плохо, но сейчас!</i><br />
<br />
В общем, я полтора года не трогал свой архиватор, а тут решил, что всё-таки стоит пожертвовать 0.2% степени сжатия и 30МБ/c скорости, и выбрать простую и дубовую реализацию, так что. архиватор почти дописан, осталось сделать последний рывок и вычистить все баги, и добавить лоска. Впрочем, про сам мой алгоритм расскажу как-нибудь в другой раз.<br />
Для начала расскажу про одну небольшую проблему, из-за которой, я начал писать архиватор, а не тупо взял готовый. Проблема весьма специфическая, но если вы в неё встрянете, то будет плохо, очень плохо.<br />
<br />
В общем, вкратце, проблема называется <i>Flush</i>. Т.е. нормальная поддержка архиватором данной команды. Что это значит? А то, что большинство реализаций архиваторов внутри работают с блоками определённой длины (самой любимой) <b>независимо!</b> Т.е. команда Flush приводит к тому, что внутренний буфер отправляется в нижележащий стрим. И если в буфере много данных, то и проблем нет, а если мало, то результат становится очень неприятным с точки зрения качества сжатия.<br />
<br />
Но ситуация может быть ещё хуже, архиваторы могут тупо проглатывать команду Flush (передаю привает реализации GZip в .NET, да она проглатывает Flush)! Что это значит? Это значит, что в некоторых задачах вы в принципе не можете использовать данную реализацию.<br />
<br />
Собственно, про задачи-то я и забыл рассказать. Представьте, у вас есть TCP канал, в котором вы обмениваетесь сообщениями, например JSON'ом (так сейчас модно). Сообщения вам надо проталкивать на другую сторону и очень хочется их сжимать. А поскольку сообщения зависимые и похожие, то и сжимать зависимо. Проталкивать их надо командой Flush, что очевидно, а сообщения у вас небольшие. Что получается? Смотрите картинку:<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEi4sMx3h25NQUkfehxEfldzEhYu7XBUCAbH4_I4LGP74pbpIPsHhhyphenhyphena1mh8bFTHXndwRMB-64W2yf0FO_o6FcVeZeP-6bpkRm2PhRu-X_-Cln7D5-vHul_a2A7RcdxXiu8l8G4tb9xOwJ8/s1600/2016-05-29+23_19_16.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="268" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEi4sMx3h25NQUkfehxEfldzEhYu7XBUCAbH4_I4LGP74pbpIPsHhhyphenhyphena1mh8bFTHXndwRMB-64W2yf0FO_o6FcVeZeP-6bpkRm2PhRu-X_-Cln7D5-vHul_a2A7RcdxXiu8l8G4tb9xOwJ8/s640/2016-05-29+23_19_16.png" width="640" /></a></div>
<br />
В качестве тестовых данных, дамп википедии в 100 Мб (один из стандартных шаблонов для сжатия). По оси X - размер блока, который флушится, по оси Y - степень сжатия.<br />
Что видно? ЧТо на блоке в 16 байт накладные расходны на заголовок превысили все ожидания (только GZip рулит за счёт мелкого заголовка). В дальнейшем, всё становится лучше, но до блока в 16Кб счастья особого нет. Т.е. сжимается, что уже неплохо, но могло быть и лучше.<br />
<br />
Вот, если для примера взять бекап какой-нить базы (какой уже не помню, но это настоящая продуктовая база была). Особенность бекапа базы — очень выражена периодичность на 64Кб, да и вообще много похожести. Хотя флушить бекап базы не очень логично, но для примера пойдёт:<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjbE0aZjj5M6nX4-FsR0xfsfs61g3wsBSqO70MWklLgUEY3OBOAXYtT_9fjhqGESVCr8PMWzyNY7m3DjQW7KyhK2AI7ypkuW9J_1jigzwZJ49h6pX2WoQ-SyuzZKw8cabZiGMnUMcYqfxg/s1600/2016-05-29+23_47_04.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="275" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjbE0aZjj5M6nX4-FsR0xfsfs61g3wsBSqO70MWklLgUEY3OBOAXYtT_9fjhqGESVCr8PMWzyNY7m3DjQW7KyhK2AI7ypkuW9J_1jigzwZJ49h6pX2WoQ-SyuzZKw8cabZiGMnUMcYqfxg/s640/2016-05-29+23_47_04.png" width="640" /></a></div>
<br />
Результаты те же, только более явно выражены. И эта явность как раз и намекет, что 64Кб — это таки нашё всё. А лучше больше. Тот же график, но у него подрезано начала, чтобы был лучше виден масштаб:<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjfpRi9I6gH2aRPVgmUaun-4zVeWSQ7UzymXCu1J1yjUbKQnA2mpKA8MsgBNBbZsByaqY2mXocr4-_NdURNSxrOpq99_ArBIWEeQyBbjKYv_K7TVQWeC8ROZkFecwzUeNe0_8WdCFF_cXs/s1600/2016-05-29+23_53_06.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="266" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjfpRi9I6gH2aRPVgmUaun-4zVeWSQ7UzymXCu1J1yjUbKQnA2mpKA8MsgBNBbZsByaqY2mXocr4-_NdURNSxrOpq99_ArBIWEeQyBbjKYv_K7TVQWeC8ROZkFecwzUeNe0_8WdCFF_cXs/s640/2016-05-29+23_53_06.png" width="640" /></a></div>
<br />
Тут видно, что LZ4 за счёт максимального блока в 262Кб (по дефолту у данной реализации), вполне неплохо идёт дальше, но такой размер блока уже как-то лучше подходит для статичных данных, а там можно и более серьёзные аргументы, начинающиеся на цифру 7 применить.<br />
<br />
В общем, цель моего архиватора: сделать так, чтобы Flush'и не оказывали серьёзного влияния, чтобы вы могли использовать архиватор не особо парясь. И как видите, всё вроде бы получается (мой - красненький). 1024 байта уже позволяют его весьма эффективно использовать.<br />
<br />
Правда на блок уходит 8 лишних байт... и это меня бесит... Ждите следующий пост через пару лет...</div>
forcehttp://www.blogger.com/profile/09205548202595699526noreply@blogger.com0tag:blogger.com,1999:blog-1623745693417995987.post-72499836643650930562016-04-17T18:57:00.001+03:002016-04-17T18:57:25.075+03:00ZeroMQ vs NetMQ<div dir="ltr" style="text-align: left;" trbidi="on">
Последнее время не спеша изучаю <a href="http://zeromq.org/">ZeroMQ</a>, а так как пишу в основном на .NET, то решил сравнить производительность двух реализаций, "официальной" <a href="https://github.com/zeromq/clrzmq4/">clrzmq4</a> (в nuget'е пакет называется ZeroMQ, и все примеры с сайта на нём), и альтернативный вариант <a href="https://github.com/zeromq/netmq">NetMQ</a>, написанный полностью на .NET и совместимый с ZeroMQ.<br />
<br />
В лоб бенчмарков не нашёл, только фразы вида <i>примерно одинаковая скорость</i>, так что решил всё-таки быстренько глянуть на производительность, и в случае принципиальной разницы исследовать дальше.<br />
<br />
Для самого простого варианта я взял Publisher/Subscribe модель и начал закидывать сообщения по-кругу, и смотреть на результат. Результат оказался слегка неожиданным, но красноречивым. Не буду делать графики, ибо и без них всё понятно. Просто некоторые пункты:<br />
<br />
<ul style="text-align: left;">
<li>clrzmq4/NetMQ одинаково быстры (около миллиона сообщений в секунду) при работе с массивами байт</li>
<li>Два паблишера на одного сабскрайбера (странный вариант, ну да ладно), гораздо быстрее работают у NetMQ </li>
<li>При попытке работы со строками clrzmq4 начинает резко сливать производительность (в 3 раза). Судя по-всему, не очень удачная работа с маршаллингом</li>
<li>С clrzmq4 надо быть очень аккуратным, пропущенный Dispose даст прирост в скорости, но может вызвать неприятные ошибки</li>
</ul>
<div>
В общем, тестировать производительность дальше неинтересно, не думаю, что там есть какая-то волшебная разница на разных паттернах, с учётом того, что у NetMQ гораздо приятнее API, рекомендую рассматривать в качестве реализации API сразу же его и не париться с выбором.</div>
<div>
<b>NetMQ — быстрее и удобнее</b>.</div>
</div>
forcehttp://www.blogger.com/profile/09205548202595699526noreply@blogger.com1tag:blogger.com,1999:blog-1623745693417995987.post-2731101904373833462016-04-07T00:33:00.001+03:002016-04-07T23:09:33.202+03:00Производительность SQL при поиске по ключу в зависимости от типа<div dir="ltr" style="text-align: left;" trbidi="on">
Последнее время в проектах для SQL-таблиц в качестве идентификатора мы используем гуиды. Ибо уникально, не нужны идентити, и можно идшники генерить на клиенте (причём сразу для всего дерева объектов), а потом вставлять в базу (при этом, хоть асинхронно).<br />
<br />
Но вот тут у меня закралась мысль, а насколько эффективно SQL в дальнейшем работает с данными типами? И не будут ли голые инты быстрее во всяких селектах и джойнах, да так, что перекроют с лихвой все недостатки при вставке (ведь в основном все селектят, это обычно важнее, чем вставка).<br />
<br />
Тестировать можно долго и по-разному, но для простоты, я ограничился следующими условиями:<br />
<br />
<ul style="text-align: left;">
<li>Таблицы с Id и Value (чтобы совсем грустные не были</li>
<li>Id типов int, bigint и guid (т.е. 4, 8 и 16 байт, меньшие типы несерьёзно тестировать)</li>
<li>Id - кластерный индекс и первичный ключ</li>
<li>Заполнение по Identity и по рандому (чтобы проверить, влияет ли более разреженный индекс на результаты). Для гуидов идентити эмулировалось вручную, генерацией "правильных" данных </li>
<li>Заранее вставил 1000000 записей в каждую таблицу</li>
<li>Селекчу много раз определённую запись (глупо, но SQL ведь особых чудес ловкости отваги проявить тут не сможет, впрочем, попробовал разные записи, результат похож)</li>
</ul>
<div>
В качестве подходящей незагруженной железяки (чтобы не влияли на результаты) оказался сервер с SSD, достаточным количеством памяти, но с SQL 2008. Не очень свежо, но не думаю, что что-то менялось на подобных запросах. Впрочем, позднее перетестирую, и если результаты изменятся — обновлю пост.</div>
<div>
<br /></div>
<div>
В общем, замерил результаты и.... они оказались <b>практически идентичными</b>. Конечно, разница есть в пределах 3%, но это несущественно. Впрочем, смотрите картинку сами:</div>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjcKtOpl25dDcImYKjWv-0D6LOfLgRB6fpg4L2QCFKqX-Uy6F_frnLBnxTDTHXTpNmFvddu7-HN_cb7_vab0A8HC6YDApYru49O8WvsQhSq5VoOF4Al2fah60LSRcK7T1AF5Zb2A7L5YCg/s1600/2016-04-07+00_13_56.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjcKtOpl25dDcImYKjWv-0D6LOfLgRB6fpg4L2QCFKqX-Uy6F_frnLBnxTDTHXTpNmFvddu7-HN_cb7_vab0A8HC6YDApYru49O8WvsQhSq5VoOF4Al2fah60LSRcK7T1AF5Zb2A7L5YCg/s1600/2016-04-07+00_13_56.png" /></a></div>
<div>
Вам может показаться, что разница существенная и в пользу инта, давайте немного поправим масштаб картинки:</div>
<div>
<br /></div>
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgD4Kn126VomUAr8asMXpGXUdCQ-dMR2Uq5UUc1u0Li7wKGjvRoLc13AIRDVFx03Ri4yCIqTO4z9FIeNpFLpy4YQ6g9u4Zyo3lYgDPrPihLXy5_kLvZIswyZJ0y7a3oFA_XNJ1OoWgN6Yg/s1600/2016-04-07+00_16_04.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgD4Kn126VomUAr8asMXpGXUdCQ-dMR2Uq5UUc1u0Li7wKGjvRoLc13AIRDVFx03Ri4yCIqTO4z9FIeNpFLpy4YQ6g9u4Zyo3lYgDPrPihLXy5_kLvZIswyZJ0y7a3oFA_XNJ1OoWgN6Yg/s1600/2016-04-07+00_16_04.png" /></a></div>
Так уже понятнее становится, что разница настолько мала, что хотя бы в данной ситуации типом можно пренебречь и выбирать тот, который больше нравится.<br />
<br />
Но дальше я решил разнообразить тесты и сделать точно такие же таблицы, но без ключей (<i>не повторять самостоятельно, опасно для жизни!</i>). Результаты оказались слегка обескураживающими<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEioIcF9noygyXOyAgtOyqiAVm1Lacxv0ASR3fx0hwbyc2Oe-61i0lsPV9tv1Mnd-96AeyVvXCa3b2A3n-Z3H1zKOaNj8aX1PdAjqHBw0B1yFLjHPXr8Vf_PXdv2IQWFjN2qMb3l4G7golA/s1600/2016-04-07+00_20_15.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEioIcF9noygyXOyAgtOyqiAVm1Lacxv0ASR3fx0hwbyc2Oe-61i0lsPV9tv1Mnd-96AeyVvXCa3b2A3n-Z3H1zKOaNj8aX1PdAjqHBw0B1yFLjHPXr8Vf_PXdv2IQWFjN2qMb3l4G7golA/s1600/2016-04-07+00_20_15.png" /></a></div>
Инт тут не просто в пролёте, он в почти в 3 раза медленнее гуида! А бигинт самый быстрый (что, в принципе не так уж и странно, но не в случае такого провала с интом).<br />
Вначале я думал, что табличке с интами не повезло и она нарвалась на длинный фулскан, пробовал селектить разные записи — результат идентичный. В чём дело, я не понимаю. Возможно проблемы SQL сервера, а возможно так и задумано, тут я не специалист. В общем, надо доисследовать, но ситуация очень неординарная. Бигинты круче интов в несколько раз...<br />
<br />
Ну и напоследок размер таблиц. Понятно, что это всё сильно depends и от данных и от наполненности индекса, но общее представление можно поиметь:<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhO_eqGiwAq5AURbmRvCAO8AlnTq5nLvCMm5ngF694beokHRuqmV1_CAkAjWXIFImmXcLyP8lsQBKtdf1H4kmHoywLyOkKrLTnUXEMq_LXVZ28sw7WtK0AA2p5zlXM7_bU2U_OY63b2DvQ/s1600/2016-04-07+00_28_33.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhO_eqGiwAq5AURbmRvCAO8AlnTq5nLvCMm5ngF694beokHRuqmV1_CAkAjWXIFImmXcLyP8lsQBKtdf1H4kmHoywLyOkKrLTnUXEMq_LXVZ28sw7WtK0AA2p5zlXM7_bU2U_OY63b2DvQ/s1600/2016-04-07+00_28_33.png" /></a></div>
<br />
Тут всё просто и логично, снос кластерного индекса экономит 4-5 мегабайт на миллионе записей, бигинт больше инта где-то на 4 мегабайта, гуид больше бигинта на 8-9 мегабайт в зависимости от наличия/отсутствия индекса.<br />
<br />
<b>Update:</b> Обновил SQL Server с 2008 до 2014. Результаты: Поиск по индексу стал где-то на 40% быстрее (смысл обновляться оказывается имеется!), Поиск по таблицам без первичного ключа: с интами ничего не изменилось, гуиды стали быстрее на 30%, т.. практически сравнялись с бигнитами. Т.е. проблемы те же самые на месте, но гуиды стали ещё более привлекательнее. Картинка с данными:<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgyuOTL-Du7qfrXqIU8wK6-fmWNHohuTfn10XxeP2KvqZ6hW8nw77PlNxRLDxJK4c6Wkd6ZYj_5V-Eiue_sZb-QZDiBx0jxvLY6PWH0n-j80uCr6_JQWOKLdKtowDmDFB8M_-os1sN2RSk/s1600/2016-04-07+22_55_37.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgyuOTL-Du7qfrXqIU8wK6-fmWNHohuTfn10XxeP2KvqZ6hW8nw77PlNxRLDxJK4c6Wkd6ZYj_5V-Eiue_sZb-QZDiBx0jxvLY6PWH0n-j80uCr6_JQWOKLdKtowDmDFB8M_-os1sN2RSk/s1600/2016-04-07+22_55_37.png" /></a></div>
<br />
<b>PS: </b>OLTP-таблицы дают офигенный прирост скорости для таблиц без индекса, но OLTP для таблиц с индексом даёт те же результаты в моём случае. В общем, OLTP, это предмет отдельного поста, просто, раз обновился, решил глянуть.</div>
forcehttp://www.blogger.com/profile/09205548202595699526noreply@blogger.com7tag:blogger.com,1999:blog-1623745693417995987.post-58815959056724057392016-04-06T10:28:00.002+03:002016-04-06T10:28:18.285+03:00Windows Update ошибка 800F0922<div dir="ltr" style="text-align: left;" trbidi="on">
Собственно, данный пост на случай, если его нагуглят, ибо гугление ошибок с виндовым апдейтом — самое милое дело. Других сведений про источник проблемы обычно нет.<br />
Итак, я долго возился с тем, что у меня не ставились некоторые обновления. Занимался уже делением пополам и установкой того, что ставится (ибо, если не ставится одно, откатываются все пакетом, ибо транзакционность). Но на днях решил разобраться, в чём же причина.<br />
Собственно, т.к. не ставилась куча апдейтов, код конкретного не привожу, но ошибка была с кодом 800F0922, и выглядела примерно так<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhUxPzrhZxjuhnFZsMuv6TfJ3NAC5t2JyYSBJmCh4edABDZN1hxZ8yDqRZFdvtxeds6zFQSBv1O4mVgP-LW8SPVrEJKRmGcXMXpb_oed8lLfSjjf3hp0gcBenEF2eM3jY2mMXHjVbII2lA/s1600/2016-04-06_10-17-27.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhUxPzrhZxjuhnFZsMuv6TfJ3NAC5t2JyYSBJmCh4edABDZN1hxZ8yDqRZFdvtxeds6zFQSBv1O4mVgP-LW8SPVrEJKRmGcXMXpb_oed8lLfSjjf3hp0gcBenEF2eM3jY2mMXHjVbII2lA/s1600/2016-04-06_10-17-27.png" /></a></div>
<br />
Гуглёж по коду выдал кучу безумных рекомендаций вида — удалить драйвер Cisco VPN или увеличить служебный раздел System Reserved. А также стандартные пляски с бубном вида sfc /scannow и сброса состояния Windows Update. Но первые причины явно были не мои, а вторые помогают только, когда действительно сломано <b>всё</b>. У меня же были проблемы только с частью апдейтов.<br />
<br />
В общем, я пошёл копать в логи, которые у апдейтов находятся в C:\Windows\Logs\CBS\CBS.log на предмет, того, что же там не так. А не так там оказалась подобная строчка (хана дизайну блога, ну да ладно):<br />
<br />
<div style="background: #f0f0f0; border: 1px dashed black; padding: 5px; whitespace: pre;">
<pre>2016-04-05 10:03:35, Error CSI 00000001 (F) Logged @2016/4/5:07:03:35.059 : [ml:262{131},l:260{130}]"events installer: online=1, install=1, component=wow64_Microsoft-Windows-Win32k_31bf3856ad364e35_6.2.9200.17461_neutral_release__."
[gle=0x80004005]
2016-04-05 10:03:35, Error CSI 00000002 (F) Logged @2016/4/5:07:03:35.090 : [ml:240{120},l:238{119}]"EventAITrace:Provider Microsoft-Windows-Win32k is already installed with GUID {e7ef96be-969f-414f-97d7-3ddb7b558ccc}.
"
[gle=0x80004005]
2016-04-05 10:03:35, Error CSI 00000003 (F) Logged @2016/4/5:07:03:35.090 : [ml:168{84},l:166{83}]"WmiCmiPlugin manproc.cpp(683): InstrumentationManifestAssert failed. HR=0x80073aa2."
[gle=0x80004005]
2016-04-05 10:03:35, Error CSI 00000004 (F) Logged @2016/4/5:07:03:35.090 : [ml:166{83},l:164{82}]"WmiCmiPlugin eventloghandler.cpp(192): ProcessEventsInstall failed. HR=0x80073aa2."
[gle=0x80004005]
2016-04-05 10:03:35, Error CSI 00000005 (F) Logged @2016/4/5:07:03:35.090 : [ml:170{85},l:168{84}]"WmiCmiPlugin eventloghandler.cpp(212): EventLogHandlerInstall failed. HR=0x80073aa2."
[gle=0x80004005]
2016-04-05 10:03:35, Error CSI 00000006@2016/4/5:07:03:35.090 (F) CMIADAPTER: Inner Error Message from AI HRESULT = HRESULT_FROM_WIN32(15010)
[
[22]"Configuration error.
"
]
</pre>
</div>
<br />
аКлючевое в этом: EventAITrace:Provider Microsoft-Windows-Win32k is already installed with GUID {e7ef96be-969f-414f-97d7-3ddb7b558ccc}, т.е. в переводе на русский, уже есть источник журнала событий, а его опять пытаются сделать. Почему бы не успокоиться, я не знаю. Но решение тут банальное:<br />
<br />
<ul style="text-align: left;">
<li>идём в реестр в ветку <i>HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\WINEVT\Publishers\{e7ef96be-969f-414f-97d7-3ddb7b558ccc}</i></li>
<li>Экспортируем её на всякий случай</li>
<li>Удаляем</li>
<li><b>Перегружаемся</b> (это важно)</li>
<li>Пробуем установить апдейт заново</li>
</ul>
<div>
И чудо! всё заработало. Все апдейты стали снова ставиться без проблем.</div>
<div>
Это, естественно, не единственный вариант источника проблемы, но, может быть в вашем случае он поможет.</div>
</div>
forcehttp://www.blogger.com/profile/09205548202595699526noreply@blogger.com1tag:blogger.com,1999:blog-1623745693417995987.post-83212696331162594762016-01-20T00:43:00.003+03:002016-01-20T00:43:58.542+03:00Не верьте бенчмаркам. Все врут!<div dir="ltr" style="text-align: left;" trbidi="on">
Последние несколько дней я развлекаюсь с кодогенерацией и выяснением особенностей работы .NET в плане производительности и эффективности. В качестве кошки, на которой я решил тренироваться была выбрана идея <i>Написать самый быстрый JSON Serializer для .NET</i>. Не факт, что я его напишу, но есть некоторые подвижки, а также обнадёживающие результаты.<br />
<br />
Собственно. пока я писал его, я покопался в кишках<a href="http://www.newtonsoft.com/json"> JSON.NET</a>, офигел от того, что они творят местами в плане выгрызания кусочков скорости, порадовался, что некоторые вещи они сделали очень похоже на мои, и в общем, понял, что JSON.NET аццки быстрый для своего функционала.<br />
<br />
Но также я видел всякие тесты, что сериализатор от <a href="http://docs.servicestack.net/text-serializers/json-serializer">ServiceStack</a>, самый быстрый сериалайзер<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhcaGygzsw1rHjZ7mHHPI7cYfQ5dqqBjJtGDBH0XHJyU50w4W-DmMhyphenhyphenhyphenhyphengBEYtZqVD5u62wLfJJnHJshq3JfA7y-mGLDJ2AvHamOhEgEPgJwMNk9UkonU2V0p4F8N2oO-rTNMs8TCS-IFg/s1600/2016-01-20+00_18_27.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="211" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhcaGygzsw1rHjZ7mHHPI7cYfQ5dqqBjJtGDBH0XHJyU50w4W-DmMhyphenhyphenhyphenhyphengBEYtZqVD5u62wLfJJnHJshq3JfA7y-mGLDJ2AvHamOhEgEPgJwMNk9UkonU2V0p4F8N2oO-rTNMs8TCS-IFg/s400/2016-01-20+00_18_27.png" width="400" /></a></div>
И меня это смутило, что же там ещё можно сделать в .NET, чтобы было ещё быстрее (хотя я знаю что, но не верю, что в зравом уме, кто-то подобное будет делать). В общем нашёл несколько красивых графиков: <a href="http://mono.servicestack.net/benchmarks/">тут</a>, <a href="http://theburningmonk.com/2013/09/binary-and-json-serializer-benchmarks-updated/">тут </a>и <a href="http://blog.developers.ba/replace-json-net-jil-json-serializer-asp-net-web-api/">тут</a>. И решил-таки проверить на практике.<br />
<br />
Взял простенький массив:<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiTlsiFCnbZ29ZRZLCrY3XItZs44AouKHZCjUv1hLekVerfm6tg3PnzEXfG9v_IsN5Y7ReHzIG0J7PfHmAcY62OL7pG5TuuWkXhX9aI_BidfDCGVdWLHKLAJJT5XEtQcPMK1yHiRaS4YAA/s1600/2016-01-20+00_36_21.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiTlsiFCnbZ29ZRZLCrY3XItZs44AouKHZCjUv1hLekVerfm6tg3PnzEXfG9v_IsN5Y7ReHzIG0J7PfHmAcY62OL7pG5TuuWkXhX9aI_BidfDCGVdWLHKLAJJT5XEtQcPMK1yHiRaS4YAA/s1600/2016-01-20+00_36_21.png" /></a></div>
<div class="separator" style="clear: both; text-align: left;">
В этом массиве есть числа, даты, строки разных видов, энамы и нуллабельные поля. Т.е. полный фарш. Набрал всяких сериализаторов и пошёл сериализовать. Результаты следующие:</div>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjnC-ToKBeKhTFA0Evbdiw_AZ-re6nDkfX8cDZY3JaeLE_i4WAzJfVs1SKdLV8SFaxaqzhZmzlA-MzMoOzDOV85Kt6Cr9CVfXbHQYdrDcvQYh609D5c39SYvA5ybYA2ViReELLAhuaxW3M/s1600/2016-01-20+00_38_16.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img alt=".net json serializers benchmarks" border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjnC-ToKBeKhTFA0Evbdiw_AZ-re6nDkfX8cDZY3JaeLE_i4WAzJfVs1SKdLV8SFaxaqzhZmzlA-MzMoOzDOV85Kt6Cr9CVfXbHQYdrDcvQYh609D5c39SYvA5ybYA2ViReELLAhuaxW3M/s1600/2016-01-20+00_38_16.png" title=".net json serializers benchmarks" /></a></div>
<div class="separator" style="clear: both; text-align: left;">
Как видно, майкрософтовский в полной жопе, за ним идёт сериализатор с говорящим названием fastJSON (понятие не имею, что это, может где-то он действительно быстр), И затем... ServiceStack! На 66% медленнее JSON.NET Вот и хвалёные тесты.</div>
<div class="separator" style="clear: both; text-align: left;">
<br /></div>
<div class="separator" style="clear: both; text-align: left;">
Самый быстрый оказался <a href="https://github.com/kevin-montrose/Jil">Jil</a>, таки есть ещё более безумный человек чем я. Мой тоже неплохо себя ведёт, несмотря на то, что прототип. Хотя стоит ли мой мучать, при наличии Jil, это интересный вопрос. <img src="http://kolobok.us/smiles/light_skin/smile.gif" style="border: 0px currentcolor; padding: 0px;" /></div>
<div class="separator" style="clear: both; text-align: left;">
<br /></div>
<div class="separator" style="clear: both; text-align: left;">
PS: Моим бенчмаркам тоже не верьте. </div>
<div class="separator" style="clear: both; text-align: left;">
<br /></div>
</div>
forcehttp://www.blogger.com/profile/09205548202595699526noreply@blogger.com0tag:blogger.com,1999:blog-1623745693417995987.post-43589800368182780882015-12-31T19:47:00.003+03:002015-12-31T19:47:52.752+03:00Про велосипеды<div dir="ltr" style="text-align: left;" trbidi="on">
Несмотря на то, что сегодня 31-е декабря, хочется написать не праздничный, а технический пост. Хотя и философский слегка. Как же без этого. <img src="http://kolobok.us/smiles/light_skin/smile.gif" style="border: 0px currentcolor; padding: 0px;" /><br />
<br />
В общем, хочу сегодня поговорить про велосипеды в программировании. Ведь я в компании считаюсь знатным велосипедистом, но спасает меня от явных насмешек то, что мои велосипеды работают, и я думаю достаточно неплохо.<br />
<br />
Собственно, можно долго обсуждать о том, что лучше: сторонние библиотеки или свои велосипеды. Аргументов много с каждой стороны, поэтому хочется обратить внимание всего-лишь на пару пунктов использования сторонних библиотек в проекте, которые для меня кажутся достаточно важными.<br />
<br />
Первая проблема это то, что в большинстве случаев сторонная библиотека не идеально подходит для вашей задачи.<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiyfNPiSIq_R_-svm29u6kJrLtRE_yGaQXj3rPN_Q4AZyr3WVfOeutlqgiF4O3ep-dqkFz8kK5IVKYQhCPdbLsyYBsh3tV628bkvpmNXNXUz7fbu6V5MRxcANLyCCF38DkcE92kYZ2KLb0/s1600/2015-12-31+19_24_00.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiyfNPiSIq_R_-svm29u6kJrLtRE_yGaQXj3rPN_Q4AZyr3WVfOeutlqgiF4O3ep-dqkFz8kK5IVKYQhCPdbLsyYBsh3tV628bkvpmNXNXUz7fbu6V5MRxcANLyCCF38DkcE92kYZ2KLb0/s1600/2015-12-31+19_24_00.png" /></a></div>
Есть некая область, которая очень плохо вписывается в область внешней библиотеки. Почему? Потому что у её авторов своё видение архитектуры у вас своё. Авторы делают универсальную библиотеку, вы делаете конкретный проект. Всё это приводит к тому, что есть в коде моменты, в которых приходится извращаться, чтобы запихнуть вашу логику, в логику библиотеки. Не буду приводить конкретные примеры, но часто проблемы бывают с лишней абстракцией, недостаточной производительностью в определённых ситуациях, проблемами с расширением функционала. В общем, проблемы бывают и достаточно часто. Но при этом все любят считать время, потораченное на свою реализацию и не считать время, потраченное на борьбу с внешней библиотекой.<br />
Например, у нас был случай, когда программист чуть ли не 2 дня потратил на интеграцию системы нотификаций в наш проект, т.к. она тянула за собой огромное число зависимостей и требовала кучу настроек. Правда, умела она тоже много чего, хоть нам это и не особо требовалось. Для нашего случая можно было бы в лоб отправить письмо, заняло бы это пару часов на простенькую реализацию. Но в конце-концов мы сделали весьма неплохую реализацию отправки сообщений, которую подключить можно за 10 минут, которую мы знаем досконально и точно можем сказать — это можно сделать, это нельзя, а это можно но костылём.<br />
<br />
Вот, кстати, зависимости, больная тема. Некоторые проекты выглядят как-то так:<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjmz6Ko2nHoVxsgtU-FlSlKJWxRmRK-X_IKQgYg7o3hwhWVz1_ZCFI_bhtga17pa1NcIykw8qGj1juBfD8AQqydmC4dAtBeCZhA-kkXAgKwUUIe7MLzBQILhMmM7wqmBxXfOBLY_0LTpOs/s1600/2015-12-31+19_38_00.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjmz6Ko2nHoVxsgtU-FlSlKJWxRmRK-X_IKQgYg7o3hwhWVz1_ZCFI_bhtga17pa1NcIykw8qGj1juBfD8AQqydmC4dAtBeCZhA-kkXAgKwUUIe7MLzBQILhMmM7wqmBxXfOBLY_0LTpOs/s1600/2015-12-31+19_38_00.png" /></a></div>
Красным цветом я выделил ваш проект, остальные круги — библиотеки. Чать их функционала пересекается друг с другом, Некоторые библиотеки используются только ради одного мелкого куска, но всё это тянет кучу файлов, кучу зависимостей, и они все не бесплатные. Приложение в результате может требоваить дополнительной установки компонетов, которые лично вам не нужны, но нужны библиотеке, размер приложения резко увеличивается, лезут неявные конфликты типа транзитивных зависимостей. А потом пишется инструкция в три страницы по установке, а очередной программист облачается в костюм хим.защиты и лезет выяснять, почему приложение стартует 20 секунд и жрёт гигабайт памяти на простейших тестовых данных.<br />
<br />
В общем, что я хочу сказать — внешние библиотеки могут приводить к куче отложенных проблем, к вендор.локу, когда она ироникают во весь код и избавиться от данной реализации уже невозможно без полного переписывания и прочим неприятным проблемам. При этом, все могут героически с этим бороться или вообще не считают проблемой. В тоже время считать, что своё решение хуже и каждый раз спрашивать: <i>А может стоило использовать технологию X?</i> Может и стоило... а может и нет, ведь сейчас ты пытаешься решить незначительную проблему, от которой будут счастливы пользователи, а на технологии X проблему в принципе не решить, ок, все-лишь менее счастливые пользователи, мир не перевернулся.</div>
forcehttp://www.blogger.com/profile/09205548202595699526noreply@blogger.com4tag:blogger.com,1999:blog-1623745693417995987.post-59501619073942085082015-11-15T14:17:00.002+03:002015-11-15T14:17:43.337+03:00Чем мне не нравятся async/await<div dir="ltr" style="text-align: left;" trbidi="on">
Тут на днях у нас вышел спор с коллегами по поводу <a href="https://msdn.microsoft.com/en-us/library/hh191443.aspx">async/await</a> в Microsoft .NET. Мне эта технология не очень нравится, несмотря на её круть, но сформулировать причины сходу весьма непросто. В этом посте попробую объяснить, в чём же проблемы (естественно, это моё мнение, и оно может не совпадать с генеральной линией партии).<br />
<br />
<h3 style="text-align: left;">
1. Это не про потоки, это про асинхронность в одном</h3>
<div>
Т.е. основной смысл этой конструкции в том, чтобы занять основной поток, пока его часть ждёт какой-то асинхронной операции. Т.е. пока мы скачиываем файл, мы можем поделать ещё что-то, а не тупо ждать завершения. Идея здравая, но есть нюанс: <b>должно</b> существовать то, чем вам заняться. Например, если у нас клиентское приложение, мы в этом же потоке обработаем всякие события перерисовки, если однопоточное серверное, займёмся другим клиентом. Но если нам нечем заняться, то смысла в конструкции <b>нет</b>. Т.е. если мы сделали отдельный поток для пользователя и ждём когда скачается файл, чтобы что-то сделать. Мы не выиграем ничего от использования async/await. Просто будем использовать как небольшой синтаксический сахар с гигантcкой работой под капотом не по делу.</div>
<div>
<br /></div>
<h3 style="text-align: left;">
2. Неявная точка асинхронности</h3>
<div>
Когда мы вызываем асинхронный метод, мы не знаем, когда он реально перейдёт в асинхронность. Другими словами, возьмём стандартный подход: </div>
<div style="background: #f0f0f0; border: 1px dashed black; padding: 5px; whitespace: pre;">
Task.Factory.StartNew(DoSomething);</div>
<div>
<br />
Сразу после вызова данного метода, мы можем продолжать работу. А теперь глянем на async/await:<br />
<div style="background: #f0f0f0; border: 1px dashed black; padding: 5px; whitespace: pre;">
var t = DoSomething();<br />
..........<br />
await t;
</div>
<div>
<br />
В данном коде мы не знаем, когда реально дойдёт до нас управление, и будет ли реальная асинхронность. Фактически, мы вернёмся в метод, когда в DoSomething() будет встречен await, а когда это будет — зависит от реализации. Фактически, точка асинхронности слово await, но она находится внутри метода который мы <b>вызываем</b>, как результат, мы нам надо знать реализацию метода <i>DoSomething</i>, чтобы было всё прозрачно и понятно.<br />
В большинстве случаев, это не проблема, но когда начинаются проблемы, вот такие мелкие нюансы очень портят жизнь.<br />
Например, указано, что эксепшены пробрасываются на await, в результате можем получить код вида:<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiN8xB0efQsQ8KEfZrDMalJcS5QM2agZZWr_lqvTrwkwiT-G3PguGBSOL6JFUv-LGEgD65quQDKGNjsagepNueCD5Q8I0gzSo5pyw3HZVQ325AVQOap1ucsYPPlU6aeGE7TsFJTq8CeNLw/s1600/2015-11-15+13_02_20.png" imageanchor="1" style="clear: left; margin-bottom: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiN8xB0efQsQ8KEfZrDMalJcS5QM2agZZWr_lqvTrwkwiT-G3PguGBSOL6JFUv-LGEgD65quQDKGNjsagepNueCD5Q8I0gzSo5pyw3HZVQ325AVQOap1ucsYPPlU6aeGE7TsFJTq8CeNLw/s1600/2015-11-15+13_02_20.png" /></a></div>
У которого вначале уничтожится мир, потом будет всё хорошо, а потом вылетит эксепшен. На мой взгляд не очень очевидно.<br />
<br />
Вся эта неявность приводит к следующей проблеме:<br />
<br />
<h3 style="text-align: left;">
3. Легко завалить производительность, при этом всё будет работать</h3>
</div>
</div>
<div>
Как я уже указал, данная технология про асинхронность. Соответственно всё должно быть асинхронно, всегда и везде. А что будет, если мы где-то забудем асинхронность? Или не мы, а тот кто написал библиотеку для нас. Может быть он не очень хороший программист и допустил маааленькую ошибку и сделал операцию синхронной. Как вы думаете, что будет? Да ничего фатального! Просто пользователь будет ждать. Например, один await не в том месте, привёл к тому, что HTTP-proxy на async/await стала обрабатывать все запросы от пользователей по очереди. И это работало год. Заметили случайно, при генерации отчётов сайт переставал открываться. А всему виной — ошибка в коде, про который все забыли и который с точки зрения логики, работал замечательно.</div>
<div>
<br /></div>
<div>
Другие примеры, чтобы понять масштаб проблемы: делаем запрос по HTTP, и перед запросом решили порезолвить DNS, синхронно (ну не нашли асинхронный метод). Можем получить 2 секунды тормозов на ровном месте. Использовали бы явную асинхронность вызовом метода в отдельном потоке — проблем не было бы. Ещё вариант — логгер, давно написанный, отлаженный, пишет в файлик, асинхронной версии не имеет, никому не нужна. Всё работает годами. Потом кто-то на том же интерфейсе решил запилить логгирование в базу, результат — пока один пишет, все ждут (у нас ведь синхронная версия). Провал производительности.<br />
<br />
<i>Хотя про то, что всё будет работать, я погорячился. Когда писал этот пост выяснил, что легко получить Deadlock из-за повторного входа и контекстов синхронизации. Не буду пересказывать <a href="https://msdn.microsoft.com/en-us/magazine/jj991977.aspx">статью</a>, там на третьем примере всё достаточно просто и понятно рассказывается. </i></div>
<div>
<br /></div>
<div>
В результате, чтобы справиться с проблемой мы должны использовать везде асинхронные версии, и это нас приводит к следующей проблеме</div>
<div>
<br /></div>
<h3 style="text-align: left;">
4. async/await заразные</h3>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhAil4QrBNmEwvRn4R8F1bHMH3A0xOSKAMz2p40qmpEvOyxrgBJw_F77dHBMovnOE1QXNNNjaSi1KagS8y2ph5ZO1hcR8H_OjTPvY4Nc7UmexCsndD1t3g3z-GdbsC6lEGsPef4HcP6yM4/s1600/risovach.ru.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhAil4QrBNmEwvRn4R8F1bHMH3A0xOSKAMz2p40qmpEvOyxrgBJw_F77dHBMovnOE1QXNNNjaSi1KagS8y2ph5ZO1hcR8H_OjTPvY4Nc7UmexCsndD1t3g3z-GdbsC6lEGsPef4HcP6yM4/s1600/risovach.ru.jpg" /></a></div>
<div>
<br /></div>
<div>
Чтобы заиспользовать его, методу надо указать что он <i>async</i>. Если метод async, то вызывающий его метод должен использовать <i>await</i> и самому стать тоже <i>async.</i></div>
<div>
Т.е. весь код резко начинает обрастать <i>async</i>'ами, как снежный ком. Вырваться из данной западни можно двумя способами: <a href="http://force-net.com/2013/12/asyncawait.html">async void</a> (не используйте никогда!) или явным хаком с вызовом Wait у таска, возвращаемого <i>async</i> методом (что может привести к некоторым проблемам с контекстом синхронизации).</div>
<div>
В общем, для нормального использования async'ов, у нас должна быть вся библиотека, написанная на них, которая использует другие библиотеки, написанные тоже на них. Надо писать сразу весь код в этой концепции, добавление небольшого куска не даёт никакого профита. А раз это концепция, то хотелось бы явно выделять её. Например, я вызываю метод <i>DoSomething()</i>, в зависимости от того, async он или нет, при вызове будут абсолютно разные вещи. Но без поддержки IDE я никогда не у знаю об этом. Если я добавлю к сигнатуре async, позднее, код скомпилируется и будет работать как-то, но о проблемах я узнаю по факту, а не во время компиляции.</div>
<div>
<br /></div>
<div>
И это косвенно подводит нас к очередной проблеме</div>
<div>
<br /></div>
<h3 style="text-align: left;">
5. Reflection и кодогенерация</h3>
<div>
Reflection должен знать про то, что методы бывают async, т.к. от этого зависит логика их работы (банально проброс эксепшена о котором я уже упомянул вначале). Оверрайдить данные методы кодогенрацией (моё любимое занятие) тоже надо аккуратно, ибо просто для понимания, вызов метода:</div>
<div style="background: #f0f0f0; border: 1px dashed black; padding: 5px; whitespace: pre;">
public static async Task DoSomething()
{
Console.WriteLine("A");
await Task.Delay(1000);
}</div>
<div>
<br />
По факту превратится в что-то подобное:<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjwEXwZ4Mkgu8YPkrHXC0mQ3a4-B0p6aVYosOwI5PfhuYn2ygjeYLECDCHsyNeeeGbF24MkXWkHk20yP2r7eGYQZWDtH1-1HGuhSaoiNrVl_u54w7y38OgdDwVV0DVAg4aar9p1OkBXnag/s1600/2015-11-15+13_36_52.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjwEXwZ4Mkgu8YPkrHXC0mQ3a4-B0p6aVYosOwI5PfhuYn2ygjeYLECDCHsyNeeeGbF24MkXWkHk20yP2r7eGYQZWDtH1-1HGuhSaoiNrVl_u54w7y38OgdDwVV0DVAg4aar9p1OkBXnag/s1600/2015-11-15+13_36_52.png" /></a></div>
А реальная логика будет запрятана далеко, и будет выглядеть как-то так:<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgA3BqW8G8EteXNDctimiG3_LPTDcxjENubYYjFOH0j5NvJC_LznVzd-S9TM4cyfBimXfzEbkM6P0MBc8Zfxgk_P__Ig5eBePyRLwJbJYGKuTZT6SJU_rljo8dKkmNs6QOaHUC5EwI9mSg/s1600/2015-11-15+13_38_23.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="313" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgA3BqW8G8EteXNDctimiG3_LPTDcxjENubYYjFOH0j5NvJC_LznVzd-S9TM4cyfBimXfzEbkM6P0MBc8Zfxgk_P__Ig5eBePyRLwJbJYGKuTZT6SJU_rljo8dKkmNs6QOaHUC5EwI9mSg/s320/2015-11-15+13_38_23.png" width="320" /></a></div>
Согласитесь, не самый простой для понимания пример.<br />
А так как все эти async/await сделаны на уровне языка, а не MSIL, то среда ничего не знает про это, и тем же рефлекшеном достать это сложно. Что в связи с неявной точкой асинхронности может резко менять логику.<br />
<br />
Например, мы хотим добавить валидацию параметров метода через AOP, простой пример, они не должны быть null. Так вот, если мы это делаем кодогенрацией, мы пропустим слово async и добавим провеку в метод. Вызывающий код упадёт на вызове метода. Если же мы добавим проверку вручную, то на слове <i>await</i> (да, это мой любимый пример с эксепшенами, раз я его разобрал). При этом не обязательно использовать кодогенерацию, достаточно просто наследования.<br />
<br />
Т.е. если программист пишет код а асинками и авейтами и не использует хитрые средства, он может никогда не знать, что концептуально допустимы различные ситуации, потому что они просто не возникают. Но они возможны, и могут привести к нестабильной работе всего кода.<br />
<br />
<h3 style="text-align: left;">
Выводы</h3>
<div>
На мой взгляд, данная технология похожа на внутренности Perl'а. Для тех, кто не знает этот чудесный язык, поясню: смотришь примеры, видишь как всё просто и круто. Начинаешь углубляться в язык, чтобы понять как это работает и с какого-то момента становится очень страшно. Ты понимаешь огромную магию внутри всего этого. Ты понимаешь, что неверное движение может всё поломать, а работает только благодаря умным людям, которые написали кишки и программистам, которые не суют нос, куда не надо. И лишь спустя длительное время ты становишься таким умным человеком, и тебе становится всё понятно. Тебе спокойно. Так вот, я с async/await не стал таким умным человеком, но мне страшно, я уже могу всё сломать и я боюсь что кто-то из моей команды тоже может всё сломать, и не поймёт этого. И я не пойму. В результате всё будет работать не так как надо, а мы не будет знать, в чём проблема. А чтобы узнать, не достаточно статей "Async и await для чайников". Надо действительно погружаться в технологию. А оно мне не надо. Я не пишу приложения под <i>Windows 10</i>, а больше смысла-то и нет, хотя Microsoft толкает её в новые библиотеки, вполне можно прожить и без неё, без этого странного волшебства и магии.</div>
<br /></div>
</div>
forcehttp://www.blogger.com/profile/09205548202595699526noreply@blogger.com2