Mikrotik (RouterOS) + Wireguard

Вступление

Один из способом сделать доступным некоторые внутренние (домашние) сервисы из Интернета является VPN. Можно, конечно, отдельные порты опубликовать и через ssh, но для более полноценной связи лучше использовать другие решения. Я уже писал и про ZeroTier, и про OpenVPN, и получил упреки, что незаслуженно забыл про Wireguard…

Так или иначе, мне стало не хватать VPN клиента (в т.ч. и Wireguard) на отдельно стоящем серверочке, потребовалось связать (в данном случае с vNet в Azure, хотя это не принципиально) всю домашнюю сеть с несколькими ресурсами. И я решил, что пора уже сделать это через роутер, для полноценного site-to-site.

Хотя Keenetic и научился поддерживать Wireguard на новых прошивках, для старенькой Ultra я такой не нашел. С OpenWRT тоже не срослось (для Ultra II есть, а моя моедль старовата). Так что я решил, что пора проапгрейдиться. И, поскольку Mikrotik RouterOS выкатила бету 7 версии с Wireguard, я решил, что пора изучить это чудо.

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

Основные моменты

Взял я MikroTik hAP ac2. Модель старая, без излишеств, но все, что нужно, делает.

Хотя дела с Микротиками я раньше не имел, запустил его достаточно быстро. Были некоторые сложности с тем, что в настройке DHCP Server недостаточно установить Network, чтобы IP адреса начали раздаваться из этой сети. Оказалось, что есть еще и отдельный IP Pool. Но это мелочи. Так что довольно быстро я приступил к настройкам именно Wireguard.

Конечно же, ничего не заработало. Более того, «на той стороне» я даже не видел входящих пакетов.

Потом уже я обратил внимание, что в поле Endpoint указан только IP адрес, но отсутствует порт. Попробовал указать его явно, но обломался. Поле становится красным и не проходит валидацию.

Пошел разбираться с командной строкой. Для меня, в первый раз увидевшего RouterOS, там, конечно, не сахар. Более-менее, разобрался, конечно. Но как узнать, какие вообще параметры имеются, так и не понял. Ну т.е. до /interface wireguard peers я добрался. Даже про add догадался. Только вот при этом система спрашивала только interface, public-key и allowed-address. В общем, всякими переборами подобрал команду:

add allowed-address=192.168.66.128/25,10.10.0.0/16 endpoint=66.166.166.42:51820 \ interface=wg0 persistent-keepalive=30 public-key="многобукв="

Т.е. порт можно указать только через командную строку. Впрочем, все равно не заработало.

Напомню, что wg0 был создан ранее через web-интерфейс. Или WinBox, не помню. Он чуток получше, чем веб-интерфейс, но порт тоже не давал указать. И тут до меня дошло, что на обычном Linux я ведь еще IP адрес своего локального хоста указываю. А тут его не задавал. Заработало только после:

/ip address add address=192.168.66.253/24 interface=wg0 network=192.168.66.0

Собственно все 🙂 В сети есть инструкции, как установить Wireguard на Mikrotik с OpenWRT. Но как по мне, это извращение. А вот поднять его в родном RouterOS можно за несколько минут. Когда уже знаешь, как. Работает прекрасно, вообще без нареканий.

P.S. Адреса я, конечно, менял. Но allowed-address=192.168.66.128/25 и add address=192.168.66.253/24 не ошибка. Просто у меня к двум серверам подключение. Половина сети класса С на один сервер, половина на другой.

P.P.S. Почему Wireguard, а не OpenVPN? Например, производительность:

https://blog.entrostat.com/openvpn-vs-wireguard-network-performance-tests/

А еще простота настройки и кое-что по мелочи.

ссылка на оригинал статьи https://habr.com/ru/post/521468/

Продолжаем чистить память с three.js

Введение

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

Основная часть

