MCP

среда, 10 октября 2018 г.

WireGuard

Сегодня Роскомнадзор в очередной раз забанил подсеть Амазона (или хостер пофиксил "проблему"), в общем пришлось заворачивать всё в VPN, что привело к весьма печальной скорости работы сервера, т.к. VPN был далеко.

Решил поднять его поближе, в Azure, а т.к. в нём весьма грустно с фаирволлом, решил попробовать новый хипстерский VPN под названием WireGuard. Правда потом оказалось, что VPN всё-таки можно было попробовать устроить, но у меня оказалась неправильная виртуалка, в которой всё делается через жопу. Надо было создавать другую и гуглить как в азуровом фаирволле сделать проброс чего-то отличного от TCP и UDP.

В общем, WireGuard позиционируется как простой и правильный VPN, без кучи опций и настроек. Просто работает. И в целом да, только оказалось, что это нифига не VPN в привычном понимании (хотя технически да, и виртуальная, и частная, и сеть).

Что хочется получить от VPN:

  • Стабильной работы (у всех существующих решений всё плохо)
  • Простой конфигурации (PPTP рулит)
  • Безопасности (SSTP, L2TP/IPSec, OpenVPN)
  • Выдачи IP адресов (все умеют)
  • Выдачи маршрутов (у всех очень грустно)
  • Выдачи доп. настроек (ещё хуже)
При этом, стоит напомнить про такую технологию как IPSec, которая не является туннелем сама по себе, т.е. не приносит дополнительных интерфейсов и IP-адресов, а просто безопасно заворачивает IP пакеты в себя. Т.е. идеально подходит для связи Server-Server или Сеть-Сеть через конкретные шлюзы. Для динамики и NAT подходит весьма плохо.

И вот тут вылезает WireGuard. Что же он делает?
  • Создаёт отдельный сетевой интерфейс
  • На него руками необходимо назначить IP (wat?)
  • Клиент и сервер в целом не разделяются (привет IPSec), они равнозначные пиры (peers), но в целом можно использовать концепцию клиент-сервер, просто сделав определённые настройки
  • Если нужно устроить VPN, то можно сделать это ручками через iptables и маскарад
  • Каждый пир определяется парой ключей (публичным и приватным), так что заранее на сервере никого не добавить (можно нагенерить ключей, но как-то коряво выглядит)
  • По умолчанию роуты связаны со списком разрешённых IP'шников, т.е. делаются на клиенте, проброса нет
Так что получается, что это какой-то упрощённый IPSec, но с отдельным интерфейсом и IP-адресом (без добавления, думаю можно извернуться, но уже не очень дефолтная конфигурация). Т.е. использовать его как VPN — можно, но очень фигово (хотя если для себя делаете, то вполне норм). И с VPN'ами по-прежнему всё тухло, а WireGuard оказался каким-то странным созданием.

Но с другой стороны, настраивается он элементарно и начинает сразу же работать. Это ему плюс. Только имейте в виду, что конфигурация через wg-quick и ip работает по-разному, через ip можно сделать гораздо больше и и точнее.



вторник, 27 февраля 2018 г.

Хранение данных в памяти

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

Для начала скажу, что сам я, пока так и не реализовал эту идею в полной мере, были продукты, где это активно использовалось, но в основном, продукты, за которые я ответственен работают достаточно быстро и так, так что как-то и не требуется усложнение и смена парадигмы. Но, когда-нибудь, обязательно попробую!

Почему не стоит полагаться на кеш SQL-сервера?

Потому что он построен именно на модели кеша для данных, и в него можно попадать или не попадать. Конечно, в MS SQL есть OLTP таблицы, которые хранятся в памяти, но они больше для очень активных данных, и вообще это на уровне SQL, причём MS SQL. А без них — используются стандартные алгоритмы для поиска, оптимизированные для данных, находящихся на диске, как результат, необходимый кеш для очень 100% попадания в память многократно превышает размер реальных данных.

Я же предлагаю забить на это, и максимально хранить все данные из базы в памяти (загружать их из базы на старте, или ленивым образом).

Что это даёт?


  • Резко упрощается внешняя логика пользователя. Во многих случаях можно отказаться от джойнов, если нет дополнительной фильтрации. Например, классический джойн с данных с пользователем (например, вывести автора данных) можно заменить на выборку данных и доставание всех пользователей по ID. Это очёнь дешёво
  • Можно использовать очень тупые алгоритмы, и это всё равно будет очень быстро. Фуллскан 10000 записей почти незаметная вещь, а если надо будет ещё быстрее — всегда можно будет прикрутить "индекс" в виде словарика
  • Можно избавиться от отдельного кеша для тяжёлых данных, ибо и так всё в памяти
  • Тупо быстрее из-за отсутствия запросов к внешнему SQL-серверу
  • Легко хранить данные, которые тяжело забирать из SQL (объект с зависимыми детишками, слабо-структурированные документы, JSON/XML поля)
  • При этом, если приложение написано с использованием LINQ, то местами можно вообще не заметить разницы между базой и памятью (если грамотно спланировать архитектуру приложения)

 А в память влезет?

А вот это как раз главный вопрос, который всех останавливает и которого все боятся. И из-за этого, всё так и останавливается на уровне идей. Но давайте прикинем необходимое количество памяти.
  • База данных в 20ГБ содержит где-то 20ГБ данных (логично, да?), в памяти это будет занимать примерно столько же. Найти подходящий сервер — не так уж и сложно. Естественно, базы в 100МБ вообще влезут в память без проблем
  • Очень часто в "больших" базах большой объём занимают всякие полумёртвые данные — журналы, результаты импорта, файлы, подписанные данные, акксесс логи... Эти данные нужны очень редко, их можно не хранить в памяти, тем самым кардинально снизив объём "реальных" данных
  • Многие данные нужны только в небольшом количестве. Например, у вас есть 10000 пользователей в системе, но активны только 1000, тут можно использовать какой-нить LRU кеш и не держать в памяти все объекты, а только активные. Опять же, очень сокращает необходимый объём памяти
  • Ну и для реально огромных баз данных можно уж держать в памяти только специально выделенные объекты (например, справочники). Хотя, с такими объёмами у вас будет проблем побольше чем просто держать в памяти

Как реализовать?

Поскольку я ещё не делал это в полном виде, то могу только предположить следующие варианты:
  • Собственный кеш класса, ратающего с сущностью (e.g. UserManager), он сам решает, что и как кешировать. Проблемы в куче аналогичного кода в разных классах и сложность с инвалидацией. Плюсы: в каждом конкретном случае можно использовать самые эффективные варианты
  • Мемоизация и автомемоизация методов. Плюсы: очень упрощается код, минусы: сложно инвалидировать и оптимально использовать данные. 
  • Обёртка над ORM (или использование встроенных средств типа Second Level Cache), которая сделает всё сама. Проблемы: сложно в реализации и конфигурировании. Плюсы: полная прозрачность в использовании со стороны кода

Краткий итог

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