Эволюция архитектуры торгово-клиринговой системы Московской биржи. Часть 1



    Всем привет! Меня зовут Сергей Костанбаев, на Бирже я занимаюсь разработкой ядра торговой системы.

    Когда в голливудских фильмах показывают Нью-Йоркскую фондовую биржу, это всегда выглядит так: толпы людей, все что-то орут, машут бумажками, творится полный хаос. У нас на Московской бирже такого никогда не было, потому что торги почти с самого начала ведутся электронно и базируются на двух основных платформах — Spectra (срочный рынок) и ASTS (валютный, фондовый и денежный рынок). И сегодня хочу рассказать об эволюции архитектуры торгово-клиринговой системы ASTS, о различных решениях и находках. Рассказ будет длинный, так что пришлось разбить его на две части.

    Мы одна из немногих бирж мира, на которых проводятся торги активами всех классов и предоставляется полный спектр биржевых услуг. К примеру, в прошлом году мы занимали второе место в мире по объёму торгов облигациями, 25 место среди всех фондовых бирж, 13 место по капитализации среди публичных бирж.



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

    Немножко истории


    В 1994 году на Московской межбанковской валютной бирже (ММВБ) была запущена австралийская система ASTS, и с этого момента можно отсчитывать российскую историю электронных торгов. В 1998 году архитектуру биржи модернизировали ради внедрения интернет-трейдинга. С тех пор скорость внедрения новых решений и архитектурных изменений во всех системах и подсистемах только набирает обороты.

    В те годы биржевая система работала на hi-end железе — сверхнадёжных серверах HP Superdome 9000 (построенных на архитектуре PA-RISC), у которых дублировалось абсолютно всё: подсистемы ввода-вывода, сеть, оперативная память (фактически, был RAID-массив из RAM), процессоры (поддерживалась горячая замена). Можно было поменять любой компонент сервера без остановки машины. Мы полагались на эти устройства, считали их фактически безотказными. В роли операционной системы выступала Unix-подобная система HP UX.

    Но примерно с 2010 года возникло такое явление, как high-frequency trading (HFT), или высокочастотная торговля — попросту говоря, биржевые роботы. Всего за 2,5 года нагрузка на наши серверы увеличилась в 140 раз.



    Выдерживать такую нагрузку со старой архитектурой и оборудованием было невозможно. Нужно было как-то адаптироваться.

    Начало


    Запросы к биржевой системе можно разделить на два типа:

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



    Схематично ядро системы можно разделить на три уровня:

    • Клиентский уровень, на котором работают брокеры, клиенты. Все они взаимодействуют с серверами доступа.
    • Серверы доступа (Gateway) — это кэширующие серверы, которые локально обрабатывают все информационные запросы. Хотите узнать, по какой цене сейчас торгуются акции «Сбербанка»? Запрос уходит на сервер доступа.
    • Но если вы хотите купить акции, то запрос идёт уже на центральный сервер (Trade Engine). Таких серверов по одному на каждый вид рынка, они играют важнейшую роль, именно ради них мы и создавали данную систему.

    Ядро торговой системы представляет собой хитрую in-memory базу данных, в которой все транзакции — это биржевые транзакции. База была написана на С, из внешних зависимостей имелась только библиотека libc и полностью отсутствовало динамическое выделение памяти. Чтобы уменьшить время обработки, система запускается со статическим набором массивов и со статической релокацией данных: сначала все данные на текущий день загружаются в память, и дальше обращений к диску не выполняется, вся работа ведётся только в памяти. При запуске системы все справочные данные уже отсортированы, поэтому поиск работает очень эффективно и занимают мало времени в runtime. Все таблицы сделаны с интрузивными списками и деревьями для динамических структур данных, чтобы они не требовали выделения памяти в runtime.

    Давайте вкратце пробежимся по истории развития нашей торгово-клиринговой системы.
    Первая версия архитектуры торгово-клиринговой системы была построена на так называемом Unix-взаимодействии: применялись разделяемая память, семафоры и очереди, а каждый процесс состоял из одного потока. Этот подход был широко распространён в начале 1990-х.

    Первая версия системы содержала два уровня Gateway и центральный сервер торговой системы. Схема работы была такая:

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

    Фактически, здесь описана репликационная модель, в которой Gateway полностью повторял действия, выполняемые в торговой системе. Отдельный канал репликации обеспечивал один и тот же порядок исполнения транзакций на множестве узлов доступа.

    Поскольку код был однопоточным, для обслуживания множества клиентов использовалась классическая схема с fork-ами процессов. Однако делать fork для всей базы данных было очень накладно, поэтому применялись легковесные процессы-сервисы, которые собирали пакеты из TCP-сессий и перекладывали их в одну очередь (SystemV Message Queue). Gateway и Trade Engine работали только с этой очередью, забирая оттуда транзакции на исполнение. Отправить в неё ответ уже было нельзя, потому что непонятно, какой сервис-процесс должен его прочитать. Так что мы прибегли к уловке: каждый fork-нутый процесс создавал для себя очередь ответов, и когда во входящую очередь приходил запрос, к нему сразу добавлялся тег для очереди ответов.

    Постоянное копирование из очереди в очередь больших объёмов данных создавало проблемы, особенно характерные для информационных запросов. Поэтому мы воспользовались ещё одним трюком: кроме очереди ответов каждый процесс создавал также и общую память (SystemV Shared Memory). В неё помещались сами пакеты, а в очереди сохранялся только тег, позволяющий найти исходный пакет. Это помогло сохранять данные в кэш-памяти процессора.

    SystemV IPC включает в себя утилиты для просмотра состояния объектов очередей, памяти и семафоров. Мы активно этим пользовались, чтобы понимать, что происходит в системе в конкретный момент, где скапливаются пакеты, что находится в блокировке и т. П.

    Первые модернизации


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

    Влияние высокочастотной торговли


    Вышеописанная версия архитектуры просуществовала вплоть до 2010 года. Тем временем нас уже перестала удовлетворять производительность серверов HP Superdome. К тому же архитектура PA-RISC фактически умерла, вендор не предлагал никаких существенных обновлений. В результате мы стали переходить с HP UX/PA RISC на Linux/x86. Переход начался с адаптации серверов доступа.

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

    Допустим, у нас есть небольшая транзакция, которая вызвала значительное изменение цены — кто-то купил полмиллиарда долларов. Спустя пару миллисекунд все участники рынка это замечают и начинают давать коррекцию. Естественно, запросы выстраиваются в огромную очередь, которую система будет долго разгребать.



    На этом интервале в 50 мс средняя скорость составляет около 16 тыс. транзакций в секунду. Если уменьшить окно до 20 мс, то получим среднюю скорость уже 90 тыс. транзакций в секунду, причём на пике будет 200 тыс. транзакций. Иными словами, нагрузка непостоянная, с резкими всплесками. А очередь запросов нужно всегда обрабатывать быстро.

    Но почему вообще возникает очередь? Итак, в нашем примере множество пользователей заметили изменение цены и отправляют соответствующие транзакции. Те приходят в Gateway, он их сериализует, задаёт некий порядок и отправляет в сеть. Маршрутизаторы перемешивают пакеты и отправляют их дальше. Чей пакет пришёл раньше, та транзакция и «выиграла». В результате клиенты биржи стали замечать, что если одну и ту же транзакцию отправлять с нескольких Gateway, то шансы на её быструю обработку возрастают. Вскоре биржевые роботы начали забрасывать Gateway запросами, и возникла лавина транзакций.



    Новый виток эволюции


    После длительного тестирования и исследований мы перешли на real-time ядро операционной системы. Для этого выбрали RedHat Enterprise MRG Linux, где MRG расшифровывается как messaging real-time grid. Преимущество real-time-патчей в том, что они оптимизируют систему под максимально быстрое исполнение: все процессы выстраиваются в FIFO-очередь, можно изолировать ядра, никаких выбрасываний, все транзакции обрабатываются в строгой последовательности.


    Красный — работа с очередью в обычном ядре, зеленый — работа в real-time ядре.

    Но достичь низкого уровня задержки на обычных серверах не так просто:

    • Сильно мешает режим SMI, который в архитектуре x86 лежит в основе работы с важной периферией. Обработка всевозможных аппаратных событий и управление компонентами и устройствами выполняется прошивкой в так называемом прозрачном SMI-режиме, при котором операционная система вообще не видит, что делает прошивка. Как правило, все крупные вендоры предлагают специальные расширения для firmware-серверов, позволяющие уменьшить объём SMI-обработки.
    • Не должно быть динамического управления частотой процессора, это приводит к дополнительному простою.
    • Когда сбрасывается журнал файловой системы, в ядре возникают некие процессы, которые приводят к непредсказуемым задержкам.
    • Нужно обращать внимание на такие вещи, как CPU Affinity, Interrupt affinity, NUMA.

    Надо сказать, тема настройки железа и ядра Linux под realtime-обработку заслуживает отдельной статьи. Мы много времени потратили на эксперименты и исследования, прежде чем достигли хорошего результата.

    При переходе с PA-RISC-серверов на x86 нам практически не пришлось сильно изменять код системы, мы лишь адаптировали и перенастроили её. Заодно поправили несколько багов. Например, быстро всплыли последствия того, что PA RISC являлась Big endian-системой, а x86 — Little endian: например, неправильно считывались данные. Более хитрый баг заключался в том, что PA RISC использует последовательно консистентный (Sequential consistent) доступ к памяти, тогда как x86 может переупорядочивать операции на чтение, поэтому код, абсолютно валидный на одной платформе, стал нерабочим на другой.

    После перехода на х86 производительность выросла почти в три раза, средняя длительность обработки транзакции снизилась до 60 мкс.

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

    Эпопея с горячим резервированием


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

    Кроме того, были и другие требования:

    • Нельзя ни в коем случае терять обработанные транзакции.
    • Система должна быть абсолютно прозрачной для нашей инфраструктуры.
    • Клиенты не должны видеть разрывы соединений.
    • Резервирование не должно вносить существенную задержку, потому что это критический фактор для биржи.

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

    В результате мы пришли к следующей схеме:



    • Главный сервер непосредственно взаимодействовал с серверами Gateway.
    • Все транзакции, поступавшие на главный сервер, моментально реплицировались на резервный сервер по отдельному каналу. Арбитр (Governor) координировал переключение при возникновении каких-либо проблем.

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



    Схема работала следующим образом.

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

    Если главный сервер снова входит в строй, на нём тоже срабатывает внутренний таймаут, потому что в течение определённого времени к серверу не было обращений от Gateway. Тогда он тоже обращается к Governor, и тот исключает его из схемы. В результате биржа до конца торгового периода работает с одним сервером. Поскольку вероятность выхода сервера из строя достаточно низкая, такая схема считалась вполне приемлемой, она не содержала сложной логики и легко тестировалась.

    Продолжение следует.
    Московская Биржа
    105,81
    Крупнейшая Биржа России, СНГ и Восточной Европы
    Поделиться публикацией

    Комментарии 24

      0
      >После перехода на х86 производительность выросла почти в три раза, средняя длительность обработки транзакции снизилась до 60 мс. наверно 60мкс?
        0
        Да, всё верно, поправили.
          –2

          У кого самый короткий шланг?

        +2

        Любопытства ради спрошу, а какие протоколы использует ваша биржа? Для market data, order entry итп.
        Что то самодельное свое, или были взяты готовые наработки?

          +1
          Доступно и то, и другое.

          Есть стандартные протоколы – FIX для подачи заявок и FAST для маркет-даты. На срочном рынке для быстрой торговли есть также бинарный TWIME (про него на Хабре была отдельная подробная статья).

          Также на всех рынках есть совсем свои: CGate на срочном рынке и ASTS Bridge на фондовом, валютном, денежном. Необходимость своего протокола вызвана, в частности, тем, что на рынках есть много специфичной для Московской Биржи функциональности, которой нет на других биржах и которая не предусмотрена стандартами. Например, клиринговые и позиционные данные и транзакции, переговорные режимы торгов, различные виды аукционов.
            +1
            Спасибо. Ещё вопрос, если можно.
            А вы ведёте какую либо статистику скорости реакции HFT участников?
            Понятное дело что все, кто не пришел первым — будут сильно с задержкой обработаны, т.к. встанут в очередь, но вот по round trip времени первого агрессивного пакета, за вычетом задержки непосредственно сети, вполне можно судить.
            Ну и если вы ведёте такую статистику — какая средняя сейчас скорость у HFT игроков на московской бирже. Спрашиваю т.к любопытно, насколько большая разница с тем же Eurex-ом например, где лидеры HFT гонки уже давно считают скорость реакции в десятках наносекунд… (У eurex-а очень много статистики доступно в сыром открытом виде, так что любой клиент биржа вооружившись python/pandas-ом может много этих данных вытащить несложным анализом).
              +1
              Данные для такой статистики мы не собираем. Но у нас работают примерно на тех же скоростях, что и у при работе с немцами… согласно полученной в кулуарах неофициальной информации :)
          0
          А на чем терминал написан, если не секрет? Мы сейчас в поиске какой-нибудь библиотеки для гуя, и пока не нашли ничего кроме QT, а плюсцы не хочется.
            0

            Говорят у Microsoft есть такая штука, WPF, которая скоро станет кросс-платформенной

              0
              Вопрос в том, потянет ли wpf + дотнет стек, допустим, два 4к монитора по 50 окон с таблицами на каждом. Что в исходном сообщении я забыл упомянуть, да )
                0

                Ни один человек не потянет 50 окон, т.к. объем у внимания ограничен на порядок.

                  0

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

                    0

                    Там хотят два по 50, а всплески на втором телевизоре могут только отвлекать и рассеивать внимание человека. Не даром на КДПВ ограничились 6 графиками.

                      0
                      В целом да, 2 по 50 на 4К наверное только для информационного табло в зале (если они ещё существуют и там ими кто-нибудь пользуется)…
                      но думаю проще (дешевле) повесить для таких целей кучу (пару десятков) простых FHD мониторов (телевизоров) с мини-компами и на каждом по 5-10 окон показывать.
                      PS: Но относительно гуя могу выразить свою уверенность что обычные таблицы и графики на «два 4к монитора по 50 окон» потянет любой стек на нормальном (да и не очень) железе. Вопрос только в адекватности скорости обсчёта (получения) отображаемых данных. Поскольку сфера достаточно специфична, вполне нормально (берите пример с GameDev) выставить требования к железу для вашего ПО
                        0
                        Это вполне конкретный запрос от вполне конкретных людей, которые работают именно в такой конфигурации. Да, информацию предоставляет боковое зрение на уровне «что-то в правом верхнем углу начало двигаться», «начало двигаться быстро, или медленно», «начало двигаться рывками, или равномерно» с последующей мгновенной реакцией на событие фокусировкой внимания, а не открыванием еще одного окна.
                    0

                    А собственно почему и нет. У нас в компании все средства мониторинга в основном на devexpress-е реализованы, которое по сути нашлепка поверх WPF, и все прекрасно работает. Хотя я не берусь утверждать какие у нас пропорции количества мониторов к количеству серверов, т.к почти все PROD приложения запущены через citrix "где-то там" на citrix farm-е…

                  0
                  Скажу по себе, что все торговые терминалы я пишу либо на DirectX либо на OpenGL. Никаких «толстых» гуи фреймворков с кнопочками, списочками и прочим бредом. Для торговли это не надо, и скорости отрисовки совершенно нехватает для быстрой отдачи.
                    +3
                    Если говорить про терминалы к ASTS, то секрет из этого сделать сложно – достаточно посмотреть на набор файлов в дистрибутиве, чтобы увидеть, что он написан на… Delphi. С одной стороны, он прекрасно справляется с большими объёмами данных, но с другой, мы все понимаем, что Delphi уже несколько вышел из моды… Но конкретно эти терминалы заточены под весьма специфичный круг задач и достаточно узкую целевую аудиторию, которая, согласно опросам, всё ещё отдаёт предпочтение толстым клиентам. Для приложений аналогичного класса и с аналогичными пользовательскими требованиями потихоньку мигрируем на .net и C#. Из библиотек, и там, и там, активно используем то, что делает DevExpress.

                    Кстати, небольшой совет – при выборе библиотек для корпоративных решений не забыть исследовать их совместимость с системами автотестирования. У нас, например, было долгое хождение по граблям, пока не научили их распознавать некоторые 3rd-party гуёвые контролы.

                    В других решениях биржи, нацеленных на более широкую аудиторию, сейчас применяем хорошо всем известный стек ныне популярных технологий – Vue.js, Kubernetes, Kafka, Camunda,…
                      +1
                      Спасибо, у нас как раз «весьма специфичный круг задач и достаточно узкая целевая аудитория»
                    +1
                    Спасибо за статью! А можно пару комментариев про in-memory DB, из текста следует, что Вы написали что то свое? Сколько это заняло по времени? Сколько серверов используется под базу? Кластер растянут между площадками или все в рамках одного ЦОДа?
                      +1
                      Вообще говоря, наша in-memory DB достаточно сильно отличается от популярных на текущий момент, таких, как Aerospike, Tarantool и т.п., и по современным понятиям назвать её DB можно с натяжкой. Основная задача была в минимизации оверхеда. Полностью написана на С, описание таблиц — это C-структуры. По этим описаниям генерируются сериализаторы/десериализаторы, чтобы можно было сбрасывать структуры на диск или в сеть. Вся работа идет непосредственно со структурами в памяти. Репликация данных идет отдельно, и вообще ее может не быть. Для отображений и связей используются интрузивные списки (элементы prev/next лежат в самой записи), а голова — в другой таблице. Их тоже нужно объявлять в заголовке структуры. Также есть интрузивные деревья поиска для более сложных структур. Принцип работы с инрузивными типами очень похож на работу в ядре ОС Linux (https://notes.shichao.io/lkd/ch6/). Есть много вспомогательных функций, макросов, автогенераторов, но всё равно это больше программирование на С, чем работа с NoSQL-базой. Сделано это было, так как корни растут еще с начала 90-х, да и на PA-RISC родной компилятор С++ оставлял желать лучшего, а g++ под PA-RISC генерировал код раза в 3-4 медленнее, чем родной, поэтому до 2011 был только С. Сейчас, конечно, boost::intrusive более предпочтительный способ :)

                      Непосредственно перед стартом определятся максимальный размер каждой таблицы — и именно такой размер выделяется в оперативной памяти (с mlock). Это сделано для обеспечения не только минимальной задержки, но и стабильного джиттера, т.к. если в процессе работы будет увеличение размера, это приведет к небольшим паузам. Из-за особенности торгов мы каждый день перезапускаем систему и, соответственно, корректируем эти значения. Также в ходе старта происходит загрузка всех справочных данных на текущей день из SQL-базы. Можно выделить 4 класса таблиц: вспомогательные (хеши, индексы), статические данные (загружаются в отсортированном виде при старте из SQL-базы и более не меняются), динамические и динамические с реюзингом. Последний тип достаточно интересный, ~70-80 % RAM занимает таблица заявок, количество таких заявок может быть под 100 млн за день, и каждая строчка — это около ~450 байт. Хотя серверы с 1Тб+ RAM вполне доступны сейчас, но лет 6 назад этот объем был существенен, да и серверов с учетом всех гейтвеев более 100. Но самое главное — хранить их всех нет необходимости. Большая часть заявок в ходе торгового дня становится неактивна и нужна только для истории, сразу скажу, эта задача решается online-импортом в SQL-базу. Поэтому в ходе работы с этой таблицей ведется список LRU (least recently used), и когда все доступные записи таблицы использованы, начинается алгоритм реюзинга (отвязывание записей из LRU, входящих в отображения, и перевод в новый список доступных после реюзинга). Эта операция идет блочно по ~32 записи, т.е. мы освободили место под текущую транзакцию и сделали задел на будущее. Если меньше, то операции синхронизации (чтобы безопасно отвязать) на каждой транзакции будут давать замедление (производительность падает на 20 % в этом случае, по сравнению, когда реюзинга еще нет). Если делать больше то на транзакции, в которой происходит операция, будет всплеск задержки в обработки. Поэтому было найдено оптимальное значение (падение производительности ~1 %).

                      Все запросы пишутся тоже с нуля. Вообще говоря, в запросах сразу заложен нативный протокол Mustang и все запросы сразу формируют Mustang-пакеты без какого-либо внутреннего промежуточного представления. Собственно, по этой причине и нет сейчас «бинарного» протокола на фондовом и валютном рынке.

                      Данная база модифицируется всё время, пока функционирует Биржа, начиная с австралийской версии ASTS.

                      База на текущий момент не распределенная и все серверы имеют ее реплики на, в теории, неограниченное число серверов. Об этом расскажем в следующей части
                      0
                      Более хитрый баг заключался в том, что PA RISC использует последовательно консистентный (Sequential consistent) доступ к памяти, тогда как x86 может переупорядочивать операции на чтение, поэтому код, абсолютно валидный на одной платформе, стал нерабочим на другой.

                      А можно об этом поподробнее? Ведь даже если сам чип на своей шине производит переупорядочивание запросов на чтение памяти, с программной точки зрения, даже в мультипроцессорной системе, это вроде не должно быть заметно…
                        0
                        Еще как заметно, особенно на ARM'ах каких-нибудь. x86 в этом плане поскромнее, но тоже приходится время от времени использовать барьеры памяти. Можно тут посмотреть несколько неплохих визуализаций (или тут как реализовано в Qt, неабстрактное описание для разработчика, возможно будет легче разобраться).
                        –3
                        много воды и фаллометрии. Где исходники посмотреть?

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

                        Самое читаемое