Изучая различные примеры сборки мусора на three.js заинтересовал подход, предложенный на threejsfundamentals.org. Однако, реализовав предложенную конфигурацию и завернув в this.track() все материалы и геометрию, выяснилось, что при загрузке новых сцен нагрузка на GPU продолжает расти. Более того, предложенный пример некорректно работает с EffectComposer и другими классами для постобработки, поскольку в этих классах track() использовать нельзя.
Решение с добавлением ResourceTracker во все используемые классы не привлекает, по очевидным причинам, поэтому решил дополнить метод очистки упомянутого класса. Вот некоторые приемы, которые были использованы:

Прием 1. Грубый.

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

Прием 2. Долгий.

Открыв код используемого класса (например AfterimagePass, который можно найти на гитхабе three.js) смотрим, где создаются ресурсы, которые нам нужно очищать, чтобы поддерживать число геометрий и материалов в требуемых рамках.
this.textureComp = new WebGLRenderTarget( window.innerWidth, window.innerHeight, { ... }
То что надо. Согласно документации, WebGLRenderTarget имеет функцию dispose, на которой завязана очистка памяти. Получаем что-то вроде

class Scene { //...     postprocessing_init(){ // В нашем классе         this.afterimagePass = new AfterimagePass(0);         this.composer.addPass(this.afterimagePass);     } //... } //...  class ResourceTracker { //...     dispose() {     //...     sceneObject.afterimagePass.WebGLRenderTarget.dispose();     //...     } } 

Прием 3.

Работает, но код для очистки в таком случае раздувается. Попробуем использовать знакомый нам из предыдущей статьи подход. Напомню, в ней мы реализовали метод disposeNode(node), в котором ресурс перебирался на поиск того, что можно очистить. disposeNode() может выглядеть как-то так:

disposeNode(node) {             node.parent = undefined;             if (node.geometry) {                 node.geometry.dispose();             }             let material = node.material;             if (material) {                 if (material.map) {                     material.map.dispose();                 }                 if (material.lightMap) {                     material.lightMap.dispose();                 }                 if (material.bumpMap) {                     material.bumpMap.dispose();                 }                 if (material.normalMap) {                     material.normalMap.dispose();                 }                 if (material.specularMap) {                     material.specularMap.dispose();                 }                 if (material.envMap) {                     material.envMap.dispose();                 }                 material.dispose();             }         } else if (node.constructor.name === "Object3D") {             node.parent.remove(node);             node.parent = undefined;         }     }

Отлично, теперь возьмем все дополнительные классы, которые мы применяли, и дополним наш ResourceTracker:

dispose() {     for (let key in sceneObject.afterimagePass) {         this.disposeNode(sceneObject.afterimagePass[key]);     }     for (let key in sceneObject.bloomPass) {         this.disposeNode(sceneObject.bloomPass[key]);     }     for (let key in sceneObject.composer) {         this.disposeNode(sceneObject.composer[key]);     } }

Итоги

В результате всех этих действий я значительно повысил ФПС и уменьшил нагрузку GPU в своем приложении. Возможно, я некорректно применял ResourceTracker, однако он в любом случае не помог бы в работе с дополнительными классами. Про то, что перебор EffectComposer через наш disposeNode(node) влияет на число текстур, оказывающихся в памяти я нигде не видел (однако так оно и есть). Этот вопрос следует рассмотреть отдельно.
Для сравнения предыдущая версия останется по старому адресу, а новую можно будет посмотреть отдельно. Проект в некотором виде есть на гитхабе.

Буду рад услышать ваш опыт по работе с аналогичными проектами и обсудить детали!

ссылка на оригинал статьи https://habr.com/ru/post/521470/

Удаленная отладка Spring Boot приложений (IntelliJ + Eclipse)

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

System.out.println («Теперь мы находимся здесь, а переменная X is =» + x); 

делает код вашего приложения довольно громоздким и его выполнение занимает много времени. К счастью в Java есть зрелая отладочная экосистема. Это позволяет нам удаленно отлаживать приложения Spring Boot и анализировать его рабочий процесс на удаленном сервере / облаке.

Чтобы показать вам, насколько на самом деле проста удаленная отладка с помощью Java, я буду использовать приложение Spring Boot 2.3, работающее на Java 11. Я разверну приложение как в виде контейнера Docker, так и с помощью старой школы java -jar… way. Наконец, вы узнаете, как удаленно отлаживать интерфейс REST с помощью IntelliJ IDEA (2019.1) и Eclipse (2019-03).

Настройка проекта

Для приложения я выбрал простое приложение Spring Boot 2.3. Он включает в себя встроенную базу данных H2 и JPA для обслуживания книг, которые генерируются случайным образом при запуске.

Класс контроллера, который я позже буду использовать для отладки, выглядит следующим образом:

@Slf4j @RestController @RequestMapping("/api/books") public class BookController {      @Autowired     private BookRepository bookRepository;      @GetMapping     public List<Book> getAllBooks() {         log.info("Retrieving all available books");         List<Book> allAvailableBooks = bookRepository.findAll();         return allAvailableBooks;     }      @GetMapping("/{id}")     public Book getBookById(@PathVariable("id") Long id) {         log.info("Retrieving book with id: {}", id);         Optional<Book> book = bookRepository.findById(id);          if (book.isEmpty()) {             throw new BookNotFoundException("Can't find book with id: " + id);         }          return book.get();     } }

Остальная часть приложения опущена, но вы можете найти весь исходный код на GitHub.

Развертывание

Чтобы иметь возможность удаленной отладки вашего Java-приложения Spring Boot, мы должны передать в JVM следующие аргументы:

-agentlib:jdwp=transport=dt_socket,server=y,suspend=n,address=*:8000

Указанный порт в этом аргументе позже используется для подключения наших IDE к работающей JVM и не должен совпадать с портом приложения.

Аргумент может быть передан в JVM с помощью следующей команды:

java -agentlib:jdwp=transport=dt_socket,server=y,suspend=n,address=*:8000 -jar target/remote-debugging-spring-boot-application.jar

При использовании Docker мы можем добавить этот аргумент в нашу ENTRYPOINT и просто нужно отобразить дополнительный порт при запуске Docker контейнера:

FROM openjdk:11-jdk-slim VOLUME /tmp COPY target/remote-debugging-spring-boot-application.jar app.jar ENTRYPOINT ["java","-agentlib:jdwp=transport=dt_socket,server=y,suspend=n,address=*:8000","-jar","/app.jar"]

docker run -d -p 8080:8080 -p 8000:8000 --name debug-me debug-me

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

Удаленная отладка приложений Spring Boot с помощью IntelliJ IDEA
Удаленная отладка приложения Spring Boot с IntelliJ IDEA требует, чтобы вы открыли проект (исходный код) с IntelliJ. Затем мы можем перейти к редактированию конфигурации… в правом верхнем углу рядом с зеленой кнопкой запуска:

Выберите создание новой конфигурации Run / Debug, нажимая кнопку +, и выберите Remote. Старые версии IntelliJ могут иметь разные названия, такие как Remote Debugging (удаленная отладка), Debugging (отладка) и т.д.:

Затем введите имя для выбранной вами конфигурации удаленной отладки и укажите порт и хост, к которому вы хотите подключиться (в нашем примере это порт 8000). Убедитесь, что вы выбрали правильный проект для Use module classpath (Использовать classpath модуля) и нажмите Apply или Ok:

Теперь отметьте в исходном коде строку, которую вы хотите отладить, и запустите конфигурацию удаленной отладки Remote debugging configuration(убедитесь, что ваше приложение уже запущено):

Теперь попробуйте получить доступ к конечной точке (http://localhost:8080/api/books в этом примере) с помощью браузера или клиента API, и вы сможете отладить указанную часть вашего приложения в вашей IDEA:

Чтобы остановить удаленную отладку приложения, просто нажмите красную кнопку остановки.

Удаленная отладка приложений Spring Boot в Eclipse

Для Eclipse вы также сначала должны импортировать проект, а затем перейти к пункту меню Run -> Debug Configurations:

ссылка на оригинал статьи https://habr.com/ru/post/513520/

Качественный аутсорсинг от А до Ё: работаем с типовыми слабостями

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

А) Дорого.

Целесообразен ли аутсорсинг вообще? Для некоторых компаний внутренний сервис будет дешевле. Например, для таких, в которых работа похожа на «конвейер». Сотрудники таких компаний всегда знают, что делать и как делать. Для этого у них прописаны инструкции и процессы, установлены стандарты. В любой предметной области работает от 3 человек и каждый может заменить другого. Если в вашей компании выстроен такой «конвейер», то внутренний сервис – ваш выбор. Сэкономите компании деньги.
Если «конвейера» нет или же масштаб задач не позволяет его построить, то на помощь приходит аутсорсинг. Вот почему это будет выгоднее:

  1. Гибкое распределение специалистов между аналогичными задачами и клиентами. Специалист A эффективно справляется с задачами типа B. Как только он справился с задачей B1, отправляете его решать B2. Так вы экономите время и деньги компании на поиске или обучении другого специалиста.
  2. Обеспечение высокой загрузки специалистов. Это не значит, что сотрудники будут пухнуть от нагрузки. Они будут знать, что им делать сейчас и что им делать потом. Сотрудники даже смогут с чистой совестью прерываться на чай или медитацию, а потом возвращаться к работе. Даже если что-то пойдёт не так и вспыхнет срочная задача, вам не нужно будет сломя голову искать специалиста, который её решит – у ваших специалистов будет на это время.
  3. Возможность найма сотрудников из регионов, в которых дешевле жить. В таком случае и стоимость услуг специалиста будет дешевле.
  4. Повторное использование процессов, тулов, стандартов и практик.

Учитывая эти выгоды, компании-поставщики услуг аутсорсинга предпочитают долгосрочные контракты. Обе стороны не расточают ресурсы на выстраивание процессов, организацию людей, и согласования. Здесь главное не допустить застоя в сервисе.
С другой стороны может имеет смысл менять поставщика каждый год, каждый раз добиваясь снижения цены? Оба варианта являются крайностями.

Б) Долго.

Завести нового пользователя – 3 дня. Сбросить пароль – 5 дней. Дать права, как у Иванова, но с ограниченным доступом – 10 дней. Знакомые ситуации? Мне – да. Когда простые задачи решаются так долго – хочется не работать, а биться головой об мягкую стену. Чем больше висит невыполненных задач, тем хуже. Можно ли заранее измерить этот аспект? Можно.

В) Негибко, бюрократично.

Случаются творческие задачи, которые нужно решать креативно. Только вот «давайте сделаем» разбивается о «надо согласовать». Начинаются перестраховки, тестирования, «консультации» и т.д. Решение задачи превращается в долгую и унылую песню. Как пишут в лучших практиках — всё должно быть обосновано ценностью для бизнеса. Очень расплывчатая формулировка, т.к. непонятно что важнее: ценность в долгосрочной перспективе (минимальный риск, доступность системы) или результат здесь и сейчас.
К сожалению универсального решения нет, всё зависит от конкретных людей. Исполнители должны вникать в ситуацию, а их руководители должны выстроить соответствующую культуру производства и отношений. Тогда проще прийти к пониманию. Аргументом в защиту аутсорсинга является более развитая дисциплина.

Г) Непрофессионально.

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

Д) Непрозрачно.

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

Е) Нет доверия (небезопасно).

Когда видишь сотрудников вживую, доверяешь им больше. В аутсорсинге, сотрудники непонятно где и как выглядят. Как довериться и дать доступ тем, кого не знаешь и не видишь? Мы рекомендуем ехать к поставщику и знакомиться. Приезжаете к нему и оцениваете как он работает, проводите свой Due Diligence.
Обязательно выясните:

  1. Как поставщик защищает себя от разного рода случайностей?
  2. Как поставщик контролирует работу сотрудников?
  3. Выстроена ли у поставщика система контрольных процедур и лучших практик? Насколько они разумны?
  4. Наблюдается ли микро-менеджмент?
  5. Умеют ли сотрудники работать самостоятельно?
  6. Готовы ли сотрудники отвечать за результат своей работы?

Ё) Не клиентоориентированно.

Мы много сказали про выстроенные процессы и автоматизацию, а что насчёт живых людей? Выполняются ли задачи по принципу «претензии к пуговицам есть?». Несмотря на внедрённую автоматизацию, установленные процессы, поддержанные «правильными» системами учета тикетов, несмотря на это всё – в сервисе должен остаться человеческий фактор, который сделает сервис индивидуальным под конкретного клиента.
Речь идет о core-команде, которая в значительной степени (>60%) выделена на этого клиента, знает его особенности. Core-команда способна предоставлять целостный сервис, понимая чем живёт компания. Здесь требуется раскрыть человеческий потенциал, перестать фокусироваться только на приложениях или ИТ-инфраструктуре, постоянно адаптироваться к изменяющейся действительности, перестать делить людей на «ваших» и «наших». Вместо этого надо учить людей широкому кругозору в сочетании с специализацией в конкретной области (T-shaped people).

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

ссылка на оригинал статьи https://habr.com/ru/company/accenture/blog/521428/

Я был неправ. Будущее за CRDT

Несколько недель назад я посмотрел презентацию Мартина Клеппмана про его подход к редактированию в реальном времени через CRDT и ощутил жгучее отчаяние. Его подход хорош настолько, что превосходит всю мою работу за последнее десятилетие, и места в будущем ей уже не видать.

Но начнем сначала.

В 2010 году я работал в Google Wave, где мы пробовали создать совместные редактируемые пространства для замены электронной почты, Google Docks, форумов, мгновенных сообщений и многих других однозадачных приложений. Среди моих инструментов мне особенно нравится среда общего назначения, нигде более как в Wave не сформулированный в то время функционал. В отличие от большинства других инструментов, среда общего назначения не навязывает собственный рабочий процесс, благодаря чему через нее можно планировать праздники, создавать вики-проекты, играть с друзьями в настольные игры, назначать рабочие собрания и много чего еще.

Изнутри совместное редактирование Wave работает поверх операционного преобразования (Operational Transform, OT), и в то время им пользовались уже не один день: наш алгоритм основывался на докладе Jupiter 1995 года. Для каждого документа алгоритм хранит отдельный хронологический список изменений, «Набрать H в позиции 0», «набрать i в позиции 1» и так далее. В большинстве случаев пользователи меняют последнюю версию документа, и лог выглядит как последовательность изменений, однако при совместном редактировании мы сталкиваемся с одновременными правками.

В таком случае первая попавшая на сервер правка записывается как обычно, а следующая, если она оказывается устаревшей, сравнивается с логом событий для определения изначальных целей пользователя. (Чаще всего все сводится к обновлению позиций символов.) Затем алгоритм, с видом якобы «именно такого результата и хотел пользователь», добавляет новую операцию, будто git-rebase в реальном времени.

С закрытием Google Wave я перенес OT модель в ShareJS. В то время node ощущался новым и странным, и если я правильно помню, ShareJS я запустил еще до релиза npm. Простой совместный редактор затребовал всего лишь тысячу строк кода, и на демонстрации демо-версии я совместно отредактировал документ в браузере и в приложении.

По своей сути, OT это приукрашенный цикл for() с несколькими функциями помощи для обновления смещения символов. На практике OT прост, легко понятен, быстро вводится в работу и работает отлично. (10-100 тысяч операций в секунду в неоптимизированном javascript, 1-20 миллионов в оптимизированном C). Лог событий может тратить памяти больше обычного, однако при желании его можно урезать, правда объединить особо давние правки не получится. Для глобального назначения операций потребуется централизованный сервер, однако у большинства систем уже есть такой сервер или база данных, не правда ли?

Централизованные серверы

Значимая проблема OT это зависимость от централизованного сервера. Задумывались ли вы когда-нибудь, почему при разрешении доступа к документу Google Docs через социальные сети вы сталкивались со странным сообщением вида «Этот документ перегружен и его редактирование отключено»? Причина (на мой взгляд) в следующем: когда вы открываете документ, для обработки всех его правок выбирается один конкретный сервер, и когда на документ набрасывается толпа пользователей, системе приходится очень постараться чтобы не перегрузить сервер.

Обойти эту проблему можно несколькими способами: помимо подокументного шардинга (как в Google Docks) можно вносить правки через цикл retry в обход транзакций базы данных, благодаря чему проблема сериализации взваливается на плечи все той же базы данных (в такой манере работают Firepad и ShareDB).

Тем не менее, OT не идеален. Мы хотели заменить электронную почту на Wave, но почта поддерживает объединения, одна цепочка писем может растягиваться на множество компаний, и все это как-то успешно работает. К тому же, в отличие от сообщений в Facebook, электронную почту можно разослать упомянутым в графе «копия» компаниям. Если мы хотим чтобы Wave заменил почту, ему также потребуется функционал передачи сообщений без выхода во внешнюю сеть, например, когда я отсылаю письмо своему коллеге за соседним столом. Но как можно реализовать все это поверх OT? У нас кое-как получилось настроить такой процесс, но он оказался слишком сложным и полным ошибок: мы создали схему, в которой каждый wave протокол настраивал дерево wave серверов для передачи операций в обе стороны, но она так до конца и не заработала. Чуть менее десяти лет назад на Wave Protocol Summit я дал презентацию по вопросу создания и настройки подобной сети, но, несмотря на всю мою подготовку и все предварительные проверки, строгое следование каждому шагу на самой презентации провалилась, и сеть так и не заработала. Я до сих пор не знаю, почему это произошло, но что бы это ни были за ошибки, в общедоступной версии их вряд ли когда-либо исправляли, слишком уж все это было сложно.

Взлет CRTD

Как я уже упомянул, основной алгоритм Wave был создан довольно давно, в 1995 году, и я даже не припомню, чтобы у меня в то время дома был интернет. С тех пор исследователи неустанно трудились над улучшением работы OT, и в самом многообещающем направлении они пользуются CRTD (Conflict-Free Replicated data types). Такой подход несколько отличается от обычного и позволяет редактировать файлы в реальном времени без необходимости центрального сервера. В презентации Мартина их работа описана лучше, чем я бы смог о них рассказать, поэтому я опущу детали.

Люди не первый год спрашивают мое мнение по поводу CRTD, и всегда мой ответ звучит в следующем ключе:

Они аккуратны и я рад, что люди над ними работают, однако:

  • Они медленные. Очень медленные. Например, на обработку сессии по редактированию академической статьи в 100 килобайт в реальном времени одним пользователем Delta-CRTD тратит почти шесть часов. (Тесты: внимание на B4.)
  • Из-за особенностей работы CRTD объем документов растет бесконтрольно, например, для отображения документа в 100 килобайт automerge master занимает 83 мегабайта памяти. Эти данные просто лежат на диске, удалить их, похоже, нельзя, и для редактирования их необходимо загружать в память. (Потенциально automerge может вырасти до 1.1 гигабайта памяти.)
  • Годами присутствующий в OT функционал отсутствует в CRDT, например никто еще не сделал CRDT с поддержкой /object move/ (перенос чего либо из одной части JSON дерева в другую). Такие процедуры требуются для приложений типа Workflowy, и OT с ними отлично справляется.
  • CRDT сложны сами по себе и рассуждать о них непросто.
  • У вас, по всей видимости, уже есть централизованный сервер/база данных.

Со всей своей критикой я игнорировал CRDT, но тем самым игнорировал соответствующую литературу, и к своему удивлению пропустил тихое и незаметное улучшение CRDT. В своей презентации (она более чем стоит вашего внимания) Мартин адресует основные моменты:

  • Скорость: С использованием современных CRDT (Automerge / RGA или Y.js / YATA) применение операций становится доступным через логарифмический [log(n)] запрос. (Подробнее об этом ниже.)
  • Размер: Столбчатое кодирование Мартина в состоянии хранить текстовый документ всего лишь в полутора-двукратном превышении размера по сравнению с исходным содержимым, о чем сам Мартин говорит в презентации на 54-ой минуте. В automerge соответствующий код еще не ввели, однако эти идеи уже есть вY.js, благодаря чему Y.js хранит наш 100 килобайтный документ как 160 килобайт на диске или 3 мегабайта в памяти. Прогресс налицо.
  • Функционал: Есть как минимум теоретические возможности по реализации функционала через перемотку и проигрывание, однако этим еще никто не занялся.
  • Сложность: Я полагаю, средний CRDT ненамного превысит аналогичную реализацию через OT. Мартин сумел создать крохотную, медленную версию automerge всего на сотню строк.

Аргументация за скорость меня не убедила, поэтому для проверки идеи я самостоятельно реализовал и протестировал CRDT на Rust через B-дерево с использованием идей из automerge. В ней недоставало функционала (удаление символов, конфликты), однако она оказалась в состоянии обрабатывать 6 миллионов правок в секунду. (Каждая итерация проводила 2000 правок в пустой документ двумя чередующимися пользователями, что в целом занимало по 330 микросекунд, или 6.06 миллиона правок в секунду.) Таким образом, CRDTs действительно улучшились и разница в скорости между ними и OT теперь даже меньше чем между Rust и Javascript.

Все эти правки уже долгое время лежат в разделе «в скором будущем» в ветке производительности automerge, но в конце концов automerge не единственный CRDT. Y.js показывает себя достойно и в своих тестах с легкостью обходит текущую версию automerge. Ему недостает интересующего меня функционала, но в целом конечно проще поправить существующую реализацию, чем создавать новый алгоритм.

Изобретая будущее

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

В наши дни JSON и REST используются повсеместно. Допустим, через 15 лет так же повсеместно будет использоваться совместное редактирование в реальном времени. Каков будет аналог у JSON в плане совместного редактирования для легкого переноса в свой проект? В этом славном будущем нам потребуется высококачественная реализация CRDT, поскольку для некоторых приложений OT попросту не будет работать, создать через него версию GIt в реальном времени или простую вариацию Google Wave уже не получится. Но если у нас уже есть хорошая реализация CRDT, нужна ли нам еще и реализация OT? Я считаю, что нет, ведь перенести весь функционал из OT в CRDT не составит труда (включая, между прочим, операции урезания), тогда как обратное неверно. Умные люди со мной не согласны, но, по моему мнению, при наличии у нас хорошего, быстрого CRDT для каждого языка, нужда в OT отпадет полностью.

Одно из преимуществ OT заключается в удобной реализации в централизованных системах – как и большинство современных приложений – но так же хорошо в них реализуются и распределенные алгоритмы. (Взгляните на Github например.) На мой взгляд, высококачественный CRDT на wasm окажется быстрее, чем реализация OT на JS. А если вас беспокоят только централизованные системы, помните: ограничения OT привели Google к проблемам скалирования в Google Docs.

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

Что дальше

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

С философской точки зрения, когда я редактирую документ в Google Doсks, мой компьютер спрашивает у Google разрешение на редактирование файла (поскольку если по какой-то причине сервер говорит «нет», я теряю все мои правки). Для сравнения, при git push в github я всего лишь оповещаю github о правках в моем коде. Мой репозиторий все еще принадлежит мне, как и каждый бит данных и железо на котором они находятся, и именно так должны работать мои приложения. Благодаря таким людям как Мартин мы теперь знаем, как сделать хорошие CRDT. Однако до принятия local-first приложений за основу придется написать еще не одну строчку кода.

В общем, пришло время прощаться с операционным преобразованием. Мы прекрасно провели это время вместе, из всего мной написанного код операционного преобразования был одним из самых непростых и интересных. Ты умен и удивителен, OT, но CRDT может то, что ты не сможешь никогда. И я нужен CRDT. Я думаю, за несколько хороших реализаций мы сможем достичь чего-то по-настоящему особенного.

Я оплакиваю весь мой вложенный за эти годы в OT труд, но OT более не вписывается в мое видение будущего. CRDT позволит нам проще и быстрее пересоздать Wave и создавать приложения с отношением к пользователям как к цифровым гражданам, а не цифровым крестьянам. И это важно.

Настало время создавать.

ссылка на оригинал статьи https://habr.com/ru/company/dcmiran/blog/521288/